diff --git a/[refs] b/[refs] index b2c5f47efa41..6544f57ff8ce 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a0019bca04029d25a8bbbaaaf28487e6ccd7878e +refs/heads/master: ee60833a4f887a09e87be52cdf1247a4963b0aef diff --git a/trunk/drivers/bluetooth/ath3k.c b/trunk/drivers/bluetooth/ath3k.c index 41dadacd3d15..a126e614601f 100644 --- a/trunk/drivers/bluetooth/ath3k.c +++ b/trunk/drivers/bluetooth/ath3k.c @@ -39,8 +39,6 @@ static struct usb_device_id ath3k_table[] = { /* Atheros AR3011 with sflash firmware*/ { USB_DEVICE(0x0CF3, 0x3002) }, - /* Atheros AR9285 Malbec with sflash firmware */ - { USB_DEVICE(0x03F0, 0x311D) }, { } /* Terminating entry */ }; @@ -108,7 +106,6 @@ static int ath3k_probe(struct usb_interface *intf, { const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); - int ret; BT_DBG("intf %p id %p", intf, id); @@ -119,10 +116,13 @@ static int ath3k_probe(struct usb_interface *intf, return -EIO; } - ret = ath3k_load_firmware(udev, firmware); + if (ath3k_load_firmware(udev, firmware)) { + release_firmware(firmware); + return -EIO; + } release_firmware(firmware); - return ret; + return 0; } static void ath3k_disconnect(struct usb_interface *intf) diff --git a/trunk/drivers/bluetooth/btusb.c b/trunk/drivers/bluetooth/btusb.c index 4cefa91e6c34..1da773f899a2 100644 --- a/trunk/drivers/bluetooth/btusb.c +++ b/trunk/drivers/bluetooth/btusb.c @@ -102,9 +102,6 @@ static struct usb_device_id blacklist_table[] = { /* Atheros 3011 with sflash firmware */ { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE }, - /* Atheros AR9285 Malbec with sflash firmware */ - { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE }, - /* Broadcom BCM2035 */ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU }, { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU }, diff --git a/trunk/drivers/net/wireless/wl12xx/acx.c b/trunk/drivers/net/wireless/wl12xx/acx.c index afdc601aa7ea..84d94b259900 100644 --- a/trunk/drivers/net/wireless/wl12xx/acx.c +++ b/trunk/drivers/net/wireless/wl12xx/acx.c @@ -1476,3 +1476,33 @@ int wl1271_acx_max_tx_retry(struct wl1271 *wl) kfree(acx); return ret; } + +int wl1271_acx_config_ps(struct wl1271 *wl) +{ + struct wl1271_acx_config_ps *config_ps; + int ret; + + wl1271_debug(DEBUG_ACX, "acx config ps"); + + config_ps = kzalloc(sizeof(*config_ps), GFP_KERNEL); + if (!config_ps) { + ret = -ENOMEM; + goto out; + } + + config_ps->exit_retries = wl->conf.conn.psm_exit_retries; + config_ps->enter_retries = wl->conf.conn.psm_entry_retries; + config_ps->null_data_rate = cpu_to_le32(wl->basic_rate); + + ret = wl1271_cmd_configure(wl, ACX_CONFIG_PS, config_ps, + sizeof(*config_ps)); + + if (ret < 0) { + wl1271_warning("acx config ps failed: %d", ret); + goto out; + } + +out: + kfree(config_ps); + return ret; +} diff --git a/trunk/drivers/net/wireless/wl12xx/acx.h b/trunk/drivers/net/wireless/wl12xx/acx.h index 4bbaf04f434e..5bc0ca97bec4 100644 --- a/trunk/drivers/net/wireless/wl12xx/acx.h +++ b/trunk/drivers/net/wireless/wl12xx/acx.h @@ -1136,6 +1136,15 @@ struct wl1271_acx_max_tx_retry { u8 padding_1[2]; } __packed; +struct wl1271_acx_config_ps { + struct acx_header header; + + u8 exit_retries; + u8 enter_retries; + u8 padding[2]; + __le32 null_data_rate; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1200,6 +1209,7 @@ enum { DOT11_RTS_THRESHOLD = 0x1013, DOT11_GROUP_ADDRESS_TBL = 0x1014, ACX_PM_CONFIG = 0x1016, + ACX_CONFIG_PS = 0x1017, MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, @@ -1269,5 +1279,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); +int wl1271_acx_config_ps(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/trunk/drivers/net/wireless/wl12xx/conf.h b/trunk/drivers/net/wireless/wl12xx/conf.h index fd1dac9ab4db..c81aecd755e5 100644 --- a/trunk/drivers/net/wireless/wl12xx/conf.h +++ b/trunk/drivers/net/wireless/wl12xx/conf.h @@ -959,6 +959,14 @@ struct conf_conn_settings { */ u8 psm_entry_retries; + /* + * Specifies the maximum number of times to try PSM exit if it fails + * (if sending the appropriate null-func message fails.) + * + * Range 0 - 255 + */ + u8 psm_exit_retries; + /* * Specifies the maximum number of times to try transmit the PSM entry * null-func frame for each PSM entry attempt diff --git a/trunk/drivers/net/wireless/wl12xx/main.c b/trunk/drivers/net/wireless/wl12xx/main.c index 254b7daccee1..522bb09c9535 100644 --- a/trunk/drivers/net/wireless/wl12xx/main.c +++ b/trunk/drivers/net/wireless/wl12xx/main.c @@ -256,6 +256,7 @@ static struct conf_drv_settings default_conf = { .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 10, .psm_entry_retries = 5, + .psm_exit_retries = 255, .psm_entry_nullfunc_retries = 3, .psm_entry_hangover_period = 1, .keep_alive_interval = 55000, diff --git a/trunk/include/net/bluetooth/bluetooth.h b/trunk/include/net/bluetooth/bluetooth.h index ed7d775337e0..0c5e72503b77 100644 --- a/trunk/include/net/bluetooth/bluetooth.h +++ b/trunk/include/net/bluetooth/bluetooth.h @@ -64,11 +64,6 @@ struct bt_security { #define BT_DEFER_SETUP 7 -#define BT_FLUSHABLE 8 - -#define BT_FLUSHABLE_OFF 0 -#define BT_FLUSHABLE_ON 1 - #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg) #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg) #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg) diff --git a/trunk/include/net/bluetooth/hci.h b/trunk/include/net/bluetooth/hci.h index 4bee030e4b52..29a7a8ca0438 100644 --- a/trunk/include/net/bluetooth/hci.h +++ b/trunk/include/net/bluetooth/hci.h @@ -76,14 +76,6 @@ enum { HCI_INQUIRY, HCI_RAW, - - HCI_SETUP, - HCI_AUTO_OFF, - HCI_MGMT, - HCI_PAIRABLE, - HCI_SERVICE_CACHE, - HCI_LINK_KEYS, - HCI_DEBUG_KEYS, }; /* HCI ioctl defines */ @@ -158,7 +150,6 @@ enum { #define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5) /* ACL flags */ -#define ACL_START_NO_FLUSH 0x00 #define ACL_CONT 0x01 #define ACL_START 0x02 #define ACL_ACTIVE_BCAST 0x04 @@ -192,25 +183,17 @@ enum { #define LMP_PSCHEME 0x02 #define LMP_PCONTROL 0x04 -#define LMP_RSSI_INQ 0x40 #define LMP_ESCO 0x80 #define LMP_EV4 0x01 #define LMP_EV5 0x02 -#define LMP_LE 0x40 #define LMP_SNIFF_SUBR 0x02 -#define LMP_PAUSE_ENC 0x04 #define LMP_EDR_ESCO_2M 0x20 #define LMP_EDR_ESCO_3M 0x40 #define LMP_EDR_3S_ESCO 0x80 -#define LMP_EXT_INQ 0x01 #define LMP_SIMPLE_PAIR 0x08 -#define LMP_NO_FLUSH 0x40 - -#define LMP_LSTO 0x01 -#define LMP_INQ_TX_PWR 0x02 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 @@ -309,19 +292,11 @@ struct hci_cp_pin_code_reply { __u8 pin_len; __u8 pin_code[16]; } __packed; -struct hci_rp_pin_code_reply { - __u8 status; - bdaddr_t bdaddr; -} __packed; #define HCI_OP_PIN_CODE_NEG_REPLY 0x040e struct hci_cp_pin_code_neg_reply { bdaddr_t bdaddr; } __packed; -struct hci_rp_pin_code_neg_reply { - __u8 status; - bdaddr_t bdaddr; -} __packed; #define HCI_OP_CHANGE_CONN_PTYPE 0x040f struct hci_cp_change_conn_ptype { @@ -402,20 +377,6 @@ struct hci_cp_reject_sync_conn_req { __u8 reason; } __packed; -#define HCI_OP_IO_CAPABILITY_REPLY 0x042b -struct hci_cp_io_capability_reply { - bdaddr_t bdaddr; - __u8 capability; - __u8 oob_data; - __u8 authentication; -} __packed; - -#define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434 -struct hci_cp_io_capability_neg_reply { - bdaddr_t bdaddr; - __u8 reason; -} __packed; - #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; @@ -513,12 +474,6 @@ struct hci_cp_set_event_flt { #define HCI_CONN_SETUP_AUTO_OFF 0x01 #define HCI_CONN_SETUP_AUTO_ON 0x02 -#define HCI_OP_DELETE_STORED_LINK_KEY 0x0c12 -struct hci_cp_delete_stored_link_key { - bdaddr_t bdaddr; - __u8 delete_all; -} __packed; - #define HCI_OP_WRITE_LOCAL_NAME 0x0c13 struct hci_cp_write_local_name { __u8 name[248]; @@ -582,8 +537,6 @@ struct hci_cp_host_buffer_size { __le16 sco_max_pkt; } __packed; -#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45 - #define HCI_OP_READ_SSP_MODE 0x0c55 struct hci_rp_read_ssp_mode { __u8 status; @@ -595,8 +548,6 @@ struct hci_cp_write_ssp_mode { __u8 mode; } __packed; -#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 - #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; @@ -882,14 +833,6 @@ struct hci_ev_io_capa_request { bdaddr_t bdaddr; } __packed; -#define HCI_EV_IO_CAPA_REPLY 0x32 -struct hci_ev_io_capa_reply { - bdaddr_t bdaddr; - __u8 capability; - __u8 oob_data; - __u8 authentication; -} __packed; - #define HCI_EV_SIMPLE_PAIR_COMPLETE 0x36 struct hci_ev_simple_pair_complete { __u8 status; diff --git a/trunk/include/net/bluetooth/hci_core.h b/trunk/include/net/bluetooth/hci_core.h index 6163bff6fa91..d2cf88407690 100644 --- a/trunk/include/net/bluetooth/hci_core.h +++ b/trunk/include/net/bluetooth/hci_core.h @@ -66,21 +66,6 @@ struct bdaddr_list { struct list_head list; bdaddr_t bdaddr; }; - -struct bt_uuid { - struct list_head list; - u8 uuid[16]; - u8 svc_hint; -}; - -struct link_key { - struct list_head list; - bdaddr_t bdaddr; - u8 type; - u8 val[16]; - u8 pin_len; -}; - #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -95,18 +80,13 @@ struct hci_dev { bdaddr_t bdaddr; __u8 dev_name[248]; __u8 dev_class[3]; - __u8 major_class; - __u8 minor_class; __u8 features[8]; __u8 commands[64]; __u8 ssp_mode; __u8 hci_ver; __u16 hci_rev; - __u8 lmp_ver; __u16 manufacturer; - __le16 lmp_subver; __u16 voice_setting; - __u8 io_capability; __u16 pkt_type; __u16 esco_type; @@ -134,10 +114,6 @@ struct hci_dev { struct workqueue_struct *workqueue; - struct work_struct power_on; - struct work_struct power_off; - struct timer_list off_timer; - struct tasklet_struct cmd_task; struct tasklet_struct rx_task; struct tasklet_struct tx_task; @@ -153,17 +129,12 @@ struct hci_dev { wait_queue_head_t req_wait_q; __u32 req_status; __u32 req_result; - - __u16 init_last_cmd; + __u16 req_last_cmd; struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; - struct list_head uuids; - - struct list_head link_keys; - struct hci_dev_stats stat; struct sk_buff_head driver_init; @@ -214,16 +185,10 @@ struct hci_conn { __u8 auth_type; __u8 sec_level; __u8 pending_sec_level; - __u8 pin_length; - __u8 io_capability; __u8 power_save; __u16 disc_timeout; unsigned long pend; - __u8 remote_cap; - __u8 remote_oob; - __u8 remote_auth; - unsigned int sent; struct sk_buff_head data_q; @@ -472,16 +437,6 @@ int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_blacklist_clear(struct hci_dev *hdev); -int hci_uuids_clear(struct hci_dev *hdev); - -int hci_link_keys_clear(struct hci_dev *hdev); -struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 *key, u8 type, u8 pin_len); -int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); - -void hci_del_off_timer(struct hci_dev *hdev); - void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); @@ -503,7 +458,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) -#define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) /* ----- HCI protocols ----- */ struct hci_proto { @@ -706,24 +660,12 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); /* ----- HCI Sockets ----- */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, - struct sock *skip_sk); +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); /* Management interface */ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); int mgmt_index_added(u16 index); int mgmt_index_removed(u16 index); -int mgmt_powered(u16 index, u8 powered); -int mgmt_discoverable(u16 index, u8 discoverable); -int mgmt_connectable(u16 index, u8 connectable); -int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type); -int mgmt_connected(u16 index, bdaddr_t *bdaddr); -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); -int mgmt_disconnect_failed(u16 index); -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr); -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/trunk/include/net/bluetooth/l2cap.h b/trunk/include/net/bluetooth/l2cap.h index 75ef0b2948f9..7ad25ca60ec0 100644 --- a/trunk/include/net/bluetooth/l2cap.h +++ b/trunk/include/net/bluetooth/l2cap.h @@ -327,7 +327,6 @@ struct l2cap_pinfo { __u8 sec_level; __u8 role_switch; __u8 force_reliable; - __u8 flushable; __u8 conf_req[64]; __u8 conf_len; @@ -424,37 +423,6 @@ static inline int l2cap_tx_window_full(struct sock *sk) #define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) #define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START) -extern int disable_ertm; -extern const struct proto_ops l2cap_sock_ops; -extern struct bt_sock_list l2cap_sk_list; - -int l2cap_init_sockets(void); -void l2cap_cleanup_sockets(void); - -u8 l2cap_get_ident(struct l2cap_conn *conn); -void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); -int l2cap_build_conf_req(struct sock *sk, void *data); -int __l2cap_wait_ack(struct sock *sk); - -struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len); -struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len); -struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen); -int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len); -void l2cap_do_send(struct sock *sk, struct sk_buff *skb); -void l2cap_streaming_send(struct sock *sk); -int l2cap_ertm_send(struct sock *sk); - -void l2cap_sock_set_timer(struct sock *sk, long timeout); -void l2cap_sock_clear_timer(struct sock *sk); -void __l2cap_sock_close(struct sock *sk, int reason); -void l2cap_sock_kill(struct sock *sk); -void l2cap_sock_init(struct sock *sk, struct sock *parent); -struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, - int proto, gfp_t prio); -void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err); -void l2cap_chan_del(struct sock *sk, int err); -int l2cap_do_connect(struct sock *sk); - void l2cap_load(void); #endif /* __L2CAP_H */ diff --git a/trunk/include/net/bluetooth/mgmt.h b/trunk/include/net/bluetooth/mgmt.h index 44ac55c85079..ca29c1367ffd 100644 --- a/trunk/include/net/bluetooth/mgmt.h +++ b/trunk/include/net/bluetooth/mgmt.h @@ -47,7 +47,6 @@ struct mgmt_rp_read_info { __le16 index; __u8 type; __u8 powered; - __u8 connectable; __u8 discoverable; __u8 pairable; __u8 sec_mode; @@ -59,107 +58,6 @@ struct mgmt_rp_read_info { __u16 hci_rev; } __packed; -struct mgmt_mode { - __le16 index; - __u8 val; -} __packed; - -#define MGMT_OP_SET_POWERED 0x0005 - -#define MGMT_OP_SET_DISCOVERABLE 0x0006 - -#define MGMT_OP_SET_CONNECTABLE 0x0007 - -#define MGMT_OP_SET_PAIRABLE 0x0008 - -#define MGMT_OP_ADD_UUID 0x0009 -struct mgmt_cp_add_uuid { - __le16 index; - __u8 uuid[16]; - __u8 svc_hint; -} __packed; - -#define MGMT_OP_REMOVE_UUID 0x000A -struct mgmt_cp_remove_uuid { - __le16 index; - __u8 uuid[16]; -} __packed; - -#define MGMT_OP_SET_DEV_CLASS 0x000B -struct mgmt_cp_set_dev_class { - __le16 index; - __u8 major; - __u8 minor; -} __packed; - -#define MGMT_OP_SET_SERVICE_CACHE 0x000C -struct mgmt_cp_set_service_cache { - __le16 index; - __u8 enable; -} __packed; - -struct mgmt_key_info { - bdaddr_t bdaddr; - u8 type; - u8 val[16]; - u8 pin_len; -} __packed; - -#define MGMT_OP_LOAD_KEYS 0x000D -struct mgmt_cp_load_keys { - __le16 index; - __u8 debug_keys; - __le16 key_count; - struct mgmt_key_info keys[0]; -} __packed; - -#define MGMT_OP_REMOVE_KEY 0x000E -struct mgmt_cp_remove_key { - __le16 index; - bdaddr_t bdaddr; - __u8 disconnect; -} __packed; - -#define MGMT_OP_DISCONNECT 0x000F -struct mgmt_cp_disconnect { - __le16 index; - bdaddr_t bdaddr; -} __packed; -struct mgmt_rp_disconnect { - __le16 index; - bdaddr_t bdaddr; -} __packed; - -#define MGMT_OP_GET_CONNECTIONS 0x0010 -struct mgmt_cp_get_connections { - __le16 index; -} __packed; -struct mgmt_rp_get_connections { - __le16 index; - __le16 conn_count; - bdaddr_t conn[0]; -} __packed; - -#define MGMT_OP_PIN_CODE_REPLY 0x0011 -struct mgmt_cp_pin_code_reply { - __le16 index; - bdaddr_t bdaddr; - __u8 pin_len; - __u8 pin_code[16]; -} __packed; - -#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012 -struct mgmt_cp_pin_code_neg_reply { - __le16 index; - bdaddr_t bdaddr; -} __packed; - -#define MGMT_OP_SET_IO_CAPABILITY 0x0013 -struct mgmt_cp_set_io_capability { - __le16 index; - __u8 io_capability; -} __packed; - #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -187,43 +85,3 @@ struct mgmt_ev_index_added { struct mgmt_ev_index_removed { __le16 index; } __packed; - -#define MGMT_EV_POWERED 0x0006 - -#define MGMT_EV_DISCOVERABLE 0x0007 - -#define MGMT_EV_CONNECTABLE 0x0008 - -#define MGMT_EV_PAIRABLE 0x0009 - -#define MGMT_EV_NEW_KEY 0x000A -struct mgmt_ev_new_key { - __le16 index; - struct mgmt_key_info key; - __u8 old_key_type; -} __packed; - -#define MGMT_EV_CONNECTED 0x000B -struct mgmt_ev_connected { - __le16 index; - bdaddr_t bdaddr; -} __packed; - -#define MGMT_EV_DISCONNECTED 0x000C -struct mgmt_ev_disconnected { - __le16 index; - bdaddr_t bdaddr; -} __packed; - -#define MGMT_EV_CONNECT_FAILED 0x000D -struct mgmt_ev_connect_failed { - __le16 index; - bdaddr_t bdaddr; - __u8 status; -} __packed; - -#define MGMT_EV_PIN_CODE_REQUEST 0x000E -struct mgmt_ev_pin_code_request { - __le16 index; - bdaddr_t bdaddr; -} __packed; diff --git a/trunk/net/bluetooth/Kconfig b/trunk/net/bluetooth/Kconfig index e45eae66eaf3..ed371684c133 100644 --- a/trunk/net/bluetooth/Kconfig +++ b/trunk/net/bluetooth/Kconfig @@ -27,9 +27,9 @@ menuconfig BT compile it as module (bluetooth). To use Linux Bluetooth subsystem, you will need several user-space - utilities like hciconfig and bluetoothd. These utilities and updates - to Bluetooth kernel modules are provided in the BlueZ packages. For - more information, see . + utilities like hciconfig and hcid. These utilities and updates to + Bluetooth kernel modules are provided in the BlueZ packages. + For more information, see . config BT_L2CAP tristate "L2CAP protocol support" diff --git a/trunk/net/bluetooth/Makefile b/trunk/net/bluetooth/Makefile index 339b42932b33..250f954f0213 100644 --- a/trunk/net/bluetooth/Makefile +++ b/trunk/net/bluetooth/Makefile @@ -11,4 +11,3 @@ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o -l2cap-y := l2cap_core.o l2cap_sock.o diff --git a/trunk/net/bluetooth/af_bluetooth.c b/trunk/net/bluetooth/af_bluetooth.c index 2abfe2f30453..c4cf3f595004 100644 --- a/trunk/net/bluetooth/af_bluetooth.c +++ b/trunk/net/bluetooth/af_bluetooth.c @@ -199,15 +199,14 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) BT_DBG("parent %p", parent); - local_bh_disable(); list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); - bh_lock_sock(sk); + lock_sock(sk); /* FIXME: Is this check still needed */ if (sk->sk_state == BT_CLOSED) { - bh_unlock_sock(sk); + release_sock(sk); bt_accept_unlink(sk); continue; } @@ -217,16 +216,12 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) bt_accept_unlink(sk); if (newsock) sock_graft(sk, newsock); - - bh_unlock_sock(sk); - local_bh_enable(); + release_sock(sk); return sk; } - bh_unlock_sock(sk); + release_sock(sk); } - local_bh_enable(); - return NULL; } EXPORT_SYMBOL(bt_accept_dequeue); @@ -245,8 +240,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (flags & (MSG_OOB)) return -EOPNOTSUPP; - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) { + if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) { if (sk->sk_shutdown & RCV_SHUTDOWN) return 0; return err; @@ -329,8 +323,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (copied >= target) break; - err = sock_error(sk); - if (err) + if ((err = sock_error(sk)) != 0) break; if (sk->sk_shutdown & RCV_SHUTDOWN) break; diff --git a/trunk/net/bluetooth/cmtp/capi.c b/trunk/net/bluetooth/cmtp/capi.c index 67cff810c77d..3487cfe74aec 100644 --- a/trunk/net/bluetooth/cmtp/capi.c +++ b/trunk/net/bluetooth/cmtp/capi.c @@ -155,8 +155,7 @@ static void cmtp_send_interopmsg(struct cmtp_session *session, BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); - skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC); - if (!skb) { + if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for interoperability packet"); return; } diff --git a/trunk/net/bluetooth/cmtp/core.c b/trunk/net/bluetooth/cmtp/core.c index 2cee71a714c4..8e5f292529ac 100644 --- a/trunk/net/bluetooth/cmtp/core.c +++ b/trunk/net/bluetooth/cmtp/core.c @@ -115,8 +115,7 @@ static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const size = (skb) ? skb->len + count : count; - nskb = alloc_skb(size, GFP_ATOMIC); - if (!nskb) { + if (!(nskb = alloc_skb(size, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for CAPI message"); return; } @@ -217,8 +216,7 @@ static void cmtp_process_transmit(struct cmtp_session *session) BT_DBG("session %p", session); - nskb = alloc_skb(session->mtu, GFP_ATOMIC); - if (!nskb) { + if (!(nskb = alloc_skb(session->mtu, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for new frame"); return; } @@ -226,8 +224,7 @@ static void cmtp_process_transmit(struct cmtp_session *session) while ((skb = skb_dequeue(&session->transmit))) { struct cmtp_scb *scb = (void *) skb->cb; - tail = session->mtu - nskb->len; - if (tail < 5) { + if ((tail = (session->mtu - nskb->len)) < 5) { cmtp_send_frame(session, nskb->data, nskb->len); skb_trim(nskb, 0); tail = session->mtu; diff --git a/trunk/net/bluetooth/hci_conn.c b/trunk/net/bluetooth/hci_conn.c index 42dc39f25b72..99cd8d9d891b 100644 --- a/trunk/net/bluetooth/hci_conn.c +++ b/trunk/net/bluetooth/hci_conn.c @@ -234,7 +234,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) conn->mode = HCI_CM_ACTIVE; conn->state = BT_OPEN; conn->auth_type = HCI_AT_GENERAL_BONDING; - conn->io_capability = hdev->io_capability; conn->power_save = 1; conn->disc_timeout = HCI_DISCONN_TIMEOUT; diff --git a/trunk/net/bluetooth/hci_core.c b/trunk/net/bluetooth/hci_core.c index 2f003224d2ea..9c4541bc488a 100644 --- a/trunk/net/bluetooth/hci_core.c +++ b/trunk/net/bluetooth/hci_core.c @@ -50,8 +50,6 @@ #include #include -#define AUTO_OFF_TIMEOUT 2000 - static void hci_cmd_task(unsigned long arg); static void hci_rx_task(unsigned long arg); static void hci_tx_task(unsigned long arg); @@ -97,10 +95,11 @@ void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) { BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result); - /* If this is the init phase check if the completed command matches - * the last init command, and if not just return. - */ - if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) + /* If the request has set req_last_cmd (typical for multi-HCI + * command requests) check if the completed command matches + * this, and if not just return. Single HCI command requests + * typically leave req_last_cmd as 0 */ + if (hdev->req_last_cmd && cmd != hdev->req_last_cmd) return; if (hdev->req_status == HCI_REQ_PEND) { @@ -157,7 +156,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, break; } - hdev->req_status = hdev->req_result = 0; + hdev->req_last_cmd = hdev->req_status = hdev->req_result = 0; BT_DBG("%s end: err %d", hdev->name, err); @@ -190,7 +189,6 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) static void hci_init_req(struct hci_dev *hdev, unsigned long opt) { - struct hci_cp_delete_stored_link_key cp; struct sk_buff *skb; __le16 param; __u8 flt_type; @@ -254,13 +252,15 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) flt_type = HCI_FLT_CLEAR_ALL; hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); + /* Page timeout ~20 secs */ + param = cpu_to_le16(0x8000); + hci_send_cmd(hdev, HCI_OP_WRITE_PG_TIMEOUT, 2, ¶m); + /* Connection accept timeout ~20 secs */ param = cpu_to_le16(0x7d00); hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - bacpy(&cp.bdaddr, BDADDR_ANY); - cp.delete_all = 1; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); + hdev->req_last_cmd = HCI_OP_WRITE_CA_TIMEOUT; } static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) @@ -429,8 +429,7 @@ int hci_inquiry(void __user *arg) if (copy_from_user(&ir, ptr, sizeof(ir))) return -EFAULT; - hdev = hci_dev_get(ir.dev_id); - if (!hdev) + if (!(hdev = hci_dev_get(ir.dev_id))) return -ENODEV; hci_dev_lock_bh(hdev); @@ -490,8 +489,7 @@ int hci_dev_open(__u16 dev) struct hci_dev *hdev; int ret = 0; - hdev = hci_dev_get(dev); - if (!hdev) + if (!(hdev = hci_dev_get(dev))) return -ENODEV; BT_DBG("%s %p", hdev->name, hdev); @@ -523,7 +521,6 @@ int hci_dev_open(__u16 dev) if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - hdev->init_last_cmd = 0; //__hci_request(hdev, hci_reset_req, 0, HZ); ret = __hci_request(hdev, hci_init_req, 0, @@ -536,8 +533,6 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->flags)) - mgmt_powered(hdev->id, 1); } else { /* Init failed, cleanup */ tasklet_kill(&hdev->rx_task); @@ -619,8 +614,6 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); - mgmt_powered(hdev->id, 0); - /* Clear flags */ hdev->flags = 0; @@ -800,17 +793,9 @@ int hci_get_dev_list(void __user *arg) read_lock_bh(&hci_dev_list_lock); list_for_each(p, &hci_dev_list) { struct hci_dev *hdev; - hdev = list_entry(p, struct hci_dev, list); - - hci_del_off_timer(hdev); - - if (!test_bit(HCI_MGMT, &hdev->flags)) - set_bit(HCI_PAIRABLE, &hdev->flags); - (dr + n)->dev_id = hdev->id; (dr + n)->dev_opt = hdev->flags; - if (++n >= dev_num) break; } @@ -838,11 +823,6 @@ int hci_get_dev_info(void __user *arg) if (!hdev) return -ENODEV; - hci_del_off_timer(hdev); - - if (!test_bit(HCI_MGMT, &hdev->flags)) - set_bit(HCI_PAIRABLE, &hdev->flags); - strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); @@ -911,149 +891,6 @@ void hci_free_dev(struct hci_dev *hdev) } EXPORT_SYMBOL(hci_free_dev); -static void hci_power_on(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, power_on); - - BT_DBG("%s", hdev->name); - - if (hci_dev_open(hdev->id) < 0) - return; - - if (test_bit(HCI_AUTO_OFF, &hdev->flags)) - mod_timer(&hdev->off_timer, - jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); - - if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) - mgmt_index_added(hdev->id); -} - -static void hci_power_off(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, power_off); - - BT_DBG("%s", hdev->name); - - hci_dev_close(hdev->id); -} - -static void hci_auto_off(unsigned long data) -{ - struct hci_dev *hdev = (struct hci_dev *) data; - - BT_DBG("%s", hdev->name); - - clear_bit(HCI_AUTO_OFF, &hdev->flags); - - queue_work(hdev->workqueue, &hdev->power_off); -} - -void hci_del_off_timer(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); - - clear_bit(HCI_AUTO_OFF, &hdev->flags); - del_timer(&hdev->off_timer); -} - -int hci_uuids_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *uuid; - - uuid = list_entry(p, struct bt_uuid, list); - - list_del(p); - kfree(uuid); - } - - return 0; -} - -int hci_link_keys_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->link_keys) { - struct link_key *key; - - key = list_entry(p, struct link_key, list); - - list_del(p); - kfree(key); - } - - return 0; -} - -struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct list_head *p; - - list_for_each(p, &hdev->link_keys) { - struct link_key *k; - - k = list_entry(p, struct link_key, list); - - if (bacmp(bdaddr, &k->bdaddr) == 0) - return k; - } - - return NULL; -} - -int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, - u8 *val, u8 type, u8 pin_len) -{ - struct link_key *key, *old_key; - u8 old_key_type; - - old_key = hci_find_link_key(hdev, bdaddr); - if (old_key) { - old_key_type = old_key->type; - key = old_key; - } else { - old_key_type = 0xff; - key = kzalloc(sizeof(*key), GFP_ATOMIC); - if (!key) - return -ENOMEM; - list_add(&key->list, &hdev->link_keys); - } - - BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); - - bacpy(&key->bdaddr, bdaddr); - memcpy(key->val, val, 16); - key->type = type; - key->pin_len = pin_len; - - if (new_key) - mgmt_new_key(hdev->id, key, old_key_type); - - if (type == 0x06) - key->type = old_key_type; - - return 0; -} - -int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct link_key *key; - - key = hci_find_link_key(hdev, bdaddr); - if (!key) - return -ENOENT; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&key->list); - kfree(key); - - return 0; -} - /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1086,7 +923,6 @@ int hci_register_dev(struct hci_dev *hdev) hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); - hdev->io_capability = 0x03; /* No Input No Output */ hdev->idle_timeout = 0; hdev->sniff_max_interval = 800; @@ -1112,14 +948,6 @@ int hci_register_dev(struct hci_dev *hdev) INIT_LIST_HEAD(&hdev->blacklist); - INIT_LIST_HEAD(&hdev->uuids); - - INIT_LIST_HEAD(&hdev->link_keys); - - INIT_WORK(&hdev->power_on, hci_power_on); - INIT_WORK(&hdev->power_off, hci_power_off); - setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); - memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); atomic_set(&hdev->promisc, 0); @@ -1141,10 +969,7 @@ int hci_register_dev(struct hci_dev *hdev) } } - set_bit(HCI_AUTO_OFF, &hdev->flags); - set_bit(HCI_SETUP, &hdev->flags); - queue_work(hdev->workqueue, &hdev->power_on); - + mgmt_index_added(hdev->id); hci_notify(hdev, HCI_DEV_REG); return id; @@ -1174,10 +999,7 @@ int hci_unregister_dev(struct hci_dev *hdev) for (i = 0; i < NUM_REASSEMBLY; i++) kfree_skb(hdev->reassembly[i]); - if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->flags)) - mgmt_index_removed(hdev->id); - + mgmt_index_removed(hdev->id); hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { @@ -1191,8 +1013,6 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_dev_lock_bh(hdev); hci_blacklist_clear(hdev); - hci_uuids_clear(hdev); - hci_link_keys_clear(hdev); hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); @@ -1493,7 +1313,7 @@ static int hci_send_frame(struct sk_buff *skb) /* Time stamp */ __net_timestamp(skb); - hci_send_to_sock(hdev, skb, NULL); + hci_send_to_sock(hdev, skb); } /* Get rid of skb owner, prior to sending to the driver. */ @@ -1529,9 +1349,6 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; - if (test_bit(HCI_INIT, &hdev->flags)) - hdev->init_last_cmd = opcode; - skb_queue_tail(&hdev->cmd_q, skb); tasklet_schedule(&hdev->cmd_task); @@ -1578,7 +1395,7 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); + hci_add_acl_hdr(skb, conn->handle, flags | ACL_START); list = skb_shinfo(skb)->frag_list; if (!list) { @@ -1596,15 +1413,12 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) spin_lock_bh(&conn->data_q.lock); __skb_queue_tail(&conn->data_q, skb); - - flags &= ~ACL_START; - flags |= ACL_CONT; do { skb = list; list = list->next; skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); + hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT); BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); @@ -1886,7 +1700,7 @@ static void hci_rx_task(unsigned long arg) while ((skb = skb_dequeue(&hdev->rx_q))) { if (atomic_read(&hdev->promisc)) { /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb, NULL); + hci_send_to_sock(hdev, skb); } if (test_bit(HCI_RAW, &hdev->flags)) { @@ -1942,11 +1756,7 @@ static void hci_cmd_task(unsigned long arg) } /* Send queued commands */ - if (atomic_read(&hdev->cmd_cnt)) { - skb = skb_dequeue(&hdev->cmd_q); - if (!skb) - return; - + if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) { kfree_skb(hdev->sent_cmd); hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); diff --git a/trunk/net/bluetooth/hci_event.c b/trunk/net/bluetooth/hci_event.c index cee46cbe7aeb..a290854fdaa6 100644 --- a/trunk/net/bluetooth/hci_event.c +++ b/trunk/net/bluetooth/hci_event.c @@ -274,24 +274,15 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!status) { __u8 param = *((__u8 *) sent); - int old_pscan, old_iscan; - old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); - old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); + clear_bit(HCI_PSCAN, &hdev->flags); + clear_bit(HCI_ISCAN, &hdev->flags); - if (param & SCAN_INQUIRY) { + if (param & SCAN_INQUIRY) set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev->id, 1); - } else if (old_iscan) - mgmt_discoverable(hdev->id, 0); - if (param & SCAN_PAGE) { + if (param & SCAN_PAGE) set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev->id, 1); - } else if (old_pscan) - mgmt_connectable(hdev->id, 0); } hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); @@ -424,115 +415,6 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) hdev->ssp_mode = *((__u8 *) sent); } -static u8 hci_get_inquiry_mode(struct hci_dev *hdev) -{ - if (hdev->features[6] & LMP_EXT_INQ) - return 2; - - if (hdev->features[3] & LMP_RSSI_INQ) - return 1; - - if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && - hdev->lmp_subver == 0x0757) - return 1; - - if (hdev->manufacturer == 15) { - if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) - return 1; - } - - if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && - hdev->lmp_subver == 0x1805) - return 1; - - return 0; -} - -static void hci_setup_inquiry_mode(struct hci_dev *hdev) -{ - u8 mode; - - mode = hci_get_inquiry_mode(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); -} - -static void hci_setup_event_mask(struct hci_dev *hdev) -{ - /* The second byte is 0xff instead of 0x9f (two reserved bits - * disabled) since a Broadcom 1.2 dongle doesn't respond to the - * command otherwise */ - u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; - - /* Events for 1.2 and newer controllers */ - if (hdev->lmp_ver > 1) { - events[4] |= 0x01; /* Flow Specification Complete */ - events[4] |= 0x02; /* Inquiry Result with RSSI */ - events[4] |= 0x04; /* Read Remote Extended Features Complete */ - events[5] |= 0x08; /* Synchronous Connection Complete */ - events[5] |= 0x10; /* Synchronous Connection Changed */ - } - - if (hdev->features[3] & LMP_RSSI_INQ) - events[4] |= 0x04; /* Inquiry Result with RSSI */ - - if (hdev->features[5] & LMP_SNIFF_SUBR) - events[5] |= 0x20; /* Sniff Subrating */ - - if (hdev->features[5] & LMP_PAUSE_ENC) - events[5] |= 0x80; /* Encryption Key Refresh Complete */ - - if (hdev->features[6] & LMP_EXT_INQ) - events[5] |= 0x40; /* Extended Inquiry Result */ - - if (hdev->features[6] & LMP_NO_FLUSH) - events[7] |= 0x01; /* Enhanced Flush Complete */ - - if (hdev->features[7] & LMP_LSTO) - events[6] |= 0x80; /* Link Supervision Timeout Changed */ - - if (hdev->features[6] & LMP_SIMPLE_PAIR) { - events[6] |= 0x01; /* IO Capability Request */ - events[6] |= 0x02; /* IO Capability Response */ - events[6] |= 0x04; /* User Confirmation Request */ - events[6] |= 0x08; /* User Passkey Request */ - events[6] |= 0x10; /* Remote OOB Data Request */ - events[6] |= 0x20; /* Simple Pairing Complete */ - events[7] |= 0x04; /* User Passkey Notification */ - events[7] |= 0x08; /* Keypress Notification */ - events[7] |= 0x10; /* Remote Host Supported - * Features Notification */ - } - - if (hdev->features[4] & LMP_LE) - events[7] |= 0x20; /* LE Meta-Event */ - - hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); -} - -static void hci_setup(struct hci_dev *hdev) -{ - hci_setup_event_mask(hdev); - - if (hdev->lmp_ver > 1) - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); - - if (hdev->features[6] & LMP_SIMPLE_PAIR) { - u8 mode = 0x01; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); - } - - if (hdev->features[3] & LMP_RSSI_INQ) - hci_setup_inquiry_mode(hdev); - - if (hdev->features[7] & LMP_INQ_TX_PWR) - hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); -} - static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_version *rp = (void *) skb->data; @@ -544,34 +426,11 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); - hdev->lmp_ver = rp->lmp_ver; hdev->manufacturer = __le16_to_cpu(rp->manufacturer); - hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name, hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); - - if (test_bit(HCI_INIT, &hdev->flags)) - hci_setup(hdev); -} - -static void hci_setup_link_policy(struct hci_dev *hdev) -{ - u16 link_policy = 0; - - if (hdev->features[0] & LMP_RSWITCH) - link_policy |= HCI_LP_RSWITCH; - if (hdev->features[0] & LMP_HOLD) - link_policy |= HCI_LP_HOLD; - if (hdev->features[0] & LMP_SNIFF) - link_policy |= HCI_LP_SNIFF; - if (hdev->features[1] & LMP_PARK) - link_policy |= HCI_LP_PARK; - - link_policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, - sizeof(link_policy), &link_policy); } static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) @@ -581,15 +440,9 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb BT_DBG("%s status 0x%x", hdev->name, rp->status); if (rp->status) - goto done; + return; memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); - - if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10)) - hci_setup_link_policy(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); } static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) @@ -695,88 +548,6 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } -static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); -} - -static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); -} - -static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); -} - -static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, status); -} - -static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); -} - -static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_pin_code_reply *rp = (void *) skb->data; - struct hci_cp_pin_code_reply *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status); - - if (rp->status != 0) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); - if (!cp) - return; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (conn) - conn->pin_length = cp->pin_len; -} - -static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_pin_code_neg_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr, - rp->status); -} - static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -851,14 +622,11 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; + if (acl && (sco = acl->link)) { + sco->state = BT_CLOSED; - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } + hci_proto_connect_cfm(sco, status); + hci_conn_del(sco); } hci_dev_unlock(hdev); @@ -1040,14 +808,11 @@ static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) hci_dev_lock(hdev); acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; + if (acl && (sco = acl->link)) { + sco->state = BT_CLOSED; - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } + hci_proto_connect_cfm(sco, status); + hci_conn_del(sco); } hci_dev_unlock(hdev); @@ -1177,7 +942,6 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev->id, &ev->bdaddr); } else conn->state = BT_CONNECTED; @@ -1206,11 +970,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } - } else { + } else conn->state = BT_CLOSED; - if (conn->type == ACL_LINK) - mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); - } if (conn->type == ACL_LINK) hci_sco_setup(conn, ev->status); @@ -1307,26 +1068,19 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, ev->status); - if (ev->status) { - mgmt_disconnect_failed(hdev->id); + if (ev->status) return; - } hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - conn->state = BT_CLOSED; - - if (conn->type == ACL_LINK) - mgmt_disconnected(hdev->id, &conn->dst); + if (conn) { + conn->state = BT_CLOSED; - hci_proto_disconn_cfm(conn, ev->reason); - hci_conn_del(conn); + hci_proto_disconn_cfm(conn, ev->reason); + hci_conn_del(conn); + } -unlock: hci_dev_unlock(hdev); } @@ -1639,34 +1393,6 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_write_ca_timeout(hdev, skb); break; - case HCI_OP_DELETE_STORED_LINK_KEY: - hci_cc_delete_stored_link_key(hdev, skb); - break; - - case HCI_OP_SET_EVENT_MASK: - hci_cc_set_event_mask(hdev, skb); - break; - - case HCI_OP_WRITE_INQUIRY_MODE: - hci_cc_write_inquiry_mode(hdev, skb); - break; - - case HCI_OP_READ_INQ_RSP_TX_POWER: - hci_cc_read_inq_rsp_tx_power(hdev, skb); - break; - - case HCI_OP_SET_EVENT_FLT: - hci_cc_set_event_flt(hdev, skb); - break; - - case HCI_OP_PIN_CODE_REPLY: - hci_cc_pin_code_reply(hdev, skb); - break; - - case HCI_OP_PIN_CODE_NEG_REPLY: - hci_cc_pin_code_neg_reply(hdev, skb); - break; - default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -1733,11 +1459,6 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_exit_sniff_mode(hdev, ev->status); break; - case HCI_OP_DISCONNECT: - if (ev->status != 0) - mgmt_disconnect_failed(hdev->id); - break; - default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -1865,72 +1586,18 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_conn_put(conn); } - if (!test_bit(HCI_PAIRABLE, &hdev->flags)) - hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_request(hdev->id, &ev->bdaddr); - hci_dev_unlock(hdev); } static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_link_key_req *ev = (void *) skb->data; - struct hci_cp_link_key_reply cp; - struct hci_conn *conn; - struct link_key *key; - BT_DBG("%s", hdev->name); - - if (!test_bit(HCI_LINK_KEYS, &hdev->flags)) - return; - - hci_dev_lock(hdev); - - key = hci_find_link_key(hdev, &ev->bdaddr); - if (!key) { - BT_DBG("%s link key not found for %s", hdev->name, - batostr(&ev->bdaddr)); - goto not_found; - } - - BT_DBG("%s found key type %u for %s", hdev->name, key->type, - batostr(&ev->bdaddr)); - - if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && key->type == 0x03) { - BT_DBG("%s ignoring debug key", hdev->name); - goto not_found; - } - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - - if (key->type == 0x04 && conn && conn->auth_type != 0xff && - (conn->auth_type & 0x01)) { - BT_DBG("%s ignoring unauthenticated key", hdev->name); - goto not_found; - } - - bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.link_key, key->val, 16); - - hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp); - - hci_dev_unlock(hdev); - - return; - -not_found: - hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr); - hci_dev_unlock(hdev); } static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_link_key_notify *ev = (void *) skb->data; struct hci_conn *conn; - u8 pin_len = 0; BT_DBG("%s", hdev->name); @@ -1940,14 +1607,9 @@ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff if (conn) { hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - pin_len = conn->pin_length; hci_conn_put(conn); } - if (test_bit(HCI_LINK_KEYS, &hdev->flags)) - hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key, - ev->key_type, pin_len); - hci_dev_unlock(hdev); } @@ -2204,25 +1866,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct hci_dev_unlock(hdev); } -static inline u8 hci_get_auth_req(struct hci_conn *conn) -{ - /* If remote requests dedicated bonding follow that lead */ - if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) { - /* If both remote and local IO capabilities allow MITM - * protection then require it, otherwise don't */ - if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) - return 0x02; - else - return 0x03; - } - - /* If remote requests no-bonding follow that lead */ - if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) - return 0x00; - - return conn->auth_type; -} - static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_io_capa_request *ev = (void *) skb->data; @@ -2233,59 +1876,9 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - hci_conn_hold(conn); - - if (!test_bit(HCI_MGMT, &hdev->flags)) - goto unlock; - - if (test_bit(HCI_PAIRABLE, &hdev->flags) || - (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { - struct hci_cp_io_capability_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.capability = conn->io_capability; - cp.oob_data = 0; - cp.authentication = hci_get_auth_req(conn); - - hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY, - sizeof(cp), &cp); - } else { - struct hci_cp_io_capability_neg_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = 0x16; /* Pairing not allowed */ - - hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, - sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_io_capa_reply *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - hci_conn_hold(conn); - - conn->remote_cap = ev->capability; - conn->remote_oob = ev->oob_data; - conn->remote_auth = ev->authentication; + if (conn) + hci_conn_hold(conn); -unlock: hci_dev_unlock(hdev); } @@ -2449,10 +2042,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_io_capa_request_evt(hdev, skb); break; - case HCI_EV_IO_CAPA_REPLY: - hci_io_capa_reply_evt(hdev, skb); - break; - case HCI_EV_SIMPLE_PAIR_COMPLETE: hci_simple_pair_complete_evt(hdev, skb); break; @@ -2494,6 +2083,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) bt_cb(skb)->pkt_type = HCI_EVENT_PKT; skb->dev = (void *) hdev; - hci_send_to_sock(hdev, skb, NULL); + hci_send_to_sock(hdev, skb); kfree_skb(skb); } diff --git a/trunk/net/bluetooth/hci_sock.c b/trunk/net/bluetooth/hci_sock.c index d50e96136608..29827c77f6ce 100644 --- a/trunk/net/bluetooth/hci_sock.c +++ b/trunk/net/bluetooth/hci_sock.c @@ -85,8 +85,7 @@ static struct bt_sock_list hci_sk_list = { }; /* Send frame to RAW socket */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, - struct sock *skip_sk) +void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) { struct sock *sk; struct hlist_node *node; @@ -98,9 +97,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, struct hci_filter *flt; struct sk_buff *nskb; - if (sk == skip_sk) - continue; - if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) continue; diff --git a/trunk/net/bluetooth/hci_sysfs.c b/trunk/net/bluetooth/hci_sysfs.c index 23471dd9ee2f..5fce3d6d07b4 100644 --- a/trunk/net/bluetooth/hci_sysfs.c +++ b/trunk/net/bluetooth/hci_sysfs.c @@ -461,56 +461,6 @@ static const struct file_operations blacklist_fops = { .llseek = seq_lseek, .release = single_release, }; - -static void print_bt_uuid(struct seq_file *f, u8 *uuid) -{ - u32 data0, data4; - u16 data1, data2, data3, data5; - - memcpy(&data0, &uuid[0], 4); - memcpy(&data1, &uuid[4], 2); - memcpy(&data2, &uuid[6], 2); - memcpy(&data3, &uuid[8], 2); - memcpy(&data4, &uuid[10], 4); - memcpy(&data5, &uuid[14], 2); - - seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", - ntohl(data0), ntohs(data1), ntohs(data2), - ntohs(data3), ntohl(data4), ntohs(data5)); -} - -static int uuids_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct list_head *l; - - hci_dev_lock_bh(hdev); - - list_for_each(l, &hdev->uuids) { - struct bt_uuid *uuid; - - uuid = list_entry(l, struct bt_uuid, list); - - print_bt_uuid(f, uuid->uuid); - } - - hci_dev_unlock_bh(hdev); - - return 0; -} - -static int uuids_open(struct inode *inode, struct file *file) -{ - return single_open(file, uuids_show, inode->i_private); -} - -static const struct file_operations uuids_fops = { - .open = uuids_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - int hci_register_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; @@ -543,8 +493,6 @@ int hci_register_sysfs(struct hci_dev *hdev) debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, &blacklist_fops); - debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); - return 0; } diff --git a/trunk/net/bluetooth/hidp/core.c b/trunk/net/bluetooth/hidp/core.c index e0de92952f32..29544c21f4b5 100644 --- a/trunk/net/bluetooth/hidp/core.c +++ b/trunk/net/bluetooth/hidp/core.c @@ -157,8 +157,7 @@ static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev, session->leds = newleds; - skb = alloc_skb(3, GFP_ATOMIC); - if (!skb) { + if (!(skb = alloc_skb(3, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for new frame"); return -ENOMEM; } @@ -251,8 +250,7 @@ static int __hidp_send_ctrl_message(struct hidp_session *session, BT_DBG("session %p data %p size %d", session, data, size); - skb = alloc_skb(size + 1, GFP_ATOMIC); - if (!skb) { + if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for new frame"); return -ENOMEM; } @@ -285,8 +283,7 @@ static int hidp_queue_report(struct hidp_session *session, BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); - skb = alloc_skb(size + 1, GFP_ATOMIC); - if (!skb) { + if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for new frame"); return -ENOMEM; } diff --git a/trunk/net/bluetooth/l2cap_core.c b/trunk/net/bluetooth/l2cap.c similarity index 78% rename from trunk/net/bluetooth/l2cap_core.c rename to trunk/net/bluetooth/l2cap.c index ba7f9da68998..7550abb0c96a 100644 --- a/trunk/net/bluetooth/l2cap_core.c +++ b/trunk/net/bluetooth/l2cap.c @@ -24,7 +24,7 @@ SOFTWARE IS DISCLAIMED. */ -/* Bluetooth L2CAP core. */ +/* Bluetooth L2CAP core and sockets. */ #include @@ -57,24 +57,77 @@ #define VERSION "2.15" -int disable_ertm; +static int disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; +static const struct proto_ops l2cap_sock_ops; + static struct workqueue_struct *_busy_wq; -struct bt_sock_list l2cap_sk_list = { +static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) }; static void l2cap_busy_work(struct work_struct *work); +static void __l2cap_sock_close(struct sock *sk, int reason); +static void l2cap_sock_close(struct sock *sk); +static void l2cap_sock_kill(struct sock *sk); + +static int l2cap_build_conf_req(struct sock *sk, void *data); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb); +/* ---- L2CAP timers ---- */ +static void l2cap_sock_set_timer(struct sock *sk, long timeout) +{ + BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); + sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); +} + +static void l2cap_sock_clear_timer(struct sock *sk) +{ + BT_DBG("sock %p state %d", sk, sk->sk_state); + sk_stop_timer(sk, &sk->sk_timer); +} + +static void l2cap_sock_timeout(unsigned long arg) +{ + struct sock *sk = (struct sock *) arg; + int reason; + + BT_DBG("sock %p state %d", sk, sk->sk_state); + + bh_lock_sock(sk); + + if (sock_owned_by_user(sk)) { + /* sk is owned by user. Try again later */ + l2cap_sock_set_timer(sk, HZ / 5); + bh_unlock_sock(sk); + sock_put(sk); + return; + } + + if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) + reason = ECONNREFUSED; + else if (sk->sk_state == BT_CONNECT && + l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) + reason = ECONNREFUSED; + else + reason = ETIMEDOUT; + + __l2cap_sock_close(sk, reason); + + bh_unlock_sock(sk); + + l2cap_sock_kill(sk); + sock_put(sk); +} + /* ---- L2CAP channels ---- */ static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) { @@ -205,7 +258,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so /* Delete channel. * Must be called on the locked socket. */ -void l2cap_chan_del(struct sock *sk, int err) +static void l2cap_chan_del(struct sock *sk, int err) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sock *parent = bt_sk(sk)->parent; @@ -295,7 +348,7 @@ static inline int l2cap_check_security(struct sock *sk) auth_type); } -u8 l2cap_get_ident(struct l2cap_conn *conn) +static inline u8 l2cap_get_ident(struct l2cap_conn *conn) { u8 id; @@ -317,22 +370,16 @@ u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +static inline void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); - u8 flags; BT_DBG("code 0x%2.2x", code); if (!skb) return; - if (lmp_no_flush_capable(conn->hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - hci_send_acl(conn->hcon, skb, flags); + hci_send_acl(conn->hcon, skb, 0); } static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) @@ -342,7 +389,6 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) struct l2cap_conn *conn = pi->conn; struct sock *sk = (struct sock *)pi; int count, hlen = L2CAP_HDR_SIZE + 2; - u8 flags; if (sk->sk_state != BT_CONNECTED) return; @@ -379,12 +425,7 @@ static inline void l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control) put_unaligned_le16(fcs, skb_put(skb, 2)); } - if (lmp_no_flush_capable(conn->hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - hci_send_acl(pi->conn->hcon, skb, flags); + hci_send_acl(pi->conn->hcon, skb, 0); } static inline void l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control) @@ -455,7 +496,7 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) +static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err) { struct l2cap_disconn_req req; @@ -706,6 +747,17 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru } /* ---- Socket interface ---- */ +static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) +{ + struct sock *sk; + struct hlist_node *node; + sk_for_each(sk, node, &l2cap_sk_list.head) + if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) + goto found; + sk = NULL; +found: + return sk; +} /* Find socket with psm and source bdaddr. * Returns closest match. @@ -737,7 +789,276 @@ static struct sock *l2cap_get_sock_by_psm(int state, __le16 psm, bdaddr_t *src) return node ? sk : sk1; } -int l2cap_do_connect(struct sock *sk) +static void l2cap_sock_destruct(struct sock *sk) +{ + BT_DBG("sk %p", sk); + + skb_queue_purge(&sk->sk_receive_queue); + skb_queue_purge(&sk->sk_write_queue); +} + +static void l2cap_sock_cleanup_listen(struct sock *parent) +{ + struct sock *sk; + + BT_DBG("parent %p", parent); + + /* Close not yet accepted channels */ + while ((sk = bt_accept_dequeue(parent, NULL))) + l2cap_sock_close(sk); + + parent->sk_state = BT_CLOSED; + sock_set_flag(parent, SOCK_ZAPPED); +} + +/* Kill socket (only if zapped and orphan) + * Must be called on unlocked socket. + */ +static void l2cap_sock_kill(struct sock *sk) +{ + if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) + return; + + BT_DBG("sk %p state %d", sk, sk->sk_state); + + /* Kill poor orphan */ + bt_sock_unlink(&l2cap_sk_list, sk); + sock_set_flag(sk, SOCK_DEAD); + sock_put(sk); +} + +static void __l2cap_sock_close(struct sock *sk, int reason) +{ + BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); + + switch (sk->sk_state) { + case BT_LISTEN: + l2cap_sock_cleanup_listen(sk); + break; + + case BT_CONNECTED: + case BT_CONFIG: + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + + l2cap_sock_set_timer(sk, sk->sk_sndtimeo); + l2cap_send_disconn_req(conn, sk, reason); + } else + l2cap_chan_del(sk, reason); + break; + + case BT_CONNECT2: + if (sk->sk_type == SOCK_SEQPACKET || + sk->sk_type == SOCK_STREAM) { + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct l2cap_conn_rsp rsp; + __u16 result; + + if (bt_sk(sk)->defer_setup) + result = L2CAP_CR_SEC_BLOCK; + else + result = L2CAP_CR_BAD_PSM; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(result); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + } else + l2cap_chan_del(sk, reason); + break; + + case BT_CONNECT: + case BT_DISCONN: + l2cap_chan_del(sk, reason); + break; + + default: + sock_set_flag(sk, SOCK_ZAPPED); + break; + } +} + +/* Must be called on unlocked socket. */ +static void l2cap_sock_close(struct sock *sk) +{ + l2cap_sock_clear_timer(sk); + lock_sock(sk); + __l2cap_sock_close(sk, ECONNRESET); + release_sock(sk); + l2cap_sock_kill(sk); +} + +static void l2cap_sock_init(struct sock *sk, struct sock *parent) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + + BT_DBG("sk %p", sk); + + if (parent) { + sk->sk_type = parent->sk_type; + bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; + + pi->imtu = l2cap_pi(parent)->imtu; + pi->omtu = l2cap_pi(parent)->omtu; + pi->conf_state = l2cap_pi(parent)->conf_state; + pi->mode = l2cap_pi(parent)->mode; + pi->fcs = l2cap_pi(parent)->fcs; + pi->max_tx = l2cap_pi(parent)->max_tx; + pi->tx_win = l2cap_pi(parent)->tx_win; + pi->sec_level = l2cap_pi(parent)->sec_level; + pi->role_switch = l2cap_pi(parent)->role_switch; + pi->force_reliable = l2cap_pi(parent)->force_reliable; + } else { + pi->imtu = L2CAP_DEFAULT_MTU; + pi->omtu = 0; + if (!disable_ertm && sk->sk_type == SOCK_STREAM) { + pi->mode = L2CAP_MODE_ERTM; + pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; + } else { + pi->mode = L2CAP_MODE_BASIC; + } + pi->max_tx = L2CAP_DEFAULT_MAX_TX; + pi->fcs = L2CAP_FCS_CRC16; + pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; + pi->sec_level = BT_SECURITY_LOW; + pi->role_switch = 0; + pi->force_reliable = 0; + } + + /* Default config options */ + pi->conf_len = 0; + pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; + skb_queue_head_init(TX_QUEUE(sk)); + skb_queue_head_init(SREJ_QUEUE(sk)); + skb_queue_head_init(BUSY_QUEUE(sk)); + INIT_LIST_HEAD(SREJ_LIST(sk)); +} + +static struct proto l2cap_proto = { + .name = "L2CAP", + .owner = THIS_MODULE, + .obj_size = sizeof(struct l2cap_pinfo) +}; + +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); + if (!sk) + return NULL; + + sock_init_data(sock, sk); + INIT_LIST_HEAD(&bt_sk(sk)->accept_q); + + sk->sk_destruct = l2cap_sock_destruct; + sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); + + sock_reset_flag(sk, SOCK_ZAPPED); + + sk->sk_protocol = proto; + sk->sk_state = BT_OPEN; + + setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); + + bt_sock_link(&l2cap_sk_list, sk); + return sk; +} + +static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + + BT_DBG("sock %p", sock); + + sock->state = SS_UNCONNECTED; + + if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && + sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + + if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) + return -EPERM; + + sock->ops = &l2cap_sock_ops; + + sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); + if (!sk) + return -ENOMEM; + + l2cap_sock_init(sk, NULL); + return 0; +} + +static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) +{ + struct sock *sk = sock->sk; + struct sockaddr_l2 la; + int len, err = 0; + + BT_DBG("sk %p", sk); + + if (!addr || addr->sa_family != AF_BLUETOOTH) + return -EINVAL; + + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); + + if (la.l2_cid) + return -EINVAL; + + lock_sock(sk); + + if (sk->sk_state != BT_OPEN) { + err = -EBADFD; + goto done; + } + + if (la.l2_psm) { + __u16 psm = __le16_to_cpu(la.l2_psm); + + /* PSM must be odd and lsb of upper byte must be 0 */ + if ((psm & 0x0101) != 0x0001) { + err = -EINVAL; + goto done; + } + + /* Restrict usage of well-known PSMs */ + if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { + err = -EACCES; + goto done; + } + } + + write_lock_bh(&l2cap_sk_list.lock); + + if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { + err = -EADDRINUSE; + } else { + /* Save source address */ + bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; + l2cap_pi(sk)->sport = la.l2_psm; + sk->sk_state = BT_BOUND; + + if (__le16_to_cpu(la.l2_psm) == 0x0001 || + __le16_to_cpu(la.l2_psm) == 0x0003) + l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; + } + + write_unlock_bh(&l2cap_sk_list.lock); + +done: + release_sock(sk); + return err; +} + +static int l2cap_do_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(sk)->dst; @@ -797,109 +1118,325 @@ int l2cap_do_connect(struct sock *sk) return err; } -int __l2cap_wait_ack(struct sock *sk) +static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { - DECLARE_WAITQUEUE(wait, current); - int err = 0; - int timeo = HZ/5; - - add_wait_queue(sk_sleep(sk), &wait); - while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) { - set_current_state(TASK_INTERRUPTIBLE); + struct sock *sk = sock->sk; + struct sockaddr_l2 la; + int len, err = 0; - if (!timeo) - timeo = HZ/5; + BT_DBG("sk %p", sk); - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } + if (!addr || alen < sizeof(addr->sa_family) || + addr->sa_family != AF_BLUETOOTH) + return -EINVAL; - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); + memset(&la, 0, sizeof(la)); + len = min_t(unsigned int, sizeof(la), alen); + memcpy(&la, addr, len); - err = sock_error(sk); - if (err) - break; - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return err; -} + if (la.l2_cid) + return -EINVAL; -static void l2cap_monitor_timeout(unsigned long arg) -{ - struct sock *sk = (void *) arg; + lock_sock(sk); - BT_DBG("sk %p", sk); + if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) + && !la.l2_psm) { + err = -EINVAL; + goto done; + } - bh_lock_sock(sk); - if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { - l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk, ECONNABORTED); - bh_unlock_sock(sk); - return; + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; } - l2cap_pi(sk)->retry_count++; - __mod_monitor_timer(); + switch (sk->sk_state) { + case BT_CONNECT: + case BT_CONNECT2: + case BT_CONFIG: + /* Already connecting */ + goto wait; - l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); - bh_unlock_sock(sk); -} + case BT_CONNECTED: + /* Already connected */ + err = -EISCONN; + goto done; -static void l2cap_retrans_timeout(unsigned long arg) -{ - struct sock *sk = (void *) arg; + case BT_OPEN: + case BT_BOUND: + /* Can connect */ + break; - BT_DBG("sk %p", sk); + default: + err = -EBADFD; + goto done; + } - bh_lock_sock(sk); - l2cap_pi(sk)->retry_count = 1; - __mod_monitor_timer(); + /* PSM must be odd and lsb of upper byte must be 0 */ + if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && + sk->sk_type != SOCK_RAW) { + err = -EINVAL; + goto done; + } - l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + /* Set destination address and psm */ + bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); + l2cap_pi(sk)->psm = la.l2_psm; - l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); - bh_unlock_sock(sk); + err = l2cap_do_connect(sk); + if (err) + goto done; + +wait: + err = bt_sock_wait_state(sk, BT_CONNECTED, + sock_sndtimeo(sk, flags & O_NONBLOCK)); +done: + release_sock(sk); + return err; } -static void l2cap_drop_acked_frames(struct sock *sk) +static int l2cap_sock_listen(struct socket *sock, int backlog) { - struct sk_buff *skb; + struct sock *sk = sock->sk; + int err = 0; - while ((skb = skb_peek(TX_QUEUE(sk))) && - l2cap_pi(sk)->unacked_frames) { - if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq) - break; + BT_DBG("sk %p backlog %d", sk, backlog); - skb = skb_dequeue(TX_QUEUE(sk)); - kfree_skb(skb); + lock_sock(sk); - l2cap_pi(sk)->unacked_frames--; + if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) + || sk->sk_state != BT_BOUND) { + err = -EBADFD; + goto done; } - if (!l2cap_pi(sk)->unacked_frames) + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + err = -ENOTSUPP; + goto done; + } + + if (!l2cap_pi(sk)->psm) { + bdaddr_t *src = &bt_sk(sk)->src; + u16 psm; + + err = -EINVAL; + + write_lock_bh(&l2cap_sk_list.lock); + + for (psm = 0x1001; psm < 0x1100; psm += 2) + if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) { + l2cap_pi(sk)->psm = cpu_to_le16(psm); + l2cap_pi(sk)->sport = cpu_to_le16(psm); + err = 0; + break; + } + + write_unlock_bh(&l2cap_sk_list.lock); + + if (err < 0) + goto done; + } + + sk->sk_max_ack_backlog = backlog; + sk->sk_ack_backlog = 0; + sk->sk_state = BT_LISTEN; + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) +{ + DECLARE_WAITQUEUE(wait, current); + struct sock *sk = sock->sk, *nsk; + long timeo; + int err = 0; + + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + goto done; + } + + timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); + + BT_DBG("sk %p timeo %ld", sk, timeo); + + /* Wait for an incoming connection. (wake-one). */ + add_wait_queue_exclusive(sk_sleep(sk), &wait); + while (!(nsk = bt_accept_dequeue(sk, newsock))) { + set_current_state(TASK_INTERRUPTIBLE); + if (!timeo) { + err = -EAGAIN; + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + break; + } + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + + if (err) + goto done; + + newsock->state = SS_CONNECTED; + + BT_DBG("new socket %p", nsk); + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +{ + struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; + struct sock *sk = sock->sk; + + BT_DBG("sock %p, sk %p", sock, sk); + + addr->sa_family = AF_BLUETOOTH; + *len = sizeof(struct sockaddr_l2); + + if (peer) { + la->l2_psm = l2cap_pi(sk)->psm; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); + la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid); + } else { + la->l2_psm = l2cap_pi(sk)->sport; + bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid); + } + + return 0; +} + +static int __l2cap_wait_ack(struct sock *sk) +{ + DECLARE_WAITQUEUE(wait, current); + int err = 0; + int timeo = HZ/5; + + add_wait_queue(sk_sleep(sk), &wait); + while ((l2cap_pi(sk)->unacked_frames > 0 && l2cap_pi(sk)->conn)) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!timeo) + timeo = HZ/5; + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + + err = sock_error(sk); + if (err) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + return err; +} + +static void l2cap_monitor_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + + BT_DBG("sk %p", sk); + + bh_lock_sock(sk); + if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) { + l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk, ECONNABORTED); + bh_unlock_sock(sk); + return; + } + + l2cap_pi(sk)->retry_count++; + __mod_monitor_timer(); + + l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); + bh_unlock_sock(sk); +} + +static void l2cap_retrans_timeout(unsigned long arg) +{ + struct sock *sk = (void *) arg; + + BT_DBG("sk %p", sk); + + bh_lock_sock(sk); + l2cap_pi(sk)->retry_count = 1; + __mod_monitor_timer(); + + l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F; + + l2cap_send_rr_or_rnr(l2cap_pi(sk), L2CAP_CTRL_POLL); + bh_unlock_sock(sk); +} + +static void l2cap_drop_acked_frames(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = skb_peek(TX_QUEUE(sk))) && + l2cap_pi(sk)->unacked_frames) { + if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq) + break; + + skb = skb_dequeue(TX_QUEUE(sk)); + kfree_skb(skb); + + l2cap_pi(sk)->unacked_frames--; + } + + if (!l2cap_pi(sk)->unacked_frames) del_timer(&l2cap_pi(sk)->retrans_timer); } -void l2cap_do_send(struct sock *sk, struct sk_buff *skb) +static inline void l2cap_do_send(struct sock *sk, struct sk_buff *skb) { struct l2cap_pinfo *pi = l2cap_pi(sk); - struct hci_conn *hcon = pi->conn->hcon; - u16 flags; BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len); - if (!pi->flushable && lmp_no_flush_capable(hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - hci_send_acl(hcon, skb, flags); + hci_send_acl(pi->conn->hcon, skb, 0); } -void l2cap_streaming_send(struct sock *sk) +static void l2cap_streaming_send(struct sock *sk) { struct sk_buff *skb; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -968,7 +1505,7 @@ static void l2cap_retransmit_one_frame(struct sock *sk, u8 tx_seq) l2cap_do_send(sk, tx_skb); } -int l2cap_ertm_send(struct sock *sk) +static int l2cap_ertm_send(struct sock *sk) { struct sk_buff *skb, *tx_skb; struct l2cap_pinfo *pi = l2cap_pi(sk); @@ -1022,238 +1559,719 @@ int l2cap_ertm_send(struct sock *sk) else sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb); - nsent++; - } + nsent++; + } + + return nsent; +} + +static int l2cap_retransmit_frames(struct sock *sk) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + int ret; + + if (!skb_queue_empty(TX_QUEUE(sk))) + sk->sk_send_head = TX_QUEUE(sk)->next; + + pi->next_tx_seq = pi->expected_ack_seq; + ret = l2cap_ertm_send(sk); + return ret; +} + +static void l2cap_send_ack(struct l2cap_pinfo *pi) +{ + struct sock *sk = (struct sock *)pi; + u16 control = 0; + + control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { + control |= L2CAP_SUPER_RCV_NOT_READY; + pi->conn_state |= L2CAP_CONN_RNR_SENT; + l2cap_send_sframe(pi, control); + return; + } + + if (l2cap_ertm_send(sk) > 0) + return; + + control |= L2CAP_SUPER_RCV_READY; + l2cap_send_sframe(pi, control); +} + +static void l2cap_send_srejtail(struct sock *sk) +{ + struct srej_list *tail; + u16 control; + + control = L2CAP_SUPER_SELECT_REJECT; + control |= L2CAP_CTRL_FINAL; + + tail = list_entry(SREJ_LIST(sk)->prev, struct srej_list, list); + control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + + l2cap_send_sframe(l2cap_pi(sk), control); +} + +static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff **frag; + int err, sent = 0; + + if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) + return -EFAULT; + + sent += count; + len -= count; + + /* Continuation fragments (no L2CAP header) */ + frag = &skb_shinfo(skb)->frag_list; + while (len) { + count = min_t(unsigned int, conn->mtu, len); + + *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); + if (!*frag) + return err; + if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) + return -EFAULT; + + sent += count; + len -= count; + + frag = &(*frag)->next; + } + + return sent; +} + +static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE + 2; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(err); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; +} + +static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(err); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + return skb; +} + +static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sk_buff *skb; + int err, count, hlen = L2CAP_HDR_SIZE + 2; + struct l2cap_hdr *lh; + + BT_DBG("sk %p len %d", sk, (int)len); + + if (!conn) + return ERR_PTR(-ENOTCONN); + + if (sdulen) + hlen += 2; + + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + hlen += 2; + + count = min_t(unsigned int, (conn->mtu - hlen), len); + skb = bt_skb_send_alloc(sk, count + hlen, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + return ERR_PTR(err); + + /* Create L2CAP header */ + lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); + lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); + lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + put_unaligned_le16(control, skb_put(skb, 2)); + if (sdulen) + put_unaligned_le16(sdulen, skb_put(skb, 2)); + + err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); + if (unlikely(err < 0)) { + kfree_skb(skb); + return ERR_PTR(err); + } + + if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) + put_unaligned_le16(0, skb_put(skb, 2)); + + bt_cb(skb)->retries = 0; + return skb; +} + +static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) +{ + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + struct sk_buff_head sar_queue; + u16 control; + size_t size = 0; + + skb_queue_head_init(&sar_queue); + control = L2CAP_SDU_START; + skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + __skb_queue_tail(&sar_queue, skb); + len -= pi->remote_mps; + size += pi->remote_mps; + + while (len > 0) { + size_t buflen; + + if (len > pi->remote_mps) { + control = L2CAP_SDU_CONTINUE; + buflen = pi->remote_mps; + } else { + control = L2CAP_SDU_END; + buflen = len; + } + + skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0); + if (IS_ERR(skb)) { + skb_queue_purge(&sar_queue); + return PTR_ERR(skb); + } + + __skb_queue_tail(&sar_queue, skb); + len -= buflen; + size += buflen; + } + skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); + if (sk->sk_send_head == NULL) + sk->sk_send_head = sar_queue.next; + + return size; +} + +static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct l2cap_pinfo *pi = l2cap_pi(sk); + struct sk_buff *skb; + u16 control; + int err; + + BT_DBG("sock %p, sk %p", sock, sk); + + err = sock_error(sk); + if (err) + return err; + + if (msg->msg_flags & MSG_OOB) + return -EOPNOTSUPP; + + lock_sock(sk); + + if (sk->sk_state != BT_CONNECTED) { + err = -ENOTCONN; + goto done; + } + + /* Connectionless channel */ + if (sk->sk_type == SOCK_DGRAM) { + skb = l2cap_create_connless_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + } else { + l2cap_do_send(sk, skb); + err = len; + } + goto done; + } + + switch (pi->mode) { + case L2CAP_MODE_BASIC: + /* Check outgoing MTU */ + if (len > pi->omtu) { + err = -EMSGSIZE; + goto done; + } + + /* Create a basic PDU */ + skb = l2cap_create_basic_pdu(sk, msg, len); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + + l2cap_do_send(sk, skb); + err = len; + break; + + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + /* Entire SDU fits into one PDU */ + if (len <= pi->remote_mps) { + control = L2CAP_SDU_UNSEGMENTED; + skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto done; + } + __skb_queue_tail(TX_QUEUE(sk), skb); + + if (sk->sk_send_head == NULL) + sk->sk_send_head = skb; + + } else { + /* Segment SDU into multiples PDUs */ + err = l2cap_sar_segment_sdu(sk, msg, len); + if (err < 0) + goto done; + } + + if (pi->mode == L2CAP_MODE_STREAMING) { + l2cap_streaming_send(sk); + } else { + if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && + (pi->conn_state & L2CAP_CONN_WAIT_F)) { + err = len; + break; + } + err = l2cap_ertm_send(sk); + } + + if (err >= 0) + err = len; + break; + + default: + BT_DBG("bad state %1.1x", pi->mode); + err = -EBADFD; + } + +done: + release_sock(sk); + return err; +} + +static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk = sock->sk; + + lock_sock(sk); + + if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { + struct l2cap_conn_rsp rsp; + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + u8 buf[128]; + + sk->sk_state = BT_CONFIG; + + rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); + rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, + L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) { + release_sock(sk); + return 0; + } + + l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, + l2cap_build_conf_req(sk, buf), buf); + l2cap_pi(sk)->num_conf_req++; + + release_sock(sk); + return 0; + } + + release_sock(sk); + + if (sock->type == SOCK_STREAM) + return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + + return bt_sock_recvmsg(iocb, sock, msg, len, flags); +} + +static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct l2cap_options opts; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + lock_sock(sk); + + switch (optname) { + case L2CAP_OPTIONS: + if (sk->sk_state == BT_CONNECTED) { + err = -EINVAL; + break; + } + + opts.imtu = l2cap_pi(sk)->imtu; + opts.omtu = l2cap_pi(sk)->omtu; + opts.flush_to = l2cap_pi(sk)->flush_to; + opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; + + len = min_t(unsigned int, sizeof(opts), optlen); + if (copy_from_user((char *) &opts, optval, len)) { + err = -EFAULT; + break; + } + + if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->mode = opts.mode; + switch (l2cap_pi(sk)->mode) { + case L2CAP_MODE_BASIC: + l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; + break; + case L2CAP_MODE_ERTM: + case L2CAP_MODE_STREAMING: + if (!disable_ertm) + break; + /* fall through */ + default: + err = -EINVAL; + break; + } + + l2cap_pi(sk)->imtu = opts.imtu; + l2cap_pi(sk)->omtu = opts.omtu; + l2cap_pi(sk)->fcs = opts.fcs; + l2cap_pi(sk)->max_tx = opts.max_tx; + l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; + break; + + case L2CAP_LM: + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt & L2CAP_LM_AUTH) + l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; + if (opt & L2CAP_LM_ENCRYPT) + l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; + if (opt & L2CAP_LM_SECURE) + l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; + + l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); + l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); + break; + + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; +} + +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct bt_security sec; + int len, err = 0; + u32 opt; + + BT_DBG("sk %p", sk); + + if (level == SOL_L2CAP) + return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); + + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; + + lock_sock(sk); + + switch (optname) { + case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } + + sec.level = BT_SECURITY_LOW; + + len = min_t(unsigned int, sizeof(sec), optlen); + if (copy_from_user((char *) &sec, optval, len)) { + err = -EFAULT; + break; + } + + if (sec.level < BT_SECURITY_LOW || + sec.level > BT_SECURITY_HIGH) { + err = -EINVAL; + break; + } + + l2cap_pi(sk)->sec_level = sec.level; + break; - return nsent; -} + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } -static int l2cap_retransmit_frames(struct sock *sk) -{ - struct l2cap_pinfo *pi = l2cap_pi(sk); - int ret; + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } - if (!skb_queue_empty(TX_QUEUE(sk))) - sk->sk_send_head = TX_QUEUE(sk)->next; + bt_sk(sk)->defer_setup = opt; + break; - pi->next_tx_seq = pi->expected_ack_seq; - ret = l2cap_ertm_send(sk); - return ret; + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + return err; } -static void l2cap_send_ack(struct l2cap_pinfo *pi) +static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { - struct sock *sk = (struct sock *)pi; - u16 control = 0; + struct sock *sk = sock->sk; + struct l2cap_options opts; + struct l2cap_conninfo cinfo; + int len, err = 0; + u32 opt; - control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + BT_DBG("sk %p", sk); - if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY) { - control |= L2CAP_SUPER_RCV_NOT_READY; - pi->conn_state |= L2CAP_CONN_RNR_SENT; - l2cap_send_sframe(pi, control); - return; - } + if (get_user(len, optlen)) + return -EFAULT; - if (l2cap_ertm_send(sk) > 0) - return; + lock_sock(sk); - control |= L2CAP_SUPER_RCV_READY; - l2cap_send_sframe(pi, control); -} + switch (optname) { + case L2CAP_OPTIONS: + opts.imtu = l2cap_pi(sk)->imtu; + opts.omtu = l2cap_pi(sk)->omtu; + opts.flush_to = l2cap_pi(sk)->flush_to; + opts.mode = l2cap_pi(sk)->mode; + opts.fcs = l2cap_pi(sk)->fcs; + opts.max_tx = l2cap_pi(sk)->max_tx; + opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; -static void l2cap_send_srejtail(struct sock *sk) -{ - struct srej_list *tail; - u16 control; + len = min_t(unsigned int, len, sizeof(opts)); + if (copy_to_user(optval, (char *) &opts, len)) + err = -EFAULT; - control = L2CAP_SUPER_SELECT_REJECT; - control |= L2CAP_CTRL_FINAL; + break; - tail = list_entry(SREJ_LIST(sk)->prev, struct srej_list, list); - control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + case L2CAP_LM: + switch (l2cap_pi(sk)->sec_level) { + case BT_SECURITY_LOW: + opt = L2CAP_LM_AUTH; + break; + case BT_SECURITY_MEDIUM: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; + break; + case BT_SECURITY_HIGH: + opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | + L2CAP_LM_SECURE; + break; + default: + opt = 0; + break; + } - l2cap_send_sframe(l2cap_pi(sk), control); -} + if (l2cap_pi(sk)->role_switch) + opt |= L2CAP_LM_MASTER; -static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb) -{ - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sk_buff **frag; - int err, sent = 0; + if (l2cap_pi(sk)->force_reliable) + opt |= L2CAP_LM_RELIABLE; - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) - return -EFAULT; + if (put_user(opt, (u32 __user *) optval)) + err = -EFAULT; + break; - sent += count; - len -= count; + case L2CAP_CONNINFO: + if (sk->sk_state != BT_CONNECTED && + !(sk->sk_state == BT_CONNECT2 && + bt_sk(sk)->defer_setup)) { + err = -ENOTCONN; + break; + } - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len) { - count = min_t(unsigned int, conn->mtu, len); + cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; + memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); - *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); - if (!*frag) - return err; - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) - return -EFAULT; + len = min_t(unsigned int, len, sizeof(cinfo)); + if (copy_to_user(optval, (char *) &cinfo, len)) + err = -EFAULT; - sent += count; - len -= count; + break; - frag = &(*frag)->next; + default: + err = -ENOPROTOOPT; + break; } - return sent; + release_sock(sk); + return err; } -struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len) +static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; - struct l2cap_hdr *lh; + struct sock *sk = sock->sk; + struct bt_security sec; + int len, err = 0; - BT_DBG("sk %p len %d", sk, (int)len); + BT_DBG("sk %p", sk); - count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = bt_skb_send_alloc(sk, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - return ERR_PTR(err); + if (level == SOL_L2CAP) + return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2)); + if (level != SOL_BLUETOOTH) + return -ENOPROTOOPT; - err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - return skb; -} + if (get_user(len, optlen)) + return -EFAULT; -struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len) -{ - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE; - struct l2cap_hdr *lh; + lock_sock(sk); - BT_DBG("sk %p len %d", sk, (int)len); + switch (optname) { + case BT_SECURITY: + if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM + && sk->sk_type != SOCK_RAW) { + err = -EINVAL; + break; + } - count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = bt_skb_send_alloc(sk, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - return ERR_PTR(err); + sec.level = l2cap_pi(sk)->sec_level; - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); + len = min_t(unsigned int, len, sizeof(sec)); + if (copy_to_user(optval, (char *) &sec, len)) + err = -EFAULT; - err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); + break; + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) + err = -EFAULT; + + break; + + default: + err = -ENOPROTOOPT; + break; } - return skb; + + release_sock(sk); + return err; } -struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +static int l2cap_sock_shutdown(struct socket *sock, int how) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; - struct l2cap_hdr *lh; - - BT_DBG("sk %p len %d", sk, (int)len); - - if (!conn) - return ERR_PTR(-ENOTCONN); + struct sock *sk = sock->sk; + int err = 0; - if (sdulen) - hlen += 2; + BT_DBG("sock %p, sk %p", sock, sk); - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) - hlen += 2; + if (!sk) + return 0; - count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = bt_skb_send_alloc(sk, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - return ERR_PTR(err); + lock_sock(sk); + if (!sk->sk_shutdown) { + if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) + err = __l2cap_wait_ack(sk); - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(control, skb_put(skb, 2)); - if (sdulen) - put_unaligned_le16(sdulen, skb_put(skb, 2)); + sk->sk_shutdown = SHUTDOWN_MASK; + l2cap_sock_clear_timer(sk); + __l2cap_sock_close(sk, 0); - err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); + if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) + err = bt_sock_wait_state(sk, BT_CLOSED, + sk->sk_lingertime); } - if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) - put_unaligned_le16(0, skb_put(skb, 2)); + if (!err && sk->sk_err) + err = -sk->sk_err; - bt_cb(skb)->retries = 0; - return skb; + release_sock(sk); + return err; } -int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len) +static int l2cap_sock_release(struct socket *sock) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct sk_buff *skb; - struct sk_buff_head sar_queue; - u16 control; - size_t size = 0; - - skb_queue_head_init(&sar_queue); - control = L2CAP_SDU_START; - skb = l2cap_create_iframe_pdu(sk, msg, pi->remote_mps, control, len); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - __skb_queue_tail(&sar_queue, skb); - len -= pi->remote_mps; - size += pi->remote_mps; - - while (len > 0) { - size_t buflen; + struct sock *sk = sock->sk; + int err; - if (len > pi->remote_mps) { - control = L2CAP_SDU_CONTINUE; - buflen = pi->remote_mps; - } else { - control = L2CAP_SDU_END; - buflen = len; - } + BT_DBG("sock %p, sk %p", sock, sk); - skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0); - if (IS_ERR(skb)) { - skb_queue_purge(&sar_queue); - return PTR_ERR(skb); - } + if (!sk) + return 0; - __skb_queue_tail(&sar_queue, skb); - len -= buflen; - size += buflen; - } - skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk)); - if (sk->sk_send_head == NULL) - sk->sk_send_head = sar_queue.next; + err = l2cap_sock_shutdown(sock, 2); - return size; + sock_orphan(sk); + l2cap_sock_kill(sk); + return err; } static void l2cap_chan_ready(struct sock *sk) @@ -1474,7 +2492,7 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) } } -int l2cap_build_conf_req(struct sock *sk, void *data) +static int l2cap_build_conf_req(struct sock *sk, void *data) { struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_conf_req *req = data; @@ -1499,11 +2517,11 @@ int l2cap_build_conf_req(struct sock *sk, void *data) } done: - if (pi->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); - switch (pi->mode) { case L2CAP_MODE_BASIC: + if (pi->imtu != L2CAP_DEFAULT_MTU) + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu); + if (!(pi->conn->feat_mask & L2CAP_FEAT_ERTM) && !(pi->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; @@ -3654,15 +4672,12 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl { struct l2cap_conn *conn = hcon->l2cap_data; - if (!conn) - conn = l2cap_conn_add(hcon, 0); - - if (!conn) + if (!conn && !(conn = l2cap_conn_add(hcon, 0))) goto drop; BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - if (!(flags & ACL_CONT)) { + if (flags & ACL_START) { struct l2cap_hdr *hdr; struct sock *sk; u16 cid; @@ -3795,6 +4810,32 @@ static const struct file_operations l2cap_debugfs_fops = { static struct dentry *l2cap_debugfs; +static const struct proto_ops l2cap_sock_ops = { + .family = PF_BLUETOOTH, + .owner = THIS_MODULE, + .release = l2cap_sock_release, + .bind = l2cap_sock_bind, + .connect = l2cap_sock_connect, + .listen = l2cap_sock_listen, + .accept = l2cap_sock_accept, + .getname = l2cap_sock_getname, + .sendmsg = l2cap_sock_sendmsg, + .recvmsg = l2cap_sock_recvmsg, + .poll = bt_sock_poll, + .ioctl = bt_sock_ioctl, + .mmap = sock_no_mmap, + .socketpair = sock_no_socketpair, + .shutdown = l2cap_sock_shutdown, + .setsockopt = l2cap_sock_setsockopt, + .getsockopt = l2cap_sock_getsockopt +}; + +static const struct net_proto_family l2cap_sock_family_ops = { + .family = PF_BLUETOOTH, + .owner = THIS_MODULE, + .create = l2cap_sock_create, +}; + static struct hci_proto l2cap_hci_proto = { .name = "L2CAP", .id = HCI_PROTO_L2CAP, @@ -3810,13 +4851,19 @@ static int __init l2cap_init(void) { int err; - err = l2cap_init_sockets(); + err = proto_register(&l2cap_proto, 0); if (err < 0) return err; _busy_wq = create_singlethread_workqueue("l2cap"); if (!_busy_wq) { - err = -ENOMEM; + proto_unregister(&l2cap_proto); + return -ENOMEM; + } + + err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); + if (err < 0) { + BT_ERR("L2CAP socket registration failed"); goto error; } @@ -3841,7 +4888,7 @@ static int __init l2cap_init(void) error: destroy_workqueue(_busy_wq); - l2cap_cleanup_sockets(); + proto_unregister(&l2cap_proto); return err; } @@ -3852,10 +4899,13 @@ static void __exit l2cap_exit(void) flush_workqueue(_busy_wq); destroy_workqueue(_busy_wq); + if (bt_sock_unregister(BTPROTO_L2CAP) < 0) + BT_ERR("L2CAP socket unregistration failed"); + if (hci_unregister_proto(&l2cap_hci_proto) < 0) BT_ERR("L2CAP protocol unregistration failed"); - l2cap_cleanup_sockets(); + proto_unregister(&l2cap_proto); } void l2cap_load(void) diff --git a/trunk/net/bluetooth/l2cap_sock.c b/trunk/net/bluetooth/l2cap_sock.c deleted file mode 100644 index adf41692daf3..000000000000 --- a/trunk/net/bluetooth/l2cap_sock.c +++ /dev/null @@ -1,1150 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2009-2010 Gustavo F. Padovan - Copyright (C) 2010 Google Inc. - - Written 2000,2001 by Maxim Krasnyansky - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 as - published by the Free Software Foundation; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth L2CAP sockets. */ - -#include -#include -#include - -/* ---- L2CAP timers ---- */ -static void l2cap_sock_timeout(unsigned long arg) -{ - struct sock *sk = (struct sock *) arg; - int reason; - - BT_DBG("sock %p state %d", sk, sk->sk_state); - - bh_lock_sock(sk); - - if (sock_owned_by_user(sk)) { - /* sk is owned by user. Try again later */ - l2cap_sock_set_timer(sk, HZ / 5); - bh_unlock_sock(sk); - sock_put(sk); - return; - } - - if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG) - reason = ECONNREFUSED; - else if (sk->sk_state == BT_CONNECT && - l2cap_pi(sk)->sec_level != BT_SECURITY_SDP) - reason = ECONNREFUSED; - else - reason = ETIMEDOUT; - - __l2cap_sock_close(sk, reason); - - bh_unlock_sock(sk); - - l2cap_sock_kill(sk); - sock_put(sk); -} - -void l2cap_sock_set_timer(struct sock *sk, long timeout) -{ - BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); - sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); -} - -void l2cap_sock_clear_timer(struct sock *sk) -{ - BT_DBG("sock %p state %d", sk, sk->sk_state); - sk_stop_timer(sk, &sk->sk_timer); -} - -static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) -{ - struct sock *sk; - struct hlist_node *node; - sk_for_each(sk, node, &l2cap_sk_list.head) - if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) - goto found; - sk = NULL; -found: - return sk; -} - -static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) -{ - struct sock *sk = sock->sk; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - if (la.l2_psm) { - __u16 psm = __le16_to_cpu(la.l2_psm); - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((psm & 0x0101) != 0x0001) { - err = -EINVAL; - goto done; - } - - /* Restrict usage of well-known PSMs */ - if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { - err = -EACCES; - goto done; - } - } - - write_lock_bh(&l2cap_sk_list.lock); - - if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); - l2cap_pi(sk)->psm = la.l2_psm; - l2cap_pi(sk)->sport = la.l2_psm; - sk->sk_state = BT_BOUND; - - if (__le16_to_cpu(la.l2_psm) == 0x0001 || - __le16_to_cpu(la.l2_psm) == 0x0003) - l2cap_pi(sk)->sec_level = BT_SECURITY_SDP; - } - - write_unlock_bh(&l2cap_sk_list.lock); - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || alen < sizeof(addr->sa_family) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid) - return -EINVAL; - - lock_sock(sk); - - if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) - && !la.l2_psm) { - err = -EINVAL; - goto done; - } - - switch (l2cap_pi(sk)->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - switch (sk->sk_state) { - case BT_CONNECT: - case BT_CONNECT2: - case BT_CONFIG: - /* Already connecting */ - goto wait; - - case BT_CONNECTED: - /* Already connected */ - err = -EISCONN; - goto done; - - case BT_OPEN: - case BT_BOUND: - /* Can connect */ - break; - - default: - err = -EBADFD; - goto done; - } - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 && - sk->sk_type != SOCK_RAW) { - err = -EINVAL; - goto done; - } - - /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr); - l2cap_pi(sk)->psm = la.l2_psm; - - err = l2cap_do_connect(sk); - if (err) - goto done; - -wait: - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) - || sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - switch (l2cap_pi(sk)->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - if (!l2cap_pi(sk)->psm) { - bdaddr_t *src = &bt_sk(sk)->src; - u16 psm; - - err = -EINVAL; - - write_lock_bh(&l2cap_sk_list.lock); - - for (psm = 0x1001; psm < 0x1100; psm += 2) - if (!__l2cap_get_sock_by_addr(cpu_to_le16(psm), src)) { - l2cap_pi(sk)->psm = cpu_to_le16(psm); - l2cap_pi(sk)->sport = cpu_to_le16(psm); - err = 0; - break; - } - - write_unlock_bh(&l2cap_sk_list.lock); - - if (err < 0) - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *nsk; - long timeo; - int err = 0; - - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - goto done; - } - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (!(nsk = bt_accept_dequeue(sk, newsock))) { - set_current_state(TASK_INTERRUPTIBLE); - if (!timeo) { - err = -EAGAIN; - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", nsk); - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; - struct sock *sk = sock->sk; - - BT_DBG("sock %p, sk %p", sock, sk); - - addr->sa_family = AF_BLUETOOTH; - *len = sizeof(struct sockaddr_l2); - - if (peer) { - la->l2_psm = l2cap_pi(sk)->psm; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - la->l2_cid = cpu_to_le16(l2cap_pi(sk)->dcid); - } else { - la->l2_psm = l2cap_pi(sk)->sport; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); - la->l2_cid = cpu_to_le16(l2cap_pi(sk)->scid); - } - - return 0; -} - -static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_options opts; - struct l2cap_conninfo cinfo; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - opts.imtu = l2cap_pi(sk)->imtu; - opts.omtu = l2cap_pi(sk)->omtu; - opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = l2cap_pi(sk)->mode; - opts.fcs = l2cap_pi(sk)->fcs; - opts.max_tx = l2cap_pi(sk)->max_tx; - opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; - - len = min_t(unsigned int, len, sizeof(opts)); - if (copy_to_user(optval, (char *) &opts, len)) - err = -EFAULT; - - break; - - case L2CAP_LM: - switch (l2cap_pi(sk)->sec_level) { - case BT_SECURITY_LOW: - opt = L2CAP_LM_AUTH; - break; - case BT_SECURITY_MEDIUM: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; - break; - case BT_SECURITY_HIGH: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | - L2CAP_LM_SECURE; - break; - default: - opt = 0; - break; - } - - if (l2cap_pi(sk)->role_switch) - opt |= L2CAP_LM_MASTER; - - if (l2cap_pi(sk)->force_reliable) - opt |= L2CAP_LM_RELIABLE; - - if (put_user(opt, (u32 __user *) optval)) - err = -EFAULT; - break; - - case L2CAP_CONNINFO: - if (sk->sk_state != BT_CONNECTED && - !(sk->sk_state == BT_CONNECT2 && - bt_sk(sk)->defer_setup)) { - err = -ENOTCONN; - break; - } - - cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; - memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { - err = -EINVAL; - break; - } - - sec.level = l2cap_pi(sk)->sec_level; - - len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) - err = -EFAULT; - - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) - err = -EFAULT; - - break; - - case BT_FLUSHABLE: - if (put_user(l2cap_pi(sk)->flushable, (u32 __user *) optval)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_options opts; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - if (sk->sk_state == BT_CONNECTED) { - err = -EINVAL; - break; - } - - opts.imtu = l2cap_pi(sk)->imtu; - opts.omtu = l2cap_pi(sk)->omtu; - opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = l2cap_pi(sk)->mode; - opts.fcs = l2cap_pi(sk)->fcs; - opts.max_tx = l2cap_pi(sk)->max_tx; - opts.txwin_size = (__u16)l2cap_pi(sk)->tx_win; - - len = min_t(unsigned int, sizeof(opts), optlen); - if (copy_from_user((char *) &opts, optval, len)) { - err = -EFAULT; - break; - } - - if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { - err = -EINVAL; - break; - } - - l2cap_pi(sk)->mode = opts.mode; - switch (l2cap_pi(sk)->mode) { - case L2CAP_MODE_BASIC: - l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_STATE2_DEVICE; - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -EINVAL; - break; - } - - l2cap_pi(sk)->imtu = opts.imtu; - l2cap_pi(sk)->omtu = opts.omtu; - l2cap_pi(sk)->fcs = opts.fcs; - l2cap_pi(sk)->max_tx = opts.max_tx; - l2cap_pi(sk)->tx_win = (__u8)opts.txwin_size; - break; - - case L2CAP_LM: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt & L2CAP_LM_AUTH) - l2cap_pi(sk)->sec_level = BT_SECURITY_LOW; - if (opt & L2CAP_LM_ENCRYPT) - l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM; - if (opt & L2CAP_LM_SECURE) - l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH; - - l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER); - l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { - err = -EINVAL; - break; - } - - sec.level = BT_SECURITY_LOW; - - len = min_t(unsigned int, sizeof(sec), optlen); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; - } - - if (sec.level < BT_SECURITY_LOW || - sec.level > BT_SECURITY_HIGH) { - err = -EINVAL; - break; - } - - l2cap_pi(sk)->sec_level = sec.level; - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - bt_sk(sk)->defer_setup = opt; - break; - - case BT_FLUSHABLE: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt > BT_FLUSHABLE_ON) { - err = -EINVAL; - break; - } - - if (opt == BT_FLUSHABLE_OFF) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - /* proceed futher only when we have l2cap_conn and - No Flush support in the LM */ - if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { - err = -EINVAL; - break; - } - } - - l2cap_pi(sk)->flushable = opt; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct sk_buff *skb; - u16 control; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - err = sock_error(sk); - if (err) - return err; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - lock_sock(sk); - - if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - goto done; - } - - /* Connectionless channel */ - if (sk->sk_type == SOCK_DGRAM) { - skb = l2cap_create_connless_pdu(sk, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - } else { - l2cap_do_send(sk, skb); - err = len; - } - goto done; - } - - switch (pi->mode) { - case L2CAP_MODE_BASIC: - /* Check outgoing MTU */ - if (len > pi->omtu) { - err = -EMSGSIZE; - goto done; - } - - /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(sk, msg, len); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - - l2cap_do_send(sk, skb); - err = len; - break; - - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - /* Entire SDU fits into one PDU */ - if (len <= pi->remote_mps) { - control = L2CAP_SDU_UNSEGMENTED; - skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0); - if (IS_ERR(skb)) { - err = PTR_ERR(skb); - goto done; - } - __skb_queue_tail(TX_QUEUE(sk), skb); - - if (sk->sk_send_head == NULL) - sk->sk_send_head = skb; - - } else { - /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(sk, msg, len); - if (err < 0) - goto done; - } - - if (pi->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(sk); - } else { - if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY) && - (pi->conn_state & L2CAP_CONN_WAIT_F)) { - err = len; - break; - } - err = l2cap_ertm_send(sk); - } - - if (err >= 0) - err = len; - break; - - default: - BT_DBG("bad state %1.1x", pi->mode); - err = -EBADFD; - } - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) -{ - struct sock *sk = sock->sk; - - lock_sock(sk); - - if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { - struct l2cap_conn_rsp rsp; - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - u8 buf[128]; - - sk->sk_state = BT_CONFIG; - - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - - if (l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT) { - release_sock(sk); - return 0; - } - - l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT; - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(sk, buf), buf); - l2cap_pi(sk)->num_conf_req++; - - release_sock(sk); - return 0; - } - - release_sock(sk); - - if (sock->type == SOCK_STREAM) - return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); - - return bt_sock_recvmsg(iocb, sock, msg, len, flags); -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -void l2cap_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) - return; - - BT_DBG("sk %p state %d", sk, sk->sk_state); - - /* Kill poor orphan */ - bt_sock_unlink(&l2cap_sk_list, sk); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -/* Must be called on unlocked socket. */ -static void l2cap_sock_close(struct sock *sk) -{ - l2cap_sock_clear_timer(sk); - lock_sock(sk); - __l2cap_sock_close(sk, ECONNRESET); - release_sock(sk); - l2cap_sock_kill(sk); -} - -static void l2cap_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) - l2cap_sock_close(sk); - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -void __l2cap_sock_close(struct sock *sk, int reason) -{ - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - l2cap_sock_cleanup_listen(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if (sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - - l2cap_sock_set_timer(sk, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, sk, reason); - } else - l2cap_chan_del(sk, reason); - break; - - case BT_CONNECT2: - if (sk->sk_type == SOCK_SEQPACKET || - sk->sk_type == SOCK_STREAM) { - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct l2cap_conn_rsp rsp; - __u16 result; - - if (bt_sk(sk)->defer_setup) - result = L2CAP_CR_SEC_BLOCK; - else - result = L2CAP_CR_BAD_PSM; - - rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid); - rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - } else - l2cap_chan_del(sk, reason); - break; - - case BT_CONNECT: - case BT_DISCONN: - l2cap_chan_del(sk, reason); - break; - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - -static int l2cap_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - lock_sock(sk); - if (!sk->sk_shutdown) { - if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) - err = __l2cap_wait_ack(sk); - - sk->sk_shutdown = SHUTDOWN_MASK; - l2cap_sock_clear_timer(sk); - __l2cap_sock_close(sk, 0); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); - } - - if (!err && sk->sk_err) - err = -sk->sk_err; - - release_sock(sk); - return err; -} - -static int l2cap_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - err = l2cap_sock_shutdown(sock, 2); - - sock_orphan(sk); - l2cap_sock_kill(sk); - return err; -} - -static void l2cap_sock_destruct(struct sock *sk) -{ - BT_DBG("sk %p", sk); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); -} - -void l2cap_sock_init(struct sock *sk, struct sock *parent) -{ - struct l2cap_pinfo *pi = l2cap_pi(sk); - - BT_DBG("sk %p", sk); - - if (parent) { - sk->sk_type = parent->sk_type; - bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; - - pi->imtu = l2cap_pi(parent)->imtu; - pi->omtu = l2cap_pi(parent)->omtu; - pi->conf_state = l2cap_pi(parent)->conf_state; - pi->mode = l2cap_pi(parent)->mode; - pi->fcs = l2cap_pi(parent)->fcs; - pi->max_tx = l2cap_pi(parent)->max_tx; - pi->tx_win = l2cap_pi(parent)->tx_win; - pi->sec_level = l2cap_pi(parent)->sec_level; - pi->role_switch = l2cap_pi(parent)->role_switch; - pi->force_reliable = l2cap_pi(parent)->force_reliable; - pi->flushable = l2cap_pi(parent)->flushable; - } else { - pi->imtu = L2CAP_DEFAULT_MTU; - pi->omtu = 0; - if (!disable_ertm && sk->sk_type == SOCK_STREAM) { - pi->mode = L2CAP_MODE_ERTM; - pi->conf_state |= L2CAP_CONF_STATE2_DEVICE; - } else { - pi->mode = L2CAP_MODE_BASIC; - } - pi->max_tx = L2CAP_DEFAULT_MAX_TX; - pi->fcs = L2CAP_FCS_CRC16; - pi->tx_win = L2CAP_DEFAULT_TX_WINDOW; - pi->sec_level = BT_SECURITY_LOW; - pi->role_switch = 0; - pi->force_reliable = 0; - pi->flushable = BT_FLUSHABLE_OFF; - } - - /* Default config options */ - pi->conf_len = 0; - pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; - skb_queue_head_init(TX_QUEUE(sk)); - skb_queue_head_init(SREJ_QUEUE(sk)); - skb_queue_head_init(BUSY_QUEUE(sk)); - INIT_LIST_HEAD(SREJ_LIST(sk)); -} - -static struct proto l2cap_proto = { - .name = "L2CAP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct l2cap_pinfo) -}; - -struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct sock *sk; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - setup_timer(&sk->sk_timer, l2cap_sock_timeout, (unsigned long) sk); - - bt_sock_link(&l2cap_sk_list, sk); - return sk; -} - -static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && - sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) - return -EPERM; - - sock->ops = &l2cap_sock_ops; - - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - l2cap_sock_init(sk, NULL); - return 0; -} - -const struct proto_ops l2cap_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = l2cap_sock_release, - .bind = l2cap_sock_bind, - .connect = l2cap_sock_connect, - .listen = l2cap_sock_listen, - .accept = l2cap_sock_accept, - .getname = l2cap_sock_getname, - .sendmsg = l2cap_sock_sendmsg, - .recvmsg = l2cap_sock_recvmsg, - .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, - .mmap = sock_no_mmap, - .socketpair = sock_no_socketpair, - .shutdown = l2cap_sock_shutdown, - .setsockopt = l2cap_sock_setsockopt, - .getsockopt = l2cap_sock_getsockopt -}; - -static const struct net_proto_family l2cap_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = l2cap_sock_create, -}; - -int __init l2cap_init_sockets(void) -{ - int err; - - err = proto_register(&l2cap_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); - if (err < 0) - goto error; - - BT_INFO("L2CAP socket layer initialized"); - - return 0; - -error: - BT_ERR("L2CAP socket registration failed"); - proto_unregister(&l2cap_proto); - return err; -} - -void l2cap_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_L2CAP) < 0) - BT_ERR("L2CAP socket unregistration failed"); - - proto_unregister(&l2cap_proto); -} diff --git a/trunk/net/bluetooth/mgmt.c b/trunk/net/bluetooth/mgmt.c index b2bda83050a4..f827fd908380 100644 --- a/trunk/net/bluetooth/mgmt.c +++ b/trunk/net/bluetooth/mgmt.c @@ -32,16 +32,6 @@ #define MGMT_VERSION 0 #define MGMT_REVISION 1 -struct pending_cmd { - struct list_head list; - __u16 opcode; - int index; - void *cmd; - struct sock *sk; -}; - -LIST_HEAD(cmd_list); - static int cmd_status(struct sock *sk, u16 cmd, u8 status) { struct sk_buff *skb; @@ -69,26 +59,29 @@ static int cmd_status(struct sock *sk, u16 cmd, u8 status) return 0; } -static int cmd_complete(struct sock *sk, u16 cmd, void *rp, size_t rp_len) +static int read_version(struct sock *sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_read_version *rp; BT_DBG("sock %p", sk); - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC); + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); if (!skb) return -ENOMEM; hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); - put_unaligned_le16(cmd, &ev->opcode); - memcpy(ev->data, rp, rp_len); + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp)); + rp->version = MGMT_VERSION; + put_unaligned_le16(MGMT_REVISION, &rp->revision); if (sock_queue_rcv_skb(sk, skb) < 0) kfree_skb(skb); @@ -96,25 +89,16 @@ static int cmd_complete(struct sock *sk, u16 cmd, void *rp, size_t rp_len) return 0; } -static int read_version(struct sock *sk) -{ - struct mgmt_rp_read_version rp; - - BT_DBG("sock %p", sk); - - rp.version = MGMT_VERSION; - put_unaligned_le16(MGMT_REVISION, &rp.revision); - - return cmd_complete(sk, MGMT_OP_READ_VERSION, &rp, sizeof(rp)); -} - static int read_index_list(struct sock *sk) { + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; struct mgmt_rp_read_index_list *rp; struct list_head *p; - size_t rp_len; + size_t body_len; u16 count; - int i, err; + int i; BT_DBG("sock %p", sk); @@ -125,43 +109,43 @@ static int read_index_list(struct sock *sk) count++; } - rp_len = sizeof(*rp) + (2 * count); - rp = kmalloc(rp_len, GFP_ATOMIC); - if (!rp) { - read_unlock(&hci_dev_list_lock); + body_len = sizeof(*ev) + sizeof(*rp) + (2 * count); + skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC); + if (!skb) return -ENOMEM; - } + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(body_len); + + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode); + + rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count)); put_unaligned_le16(count, &rp->num_controllers); i = 0; list_for_each(p, &hci_dev_list) { struct hci_dev *d = list_entry(p, struct hci_dev, list); - - hci_del_off_timer(d); - - set_bit(HCI_MGMT, &d->flags); - - if (test_bit(HCI_SETUP, &d->flags)) - continue; - put_unaligned_le16(d->id, &rp->index[i++]); BT_DBG("Added hci%u", d->id); } read_unlock(&hci_dev_list_lock); - err = cmd_complete(sk, MGMT_OP_READ_INDEX_LIST, rp, rp_len); - - kfree(rp); + if (sock_queue_rcv_skb(sk, skb) < 0) + kfree_skb(skb); - return err; + return 0; } static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) { - struct mgmt_rp_read_info rp; - struct mgmt_cp_read_info *cp = (void *) data; + struct sk_buff *skb; + struct mgmt_hdr *hdr; + struct mgmt_ev_cmd_complete *ev; + struct mgmt_rp_read_info *rp; + struct mgmt_cp_read_info *cp; struct hci_dev *hdev; u16 dev_id; @@ -170,143 +154,18 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) if (len != 2) return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL); - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); - - hci_del_off_timer(hdev); - - hci_dev_lock_bh(hdev); - - set_bit(HCI_MGMT, &hdev->flags); - - put_unaligned_le16(hdev->id, &rp.index); - rp.type = hdev->dev_type; - - rp.powered = test_bit(HCI_UP, &hdev->flags); - rp.connectable = test_bit(HCI_PSCAN, &hdev->flags); - rp.discoverable = test_bit(HCI_ISCAN, &hdev->flags); - rp.pairable = test_bit(HCI_PSCAN, &hdev->flags); - - if (test_bit(HCI_AUTH, &hdev->flags)) - rp.sec_mode = 3; - else if (hdev->ssp_mode > 0) - rp.sec_mode = 4; - else - rp.sec_mode = 2; - - bacpy(&rp.bdaddr, &hdev->bdaddr); - memcpy(rp.features, hdev->features, 8); - memcpy(rp.dev_class, hdev->dev_class, 3); - put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); - rp.hci_ver = hdev->hci_ver; - put_unaligned_le16(hdev->hci_rev, &rp.hci_rev); - - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return cmd_complete(sk, MGMT_OP_READ_INFO, &rp, sizeof(rp)); -} - -static void mgmt_pending_free(struct pending_cmd *cmd) -{ - sock_put(cmd->sk); - kfree(cmd->cmd); - kfree(cmd); -} - -static int mgmt_pending_add(struct sock *sk, u16 opcode, int index, - void *data, u16 len) -{ - struct pending_cmd *cmd; - - cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); - if (!cmd) - return -ENOMEM; - - cmd->opcode = opcode; - cmd->index = index; - - cmd->cmd = kmalloc(len, GFP_ATOMIC); - if (!cmd->cmd) { - kfree(cmd); + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); + if (!skb) return -ENOMEM; - } - - memcpy(cmd->cmd, data, len); - - cmd->sk = sk; - sock_hold(sk); - - list_add(&cmd->list, &cmd_list); - - return 0; -} - -static void mgmt_pending_foreach(u16 opcode, int index, - void (*cb)(struct pending_cmd *cmd, void *data), - void *data) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &cmd_list) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); - - if (cmd->opcode != opcode) - continue; - - if (index >= 0 && cmd->index != index) - continue; - - cb(cmd, data); - } -} - -static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) -{ - struct list_head *p; - - list_for_each(p, &cmd_list) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); - - if (cmd->opcode != opcode) - continue; - - if (index >= 0 && cmd->index != index) - continue; - - return cmd; - } - - return NULL; -} - -static void mgmt_pending_remove(u16 opcode, int index) -{ - struct pending_cmd *cmd; - cmd = mgmt_pending_find(opcode, index); - if (cmd == NULL) - return; + hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - list_del(&cmd->list); - mgmt_pending_free(cmd); -} + ev = (void *) skb_put(skb, sizeof(*ev)); + put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode); -static int set_powered(struct sock *sk, unsigned char *data, u16 len) -{ - struct mgmt_mode *cp; - struct hci_dev *hdev; - u16 dev_id; - int ret, up; + rp = (void *) skb_put(skb, sizeof(*rp)); cp = (void *) data; dev_id = get_unaligned_le16(&cp->index); @@ -314,151 +173,100 @@ static int set_powered(struct sock *sk, unsigned char *data, u16 len) BT_DBG("request for hci%u", dev_id); hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV); + if (!hdev) { + kfree_skb(skb); + return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); + } hci_dev_lock_bh(hdev); - up = test_bit(HCI_UP, &hdev->flags); - if ((cp->val && up) || (!cp->val && !up)) { - ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) { - ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY); - goto failed; - } + put_unaligned_le16(hdev->id, &rp->index); + rp->type = hdev->dev_type; - ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len); - if (ret < 0) - goto failed; + rp->powered = test_bit(HCI_UP, &hdev->flags); + rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); + rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); - if (cp->val) - queue_work(hdev->workqueue, &hdev->power_on); + if (test_bit(HCI_AUTH, &hdev->flags)) + rp->sec_mode = 3; + else if (hdev->ssp_mode > 0) + rp->sec_mode = 4; else - queue_work(hdev->workqueue, &hdev->power_off); + rp->sec_mode = 2; - ret = 0; + bacpy(&rp->bdaddr, &hdev->bdaddr); + memcpy(rp->features, hdev->features, 8); + memcpy(rp->dev_class, hdev->dev_class, 3); + put_unaligned_le16(hdev->manufacturer, &rp->manufacturer); + rp->hci_ver = hdev->hci_ver; + put_unaligned_le16(hdev->hci_rev, &rp->hci_rev); -failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return ret; -} - -static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) -{ - struct mgmt_mode *cp; - struct hci_dev *hdev; - u16 dev_id; - u8 scan; - int err; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV); - - hci_dev_lock_bh(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); - goto failed; - } - - if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && - test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY); - goto failed; - } - - err = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len); - if (err < 0) - goto failed; - - scan = SCAN_PAGE; - - if (cp->val) - scan |= SCAN_INQUIRY; - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (err < 0) - mgmt_pending_remove(MGMT_OP_SET_DISCOVERABLE, dev_id); -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); + if (sock_queue_rcv_skb(sk, skb) < 0) + kfree_skb(skb); - return err; + return 0; } -static int set_connectable(struct sock *sk, unsigned char *data, u16 len) +int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { - struct mgmt_mode *cp; - struct hci_dev *hdev; - u16 dev_id; - u8 scan; + unsigned char *buf; + struct mgmt_hdr *hdr; + u16 opcode, len; int err; - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); + BT_DBG("got %zu bytes", msglen); - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV); + if (msglen < sizeof(*hdr)) + return -EINVAL; - hci_dev_lock_bh(hdev); + buf = kmalloc(msglen, GFP_ATOMIC); + if (!buf) + return -ENOMEM; - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN); - goto failed; + if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { + err = -EFAULT; + goto done; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY); - goto failed; - } + hdr = (struct mgmt_hdr *) buf; + opcode = get_unaligned_le16(&hdr->opcode); + len = get_unaligned_le16(&hdr->len); - if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); - goto failed; + if (len != msglen - sizeof(*hdr)) { + err = -EINVAL; + goto done; } - err = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len); - if (err < 0) - goto failed; - - if (cp->val) - scan = SCAN_PAGE; - else - scan = 0; + switch (opcode) { + case MGMT_OP_READ_VERSION: + err = read_version(sk); + break; + case MGMT_OP_READ_INDEX_LIST: + err = read_index_list(sk); + break; + case MGMT_OP_READ_INFO: + err = read_controller_info(sk, buf + sizeof(*hdr), len); + break; + default: + BT_DBG("Unknown op %u", opcode); + err = cmd_status(sk, opcode, 0x01); + break; + } - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) - mgmt_pending_remove(MGMT_OP_SET_CONNECTABLE, dev_id); + goto done; -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); + err = msglen; +done: + kfree(buf); return err; } -static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) +static int mgmt_event(u16 event, void *data, u16 data_len) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -475,918 +283,26 @@ static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) memcpy(skb_put(skb, data_len), data, data_len); - hci_send_to_sock(NULL, skb, skip_sk); + hci_send_to_sock(NULL, skb); kfree_skb(skb); return 0; } -static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) -{ - struct mgmt_mode rp; - - put_unaligned_le16(index, &rp.index); - rp.val = val; - - return cmd_complete(sk, opcode, &rp, sizeof(rp)); -} - -static int set_pairable(struct sock *sk, unsigned char *data, u16 len) -{ - struct mgmt_mode *cp, ev; - struct hci_dev *hdev; - u16 dev_id; - int err; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV); - - hci_dev_lock_bh(hdev); - - if (cp->val) - set_bit(HCI_PAIRABLE, &hdev->flags); - else - clear_bit(HCI_PAIRABLE, &hdev->flags); - - err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val); - if (err < 0) - goto failed; - - put_unaligned_le16(dev_id, &ev.index); - ev.val = cp->val; - - err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static u8 get_service_classes(struct hci_dev *hdev) -{ - struct list_head *p; - u8 val = 0; - - list_for_each(p, &hdev->uuids) { - struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); - - val |= uuid->svc_hint; - } - - return val; -} - -static int update_class(struct hci_dev *hdev) +int mgmt_index_added(u16 index) { - u8 cod[3]; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) - return 0; - - cod[0] = hdev->minor_class; - cod[1] = hdev->major_class; - cod[2] = get_service_classes(hdev); + struct mgmt_ev_index_added ev; - if (memcmp(cod, hdev->dev_class, 3) == 0) - return 0; + put_unaligned_le16(index, &ev.index); - return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); + return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev)); } -static int add_uuid(struct sock *sk, unsigned char *data, u16 len) +int mgmt_index_removed(u16 index) { - struct mgmt_cp_add_uuid *cp; - struct hci_dev *hdev; - struct bt_uuid *uuid; - u16 dev_id; - int err; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); + struct mgmt_ev_index_added ev; - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_ADD_UUID, ENODEV); + put_unaligned_le16(index, &ev.index); - hci_dev_lock_bh(hdev); - - uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); - if (!uuid) { - err = -ENOMEM; - goto failed; - } - - memcpy(uuid->uuid, cp->uuid, 16); - uuid->svc_hint = cp->svc_hint; - - list_add(&uuid->list, &hdev->uuids); - - err = update_class(hdev); - if (err < 0) - goto failed; - - err = cmd_complete(sk, MGMT_OP_ADD_UUID, &dev_id, sizeof(dev_id)); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) -{ - struct list_head *p, *n; - struct mgmt_cp_add_uuid *cp; - struct hci_dev *hdev; - u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - u16 dev_id; - int err, found; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_REMOVE_UUID, ENODEV); - - hci_dev_lock_bh(hdev); - - if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { - err = hci_uuids_clear(hdev); - goto unlock; - } - - found = 0; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *match = list_entry(p, struct bt_uuid, list); - - if (memcmp(match->uuid, cp->uuid, 16) != 0) - continue; - - list_del(&match->list); - found++; - } - - if (found == 0) { - err = cmd_status(sk, MGMT_OP_REMOVE_UUID, ENOENT); - goto unlock; - } - - err = update_class(hdev); - if (err < 0) - goto unlock; - - err = cmd_complete(sk, MGMT_OP_REMOVE_UUID, &dev_id, sizeof(dev_id)); - -unlock: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_set_dev_class *cp; - u16 dev_id; - int err; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV); - - hci_dev_lock_bh(hdev); - - hdev->major_class = cp->major; - hdev->minor_class = cp->minor; - - err = update_class(hdev); - - if (err == 0) - err = cmd_complete(sk, MGMT_OP_SET_DEV_CLASS, &dev_id, - sizeof(dev_id)); - - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_set_service_cache *cp; - u16 dev_id; - int err; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV); - - hci_dev_lock_bh(hdev); - - BT_DBG("hci%u enable %d", dev_id, cp->enable); - - if (cp->enable) { - set_bit(HCI_SERVICE_CACHE, &hdev->flags); - err = 0; - } else { - clear_bit(HCI_SERVICE_CACHE, &hdev->flags); - err = update_class(hdev); - } - - if (err == 0) - err = cmd_complete(sk, MGMT_OP_SET_SERVICE_CACHE, &dev_id, - sizeof(dev_id)); - - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int load_keys(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_load_keys *cp; - u16 dev_id, key_count, expected_len; - int i; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - key_count = get_unaligned_le16(&cp->key_count); - - expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); - if (expected_len != len) { - BT_ERR("load_keys: expected %u bytes, got %u bytes", - len, expected_len); - return -EINVAL; - } - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_LOAD_KEYS, ENODEV); - - BT_DBG("hci%u debug_keys %u key_count %u", dev_id, cp->debug_keys, - key_count); - - hci_dev_lock_bh(hdev); - - hci_link_keys_clear(hdev); - - set_bit(HCI_LINK_KEYS, &hdev->flags); - - if (cp->debug_keys) - set_bit(HCI_DEBUG_KEYS, &hdev->flags); - else - clear_bit(HCI_DEBUG_KEYS, &hdev->flags); - - for (i = 0; i < key_count; i++) { - struct mgmt_key_info *key = &cp->keys[i]; - - hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type, - key->pin_len); - } - - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return 0; -} - -static int remove_key(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_remove_key *cp; - struct hci_conn *conn; - u16 dev_id; - int err; - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_REMOVE_KEY, ENODEV); - - hci_dev_lock_bh(hdev); - - err = hci_remove_link_key(hdev, &cp->bdaddr); - if (err < 0) { - err = cmd_status(sk, MGMT_OP_REMOVE_KEY, -err); - goto unlock; - } - - err = 0; - - if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (conn) { - struct hci_cp_disconnect dc; - - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL); - } - -unlock: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int disconnect(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_disconnect *cp; - struct hci_cp_disconnect dc; - struct hci_conn *conn; - u16 dev_id; - int err; - - BT_DBG(""); - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); - - hci_dev_lock_bh(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, ENETDOWN); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_DISCONNECT, dev_id)) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, EBUSY); - goto failed; - } - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (!conn) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, ENOTCONN); - goto failed; - } - - err = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, dev_id, data, len); - if (err < 0) - goto failed; - - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); - if (err < 0) - mgmt_pending_remove(MGMT_OP_DISCONNECT, dev_id); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int get_connections(struct sock *sk, unsigned char *data, u16 len) -{ - struct mgmt_cp_get_connections *cp; - struct mgmt_rp_get_connections *rp; - struct hci_dev *hdev; - struct list_head *p; - size_t rp_len; - u16 dev_id, count; - int i, err; - - BT_DBG(""); - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_GET_CONNECTIONS, ENODEV); - - hci_dev_lock_bh(hdev); - - count = 0; - list_for_each(p, &hdev->conn_hash.list) { - count++; - } - - rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t)); - rp = kmalloc(rp_len, GFP_ATOMIC); - if (!rp) { - err = -ENOMEM; - goto unlock; - } - - put_unaligned_le16(dev_id, &rp->index); - put_unaligned_le16(count, &rp->conn_count); - - read_lock(&hci_dev_list_lock); - - i = 0; - list_for_each(p, &hdev->conn_hash.list) { - struct hci_conn *c = list_entry(p, struct hci_conn, list); - - bacpy(&rp->conn[i++], &c->dst); - } - - read_unlock(&hci_dev_list_lock); - - err = cmd_complete(sk, MGMT_OP_GET_CONNECTIONS, rp, rp_len); - -unlock: - kfree(rp); - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - return err; -} - -static int pin_code_reply(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_pin_code_reply *cp; - struct hci_cp_pin_code_reply reply; - u16 dev_id; - int err; - - BT_DBG(""); - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); - - hci_dev_lock_bh(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); - goto failed; - } - - err = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, dev_id, data, len); - if (err < 0) - goto failed; - - bacpy(&reply.bdaddr, &cp->bdaddr); - reply.pin_len = cp->pin_len; - memcpy(reply.pin_code, cp->pin_code, 16); - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); - if (err < 0) - mgmt_pending_remove(MGMT_OP_PIN_CODE_REPLY, dev_id); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int pin_code_neg_reply(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_pin_code_neg_reply *cp; - u16 dev_id; - int err; - - BT_DBG(""); - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENODEV); - - hci_dev_lock_bh(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENETDOWN); - goto failed; - } - - err = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, dev_id, - data, len); - if (err < 0) - goto failed; - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(bdaddr_t), - &cp->bdaddr); - if (err < 0) - mgmt_pending_remove(MGMT_OP_PIN_CODE_NEG_REPLY, dev_id); - -failed: - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return err; -} - -static int set_io_capability(struct sock *sk, unsigned char *data, u16 len) -{ - struct hci_dev *hdev; - struct mgmt_cp_set_io_capability *cp; - u16 dev_id; - - BT_DBG(""); - - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - hdev = hci_dev_get(dev_id); - if (!hdev) - return cmd_status(sk, MGMT_OP_SET_IO_CAPABILITY, ENODEV); - - hci_dev_lock_bh(hdev); - - hdev->io_capability = cp->io_capability; - - BT_DBG("%s IO capability set to 0x%02x", hdev->name, - hdev->io_capability); - - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - - return cmd_complete(sk, MGMT_OP_SET_IO_CAPABILITY, - &dev_id, sizeof(dev_id)); -} - -int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) -{ - unsigned char *buf; - struct mgmt_hdr *hdr; - u16 opcode, len; - int err; - - BT_DBG("got %zu bytes", msglen); - - if (msglen < sizeof(*hdr)) - return -EINVAL; - - buf = kmalloc(msglen, GFP_ATOMIC); - if (!buf) - return -ENOMEM; - - if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { - err = -EFAULT; - goto done; - } - - hdr = (struct mgmt_hdr *) buf; - opcode = get_unaligned_le16(&hdr->opcode); - len = get_unaligned_le16(&hdr->len); - - if (len != msglen - sizeof(*hdr)) { - err = -EINVAL; - goto done; - } - - switch (opcode) { - case MGMT_OP_READ_VERSION: - err = read_version(sk); - break; - case MGMT_OP_READ_INDEX_LIST: - err = read_index_list(sk); - break; - case MGMT_OP_READ_INFO: - err = read_controller_info(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_POWERED: - err = set_powered(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_DISCOVERABLE: - err = set_discoverable(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_CONNECTABLE: - err = set_connectable(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_PAIRABLE: - err = set_pairable(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_ADD_UUID: - err = add_uuid(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_REMOVE_UUID: - err = remove_uuid(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_DEV_CLASS: - err = set_dev_class(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_SERVICE_CACHE: - err = set_service_cache(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_LOAD_KEYS: - err = load_keys(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_REMOVE_KEY: - err = remove_key(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_DISCONNECT: - err = disconnect(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_GET_CONNECTIONS: - err = get_connections(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_PIN_CODE_REPLY: - err = pin_code_reply(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_PIN_CODE_NEG_REPLY: - err = pin_code_neg_reply(sk, buf + sizeof(*hdr), len); - break; - case MGMT_OP_SET_IO_CAPABILITY: - err = set_io_capability(sk, buf + sizeof(*hdr), len); - break; - default: - BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, opcode, 0x01); - break; - } - - if (err < 0) - goto done; - - err = msglen; - -done: - kfree(buf); - return err; -} - -int mgmt_index_added(u16 index) -{ - struct mgmt_ev_index_added ev; - - put_unaligned_le16(index, &ev.index); - - return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL); -} - -int mgmt_index_removed(u16 index) -{ - struct mgmt_ev_index_added ev; - - put_unaligned_le16(index, &ev.index); - - return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); -} - -struct cmd_lookup { - u8 val; - struct sock *sk; -}; - -static void mode_rsp(struct pending_cmd *cmd, void *data) -{ - struct mgmt_mode *cp = cmd->cmd; - struct cmd_lookup *match = data; - - if (cp->val != match->val) - return; - - send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - -int mgmt_powered(u16 index, u8 powered) -{ - struct mgmt_mode ev; - struct cmd_lookup match = { powered, NULL }; - int ret; - - mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); - - put_unaligned_le16(index, &ev.index); - ev.val = powered; - - ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk); - - if (match.sk) - sock_put(match.sk); - - return ret; -} - -int mgmt_discoverable(u16 index, u8 discoverable) -{ - struct mgmt_mode ev; - struct cmd_lookup match = { discoverable, NULL }; - int ret; - - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, - mode_rsp, &match); - - put_unaligned_le16(index, &ev.index); - ev.val = discoverable; - - ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk); - - if (match.sk) - sock_put(match.sk); - - return ret; -} - -int mgmt_connectable(u16 index, u8 connectable) -{ - struct mgmt_mode ev; - struct cmd_lookup match = { connectable, NULL }; - int ret; - - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); - - put_unaligned_le16(index, &ev.index); - ev.val = connectable; - - ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); - - if (match.sk) - sock_put(match.sk); - - return ret; -} - -int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) -{ - struct mgmt_ev_new_key ev; - - memset(&ev, 0, sizeof(ev)); - - put_unaligned_le16(index, &ev.index); - - bacpy(&ev.key.bdaddr, &key->bdaddr); - ev.key.type = key->type; - memcpy(ev.key.val, key->val, 16); - ev.key.pin_len = key->pin_len; - ev.old_key_type = old_key_type; - - return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); -} - -int mgmt_connected(u16 index, bdaddr_t *bdaddr) -{ - struct mgmt_ev_connected ev; - - put_unaligned_le16(index, &ev.index); - bacpy(&ev.bdaddr, bdaddr); - - return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL); -} - -static void disconnect_rsp(struct pending_cmd *cmd, void *data) -{ - struct mgmt_cp_disconnect *cp = cmd->cmd; - struct sock **sk = data; - struct mgmt_rp_disconnect rp; - - put_unaligned_le16(cmd->index, &rp.index); - bacpy(&rp.bdaddr, &cp->bdaddr); - - cmd_complete(cmd->sk, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); - - *sk = cmd->sk; - sock_hold(*sk); - - list_del(&cmd->list); - mgmt_pending_free(cmd); -} - -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) -{ - struct mgmt_ev_disconnected ev; - struct sock *sk = NULL; - int err; - - mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); - - put_unaligned_le16(index, &ev.index); - bacpy(&ev.bdaddr, bdaddr); - - err = mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), sk); - - if (sk) - sock_put(sk); - - return err; -} - -int mgmt_disconnect_failed(u16 index) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index); - if (!cmd) - return -ENOENT; - - err = cmd_status(cmd->sk, MGMT_OP_DISCONNECT, EIO); - - list_del(&cmd->list); - mgmt_pending_free(cmd); - - return err; -} - -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) -{ - struct mgmt_ev_connect_failed ev; - - put_unaligned_le16(index, &ev.index); - bacpy(&ev.bdaddr, bdaddr); - ev.status = status; - - return mgmt_event(MGMT_EV_CONNECT_FAILED, &ev, sizeof(ev), NULL); -} - -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) -{ - struct mgmt_ev_pin_code_request ev; - - put_unaligned_le16(index, &ev.index); - bacpy(&ev.bdaddr, bdaddr); - - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, &ev, sizeof(ev), NULL); -} - -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); - if (!cmd) - return -ENOENT; - - if (status != 0) - err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_REPLY, status); - else - err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_REPLY, - bdaddr, sizeof(*bdaddr)); - - list_del(&cmd->list); - mgmt_pending_free(cmd); - - return err; -} - -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); - if (!cmd) - return -ENOENT; - - if (status != 0) - err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, status); - else - err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, - bdaddr, sizeof(*bdaddr)); - - list_del(&cmd->list); - mgmt_pending_free(cmd); - - return err; + return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev)); }