diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-07-11 14:58:22 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-07-11 14:58:22 -0400 |
commit | 4b42c542afbc119c4012324ea80e0c5a89afea4f (patch) | |
tree | a67a788c7ba06cb40219b49505f84594dabc6f82 | |
parent | d8598981146241064993e371cea8333f59553cb6 (diff) | |
parent | e2fd318e3a9208245ee1041f6d413c8593fba29d (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
Conflicts:
net/bluetooth/l2cap_core.c
-rw-r--r-- | drivers/bluetooth/ath3k.c | 5 | ||||
-rw-r--r-- | drivers/bluetooth/btusb.c | 12 | ||||
-rw-r--r-- | include/net/bluetooth/bluetooth.h | 12 | ||||
-rw-r--r-- | include/net/bluetooth/hci.h | 18 | ||||
-rw-r--r-- | include/net/bluetooth/hci_core.h | 29 | ||||
-rw-r--r-- | include/net/bluetooth/l2cap.h | 25 | ||||
-rw-r--r-- | include/net/bluetooth/mgmt.h | 2 | ||||
-rw-r--r-- | include/net/bluetooth/smp.h | 1 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 101 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 87 | ||||
-rw-r--r-- | net/bluetooth/l2cap_core.c | 216 | ||||
-rw-r--r-- | net/bluetooth/l2cap_sock.c | 69 | ||||
-rw-r--r-- | net/bluetooth/lib.c | 23 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 148 | ||||
-rw-r--r-- | net/bluetooth/sco.c | 4 | ||||
-rw-r--r-- | net/bluetooth/smp.c | 208 |
16 files changed, 690 insertions, 270 deletions
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 6bacef368fa..a5854735bb2 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -375,6 +375,11 @@ static int ath3k_probe(struct usb_interface *intf, /* load patch and sysconfig files for AR3012 */ if (id->driver_info & BTUSB_ATH3012) { + + /* New firmware with patch and sysconfig files already loaded */ + if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001) + return -ENODEV; + ret = ath3k_load_patch(udev); if (ret < 0) { BT_ERR("Loading patch file failed"); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c2de8951e3f..91d13a9e8c6 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -54,6 +54,7 @@ static struct usb_driver btusb_driver; #define BTUSB_BCM92035 0x10 #define BTUSB_BROKEN_ISOC 0x20 #define BTUSB_WRONG_SCO_MTU 0x40 +#define BTUSB_ATH3012 0x80 static struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -110,7 +111,7 @@ static struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE }, /* Atheros 3012 with sflash firmware */ - { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE }, + { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, @@ -914,6 +915,15 @@ static int btusb_probe(struct usb_interface *intf, if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER) return -ENODEV; + if (id->driver_info & BTUSB_ATH3012) { + struct usb_device *udev = interface_to_usbdev(intf); + + /* Old firmware would otherwise let ath3k driver load + * patch and sysconfig files */ + if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001) + return -ENODEV; + } + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 7bccaf921ca..e727555d4ee 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -56,6 +56,7 @@ #define BT_SECURITY 4 struct bt_security { __u8 level; + __u8 key_size; }; #define BT_SECURITY_SDP 0 #define BT_SECURITY_LOW 1 @@ -76,9 +77,12 @@ struct bt_power { #define BT_POWER_FORCE_ACTIVE_OFF 0 #define BT_POWER_FORCE_ACTIVE_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) +__attribute__((format (printf, 2, 3))) +int bt_printk(const char *level, const char *fmt, ...); + +#define BT_INFO(fmt, arg...) bt_printk(KERN_INFO, pr_fmt(fmt), ##arg) +#define BT_ERR(fmt, arg...) bt_printk(KERN_ERR, pr_fmt(fmt), ##arg) +#define BT_DBG(fmt, arg...) pr_debug(fmt "\n", ##arg) /* Connection and socket states */ enum { @@ -204,7 +208,7 @@ out: return NULL; } -int bt_err(__u16 code); +int bt_to_errno(__u16 code); extern int hci_sock_init(void); extern void hci_sock_cleanup(void); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 65345cd215b..be30aabe7b8 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -211,11 +211,16 @@ enum { #define LMP_EDR_3S_ESCO 0x80 #define LMP_EXT_INQ 0x01 +#define LMP_SIMUL_LE_BR 0x02 #define LMP_SIMPLE_PAIR 0x08 #define LMP_NO_FLUSH 0x40 #define LMP_LSTO 0x01 #define LMP_INQ_TX_PWR 0x02 +#define LMP_EXTFEATURES 0x80 + +/* Extended LMP features */ +#define LMP_HOST_LE 0x02 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 @@ -254,6 +259,10 @@ enum { #define HCI_LK_UNAUTH_COMBINATION 0x04 #define HCI_LK_AUTH_COMBINATION 0x05 #define HCI_LK_CHANGED_COMBINATION 0x06 +/* The spec doesn't define types for SMP keys */ +#define HCI_LK_SMP_LTK 0x81 +#define HCI_LK_SMP_IRK 0x82 +#define HCI_LK_SMP_CSRK 0x83 /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 @@ -653,6 +662,12 @@ struct hci_rp_read_local_oob_data { #define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 +#define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d +struct hci_cp_write_le_host_supported { + __u8 le; + __u8 simul; +} __packed; + #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; @@ -676,6 +691,9 @@ struct hci_rp_read_local_features { } __packed; #define HCI_OP_READ_LOCAL_EXT_FEATURES 0x1004 +struct hci_cp_read_local_ext_features { + __u8 page; +} __packed; struct hci_rp_read_local_ext_features { __u8 status; __u8 page; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 26233d4d371..c41e275917c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -74,12 +74,28 @@ struct bt_uuid { u8 svc_hint; }; +struct key_master_id { + __le16 ediv; + u8 rand[8]; +} __packed; + +struct link_key_data { + bdaddr_t bdaddr; + u8 type; + u8 val[16]; + u8 pin_len; + u8 dlen; + u8 data[0]; +} __packed; + struct link_key { struct list_head list; bdaddr_t bdaddr; u8 type; u8 val[16]; u8 pin_len; + u8 dlen; + u8 data[0]; }; struct oob_data { @@ -113,6 +129,7 @@ struct hci_dev { __u8 major_class; __u8 minor_class; __u8 features[8]; + __u8 extfeatures[8]; __u8 commands[64]; __u8 ssp_mode; __u8 hci_ver; @@ -223,7 +240,6 @@ struct hci_conn { struct list_head list; atomic_t refcnt; - spinlock_t lock; bdaddr_t dst; __u8 dst_type; @@ -245,11 +261,11 @@ struct hci_conn { __u8 sec_level; __u8 pending_sec_level; __u8 pin_length; + __u8 enc_key_size; __u8 io_capability; __u8 power_save; __u16 disc_timeout; unsigned long pend; - __u8 ltk[16]; __u8 remote_cap; __u8 remote_oob; @@ -272,7 +288,6 @@ struct hci_conn { struct hci_dev *hdev; void *l2cap_data; void *sco_data; - void *priv; struct hci_conn *link; @@ -538,6 +553,11 @@ 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, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); +struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]); +struct link_key *hci_find_link_key_type(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type); +int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, + u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]); int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); int hci_remote_oob_data_clear(struct hci_dev *hdev); @@ -579,6 +599,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) #define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) +/* ----- Extended LMP capabilities ----- */ +#define lmp_host_le_capable(dev) ((dev)->extfeatures[0] & LMP_HOST_LE) + /* ----- HCI protocols ----- */ struct hci_proto { char *name; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9c18e555b6e..4f34ad25e75 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -37,7 +37,6 @@ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_ACK_TO 200 -#define L2CAP_LOCAL_BUSY_TRIES 12 #define L2CAP_LE_DEFAULT_MTU 23 #define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ @@ -130,6 +129,12 @@ struct l2cap_conninfo { #define L2CAP_SDU_END 0x8000 #define L2CAP_SDU_CONTINUE 0xC000 +/* L2CAP Command rej. reasons */ +#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 +#define L2CAP_REJ_MTU_EXCEEDED 0x0001 +#define L2CAP_REJ_INVALID_CID 0x0002 + + /* L2CAP structures */ struct l2cap_hdr { __le16 len; @@ -144,8 +149,19 @@ struct l2cap_cmd_hdr { } __packed; #define L2CAP_CMD_HDR_SIZE 4 -struct l2cap_cmd_rej { +struct l2cap_cmd_rej_unk { + __le16 reason; +} __packed; + +struct l2cap_cmd_rej_mtu { __le16 reason; + __le16 max_mtu; +} __packed; + +struct l2cap_cmd_rej_cid { + __le16 reason; + __le16 scid; + __le16 dcid; } __packed; struct l2cap_conn_req { @@ -352,8 +368,6 @@ struct l2cap_chan { struct sk_buff *tx_send_head; struct sk_buff_head tx_q; struct sk_buff_head srej_q; - struct sk_buff_head busy_q; - struct work_struct busy_work; struct list_head srej_l; struct list_head list; @@ -422,6 +436,7 @@ struct l2cap_conn { struct l2cap_pinfo { struct bt_sock bt; struct l2cap_chan *chan; + struct sk_buff *rx_busy_skb; }; enum { @@ -449,7 +464,6 @@ enum { CONN_REJ_ACT, CONN_SEND_FBIT, CONN_RNR_SENT, - CONN_SAR_RETRY, }; #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) @@ -498,5 +512,6 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); +void l2cap_chan_busy(struct l2cap_chan *chan, int busy); #endif /* __L2CAP_H */ diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 45bea25d737..5428fd32cce 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -101,6 +101,8 @@ struct mgmt_key_info { u8 type; u8 val[16]; u8 pin_len; + u8 dlen; + u8 data[0]; } __packed; #define MGMT_OP_LOAD_KEYS 0x000D diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 4fb7d198a87..46c45761230 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -118,5 +118,6 @@ struct smp_cmd_security_req { /* SMP Commands */ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); +int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); #endif /* __SMP_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0029e178e52..908fcd384ab 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -60,8 +60,6 @@ static void hci_tx_task(unsigned long arg); static DEFINE_RWLOCK(hci_task_lock); -static int enable_smp; - /* HCI device list */ LIST_HEAD(hci_dev_list); DEFINE_RWLOCK(hci_dev_list_lock); @@ -148,7 +146,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, switch (hdev->req_status) { case HCI_REQ_DONE: - err = -bt_err(hdev->req_result); + err = -bt_to_errno(hdev->req_result); break; case HCI_REQ_CANCELED: @@ -542,7 +540,7 @@ int hci_dev_open(__u16 dev) ret = __hci_request(hdev, hci_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); - if (lmp_le_capable(hdev)) + if (lmp_host_le_capable(hdev)) ret = __hci_request(hdev, hci_le_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); @@ -1059,6 +1057,42 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, return 0; } +struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) +{ + struct link_key *k; + + list_for_each_entry(k, &hdev->link_keys, list) { + struct key_master_id *id; + + if (k->type != HCI_LK_SMP_LTK) + continue; + + if (k->dlen != sizeof(*id)) + continue; + + id = (void *) &k->data; + if (id->ediv == ediv && + (memcmp(rand, id->rand, sizeof(id->rand)) == 0)) + return k; + } + + return NULL; +} +EXPORT_SYMBOL(hci_find_ltk); + +struct link_key *hci_find_link_key_type(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type) +{ + struct link_key *k; + + list_for_each_entry(k, &hdev->link_keys, list) + if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0) + return k; + + return NULL; +} +EXPORT_SYMBOL(hci_find_link_key_type); + int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) { @@ -1114,6 +1148,44 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, return 0; } +int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, + u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]) +{ + struct link_key *key, *old_key; + struct key_master_id *id; + u8 old_key_type; + + BT_DBG("%s addr %s", hdev->name, batostr(bdaddr)); + + old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK); + if (old_key) { + key = old_key; + old_key_type = old_key->type; + } else { + key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC); + if (!key) + return -ENOMEM; + list_add(&key->list, &hdev->link_keys); + old_key_type = 0xff; + } + + key->dlen = sizeof(*id); + + bacpy(&key->bdaddr, bdaddr); + memcpy(key->val, ltk, sizeof(key->val)); + key->type = HCI_LK_SMP_LTK; + key->pin_len = key_size; + + id = (void *) &key->data; + id->ediv = ediv; + memcpy(id->rand, rand, sizeof(id->rand)); + + if (new_key) + mgmt_new_key(hdev->id, key, old_key_type); + + return 0; +} + int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct link_key *key; @@ -1246,7 +1318,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (hci_blacklist_lookup(hdev, bdaddr)) { err = -EEXIST; @@ -1266,7 +1338,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) err = 0; err: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); return err; } @@ -1275,7 +1347,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) struct bdaddr_list *entry; int err = 0; - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (bacmp(bdaddr, BDADDR_ANY) == 0) { hci_blacklist_clear(hdev); @@ -1292,7 +1364,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) kfree(entry); done: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); return err; } @@ -1368,14 +1440,6 @@ int hci_add_adv_entry(struct hci_dev *hdev, return 0; } -static struct crypto_blkcipher *alloc_cypher(void) -{ - if (enable_smp) - return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - - return ERR_PTR(-ENOTSUPP); -} - /* Register HCI device */ int hci_register_dev(struct hci_dev *hdev) { @@ -1460,7 +1524,7 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; - hdev->tfm = alloc_cypher(); + hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(hdev->tfm)) BT_INFO("Failed to load transform for ecb(aes): %ld", PTR_ERR(hdev->tfm)); @@ -2352,6 +2416,3 @@ static void hci_cmd_task(unsigned long arg) } } } - -module_param(enable_smp, bool, 0644); -MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)"); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ac2c5e89617..a40170e022e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -45,6 +45,8 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +static int enable_le; + /* Handle HCI Event packets */ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) @@ -525,6 +527,20 @@ static void hci_setup_event_mask(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); } +static void hci_set_le_support(struct hci_dev *hdev) +{ + struct hci_cp_write_le_host_supported cp; + + memset(&cp, 0, sizeof(cp)); + + if (enable_le) { + cp.le = 1; + cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + } + + hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); +} + static void hci_setup(struct hci_dev *hdev) { hci_setup_event_mask(hdev); @@ -542,6 +558,17 @@ static void hci_setup(struct hci_dev *hdev) if (hdev->features[7] & LMP_INQ_TX_PWR) hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); + + if (hdev->features[7] & LMP_EXTFEATURES) { + struct hci_cp_read_local_ext_features cp; + + cp.page = 0x01; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, + sizeof(cp), &cp); + } + + if (hdev->features[4] & LMP_LE) + hci_set_le_support(hdev); } static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) @@ -658,6 +685,21 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb hdev->features[6], hdev->features[7]); } +static void hci_cc_read_local_ext_features(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_ext_features *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + memcpy(hdev->extfeatures, rp->features, 8); + + hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); +} + static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_buffer_size *rp = (void *) skb->data; @@ -892,6 +934,21 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } +static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_cp_read_local_ext_features cp; + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + if (status) + return; + + cp.page = 0x01; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp); +} + static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%x", hdev->name, status); @@ -1826,6 +1883,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_read_local_features(hdev, skb); break; + case HCI_OP_READ_LOCAL_EXT_FEATURES: + hci_cc_read_local_ext_features(hdev, skb); + break; + case HCI_OP_READ_BUFFER_SIZE: hci_cc_read_buffer_size(hdev, skb); break; @@ -1894,6 +1955,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_le_ltk_neg_reply(hdev, skb); break; + case HCI_OP_WRITE_LE_HOST_SUPPORTED: + hci_cc_write_le_host_supported(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%x", hdev->name, opcode); break; @@ -2793,21 +2858,36 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, { struct hci_ev_le_ltk_req *ev = (void *) skb->data; struct hci_cp_le_ltk_reply cp; + struct hci_cp_le_ltk_neg_reply neg; struct hci_conn *conn; + struct link_key *ltk; BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn == NULL) + goto not_found; - memset(&cp, 0, sizeof(cp)); + ltk = hci_find_ltk(hdev, ev->ediv, ev->random); + if (ltk == NULL) + goto not_found; + + memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk)); + conn->pin_length = ltk->pin_len; hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); hci_dev_unlock(hdev); + + return; + +not_found: + neg.handle = ev->handle; + hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg); + hci_dev_unlock(hdev); } static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -3022,3 +3102,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) hci_send_to_sock(hdev, skb, NULL); kfree_skb(skb); } + +module_param(enable_le, bool, 0444); +MODULE_PARM_DESC(enable_le, "Enable LE support"); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fc219ec2871..f7f8e2cd3f7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -61,13 +61,9 @@ int disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; -static struct workqueue_struct *_busy_wq; - static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); -static void l2cap_busy_work(struct work_struct *work); - static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, @@ -223,18 +219,18 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); + BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); - if (!mod_timer(timer, jiffies + timeout)) - chan_hold(chan); + if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout))) + chan_hold(chan); } static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer) { - BT_DBG("chan %p state %d", chan, chan->state); + BT_DBG("chan %p state %d", chan, chan->state); - if (timer_pending(timer) && del_timer(timer)) - chan_put(chan); + if (timer_pending(timer) && del_timer(timer)) + chan_put(chan); } static void l2cap_state_change(struct l2cap_chan *chan, int state) @@ -395,7 +391,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err) __clear_ack_timer(chan); skb_queue_purge(&chan->srej_q); - skb_queue_purge(&chan->busy_q); list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { list_del(&l->list); @@ -741,9 +736,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) &chan->conf_state)) { /* l2cap_chan_close() calls list_del(chan) * so release the lock */ - read_unlock_bh(&conn->chan_lock); + read_unlock(&conn->chan_lock); l2cap_chan_close(chan, ECONNRESET); - read_lock_bh(&conn->chan_lock); + read_lock(&conn->chan_lock); bh_unlock_sock(sk); continue; } @@ -1873,11 +1868,9 @@ static inline void l2cap_ertm_init(struct l2cap_chan *chan) setup_timer(&chan->ack_timer, l2cap_ack_timeout, (unsigned long) chan); skb_queue_head_init(&chan->srej_q); - skb_queue_head_init(&chan->busy_q); INIT_LIST_HEAD(&chan->srej_l); - INIT_WORK(&chan->busy_work, l2cap_busy_work); sk->sk_backlog_rcv = l2cap_ertm_data_rcv; } @@ -2284,9 +2277,9 @@ done: static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { - struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data; + struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data; - if (rej->reason != 0x0000) + if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD) return 0; if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && @@ -2532,9 +2525,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if ((bt_sk(sk)->defer_setup && chan->state != BT_CONNECT2) || (!bt_sk(sk)->defer_setup && chan->state != BT_CONFIG)) { - struct l2cap_cmd_rej rej; + struct l2cap_cmd_rej_cid rej; + + rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID); + rej.scid = cpu_to_le16(chan->scid); + rej.dcid = cpu_to_le16(chan->dcid); - rej.reason = cpu_to_le16(0x0002); l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); goto unlock; @@ -3025,12 +3021,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data); if (err) { - struct l2cap_cmd_rej rej; + struct l2cap_cmd_rej_unk rej; BT_ERR("Wrong link type (%d)", err); /* FIXME: Map err to a valid reason */ - rej.reason = cpu_to_le16(0); + rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); } @@ -3183,32 +3179,27 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk if (!chan->sdu) goto disconnect; - if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) { - chan->partial_sdu_len += skb->len; + chan->partial_sdu_len += skb->len; - if (chan->partial_sdu_len > chan->imtu) - goto drop; + if (chan->partial_sdu_len > chan->imtu) + goto drop; - if (chan->partial_sdu_len != chan->sdu_len) - goto drop; + if (chan->partial_sdu_len != chan->sdu_len) + goto drop; - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - } + memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); _skb = skb_clone(chan->sdu, GFP_ATOMIC); if (!_skb) { - set_bit(CONN_SAR_RETRY, &chan->conn_state); return -ENOMEM; } err = chan->ops->recv(chan->data, _skb); if (err < 0) { kfree_skb(_skb); - set_bit(CONN_SAR_RETRY, &chan->conn_state); return err; } - clear_bit(CONN_SAR_RETRY, &chan->conn_state); clear_bit(CONN_SAR_SDU, &chan->conn_state); kfree_skb(chan->sdu); @@ -3228,22 +3219,26 @@ disconnect: return 0; } -static int l2cap_try_push_rx_skb(struct l2cap_chan *chan) +static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) { - struct sk_buff *skb; u16 control; - int err; - while ((skb = skb_dequeue(&chan->busy_q))) { - control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(chan, skb, control); - if (err < 0) { - skb_queue_head(&chan->busy_q, skb); - return -EBUSY; - } + BT_DBG("chan %p, Enter local busy", chan); - chan->buffer_seq = (chan->buffer_seq + 1) % 64; - } + set_bit(CONN_LOCAL_BUSY, &chan->conn_state); + + control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= L2CAP_SUPER_RCV_NOT_READY; + l2cap_send_sframe(chan, control); + + set_bit(CONN_RNR_SENT, &chan->conn_state); + + __clear_ack_timer(chan); +} + +static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) +{ + u16 control; if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; @@ -3263,93 +3258,16 @@ done: clear_bit(CONN_RNR_SENT, &chan->conn_state); BT_DBG("chan %p, Exit local busy", chan); - - return 0; } -static void l2cap_busy_work(struct work_struct *work) +void l2cap_chan_busy(struct l2cap_chan *chan, int busy) { - DECLARE_WAITQUEUE(wait, current); - struct l2cap_chan *chan = - container_of(work, struct l2cap_chan, busy_work); - struct sock *sk = chan->sk; - int n_tries = 0, timeo = HZ/5, err; - struct sk_buff *skb; - - lock_sock(sk); - - add_wait_queue(sk_sleep(sk), &wait); - while ((skb = skb_peek(&chan->busy_q))) { - set_current_state(TASK_INTERRUPTIBLE); - - if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) { - err = -EBUSY; - l2cap_send_disconn_req(chan->conn, chan, EBUSY); - break; - } - - 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; - - if (l2cap_try_push_rx_skb(chan) == 0) - break; - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - release_sock(sk); -} - -static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) -{ - int sctrl, err; - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; - __skb_queue_tail(&chan->busy_q, skb); - return l2cap_try_push_rx_skb(chan); - - - } - - err = l2cap_ertm_reassembly_sdu(chan, skb, control); - if (err >= 0) { - chan->buffer_seq = (chan->buffer_seq + 1) % 64; - return err; + if (chan->mode == L2CAP_MODE_ERTM) { + if (busy) + l2cap_ertm_enter_local_busy(chan); + else + l2cap_ertm_exit_local_busy(chan); } - - /* Busy Condition */ - BT_DBG("chan %p, Enter local busy", chan); - - set_bit(CONN_LOCAL_BUSY, &chan->conn_state); - bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT; - __skb_queue_tail(&chan->busy_q, skb); - - sctrl = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - sctrl |= L2CAP_SUPER_RCV_NOT_READY; - l2cap_send_sframe(chan, sctrl); - - set_bit(CONN_RNR_SENT, &chan->conn_state); - - __clear_ack_timer(chan); - - queue_work(_busy_wq, &chan->busy_work); - - return err; } static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) @@ -3450,13 +3368,22 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) struct sk_buff *skb; u16 control; - while ((skb = skb_peek(&chan->srej_q))) { + while ((skb = skb_peek(&chan->srej_q)) && + !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { + int err; + |