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 /net/bluetooth | |
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
Diffstat (limited to 'net/bluetooth')
-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 |
8 files changed, 599 insertions, 257 deletions
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; + if (bt_cb(skb)->tx_seq != tx_seq) break; skb = skb_dequeue(&chan->srej_q); control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - l2cap_ertm_reassembly_sdu(chan, skb, control); + err = l2cap_ertm_reassembly_sdu(chan, skb, control); + + if (err < 0) { + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + break; + } + chan->buffer_seq_srej = (chan->buffer_seq_srej + 1) % 64; tx_seq = (tx_seq + 1) % 64; @@ -3523,9 +3450,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont chan->expected_ack_seq = req_seq; l2cap_drop_acked_frames(chan); - if (tx_seq == chan->expected_tx_seq) - goto expected; - tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; if (tx_seq_offset < 0) tx_seq_offset += 64; @@ -3539,6 +3463,9 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) goto drop; + if (tx_seq == chan->expected_tx_seq) + goto expected; + if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { struct srej_list *first; @@ -3590,7 +3517,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont chan->buffer_seq_srej = chan->buffer_seq; __skb_queue_head_init(&chan->srej_q); - __skb_queue_head_init(&chan->busy_q); l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); set_bit(CONN_SEND_PBIT, &chan->conn_state); @@ -3611,9 +3537,12 @@ expected: return 0; } - err = l2cap_push_rx_skb(chan, skb, rx_control); - if (err < 0) - return 0; + err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control); + chan->buffer_seq = (chan->buffer_seq + 1) % 64; + if (err < 0) { + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + return err; + } if (rx_control & L2CAP_CTRL_FINAL) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) @@ -4108,7 +4037,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) if (conn) l2cap_conn_ready(conn); } else - l2cap_conn_del(hcon, bt_err(status)); + l2cap_conn_del(hcon, bt_to_errno(status)); return 0; } @@ -4132,7 +4061,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK)) return -EINVAL; - l2cap_conn_del(hcon, bt_err(reason)); + l2cap_conn_del(hcon, bt_to_errno(reason)); return 0; } @@ -4178,6 +4107,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) chan->sec_level = hcon->sec_level; del_timer(&conn->security_timer); l2cap_chan_ready(sk); + smp_distribute_keys(conn, 0); } bh_unlock_sock(sk); @@ -4415,12 +4345,6 @@ int __init l2cap_init(void) if (err < 0) return err; - _busy_wq = create_singlethread_workqueue("l2cap"); - if (!_busy_wq) { - err = -ENOMEM; - goto error; - } - err = hci_register_proto(&l2cap_hci_proto); if (err < 0) { BT_ERR("L2CAP protocol registration failed"); @@ -4438,7 +4362,6 @@ int __init l2cap_init(void) return 0; error: - destroy_workqueue(_busy_wq); l2cap_cleanup_sockets(); return err; } @@ -4447,9 +4370,6 @@ void l2cap_exit(void) { debugfs_remove(l2cap_debugfs); - flush_workqueue(_busy_wq); - destroy_workqueue(_busy_wq); - if (hci_unregister_proto(&l2cap_hci_proto) < 0) BT_ERR("L2CAP protocol unregistration failed"); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 39082d4e77c..5c36b3e8739 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -422,8 +422,12 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; } + memset(&sec, 0, sizeof(sec)); sec.level = chan->sec_level; + if (sk->sk_state == BT_CONNECTED) + sec.key_size = chan->conn->hcon->enc_key_size; + len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) err = -EFAULT; @@ -711,13 +715,15 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) { struct sock *sk = sock->sk; + struct l2cap_pinfo *pi = l2cap_pi(sk); + int err; lock_sock(sk); if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { sk->sk_state = BT_CONFIG; - __l2cap_connect_rsp_defer(l2cap_pi(sk)->chan); + __l2cap_connect_rsp_defer(pi->chan); release_sock(sk); return 0; } @@ -725,9 +731,37 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms release_sock(sk); if (sock->type == SOCK_STREAM) - return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); + else + err = bt_sock_recvmsg(iocb, sock, msg, len, flags); + + if (pi->chan->mode != L2CAP_MODE_ERTM) + return err; + + /* Attempt to put pending rx data in the socket buffer */ + + lock_sock(sk); + + if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) + goto done; + + if (pi->rx_busy_skb) { + if (!sock_queue_rcv_skb(sk, pi->rx_busy_skb)) + pi->rx_busy_skb = NULL; + else + goto done; + } - return bt_sock_recvmsg(iocb, sock, msg, len, flags); + /* Restore data flow when half of the receive buffer is + * available. This avoids resending large numbers of + * frames. + */ + if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) + l2cap_chan_busy(pi->chan, 0); + +done: + release_sock(sk); + return err; } /* Kill socket (only if zapped and orphan) @@ -811,9 +845,31 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) { + int err; struct sock *sk = data; + struct l2cap_pinfo *pi = l2cap_pi(sk); - return sock_queue_rcv_skb(sk, skb); + if (pi->rx_busy_skb) + return -ENOMEM; + + err = sock_queue_rcv_skb(sk, skb); + + /* For ERTM, handle one skb that doesn't fit into the recv + * buffer. This is important to do because the data frames + * have already been acked, so the skb cannot be discarded. + * + * Notify the l2cap core that the buffer is full, so the + * LOCAL_BUSY state is entered and no more frames are + * acked and reassembled until there is buffer space + * available. + */ + if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) { + pi->rx_busy_skb = skb; + l2cap_chan_busy(pi->chan, 1); + err = 0; + } + + return err; } static void l2cap_sock_close_cb(void *data) @@ -842,6 +898,11 @@ static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); + if (l2cap_pi(sk)->rx_busy_skb) { + kfree_skb(l2cap_pi(sk)->rx_busy_skb); + l2cap_pi(sk)->rx_busy_skb = NULL; + } + skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); } diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index b826d1bf10d..86a6bed229d 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -59,7 +59,7 @@ char *batostr(bdaddr_t *ba) EXPORT_SYMBOL(batostr); /* Bluetooth error codes to Unix errno mapping */ -int bt_err(__u16 code) +int bt_to_errno(__u16 code) { switch (code) { case 0: @@ -149,4 +149,23 @@ int bt_err(__u16 code) return ENOSYS; } } -EXPORT_SYMBOL(bt_err); +EXPORT_SYMBOL(bt_to_errno); + +int bt_printk(const char *level, const char *format, ...) +{ + struct va_format vaf; + va_list args; + int r; + + va_start(args, format); + + vaf.fmt = format; + vaf.va = &args; + + r = printk("%sBluetooth: %pV\n", level, &vaf); + + va_end(args); + + return r; +} +EXPORT_SYMBOL(bt_printk); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 64c0418a622..53e109eb043 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -179,7 +179,7 @@ static int read_controller_info(struct sock *sk, u16 index) hci_del_off_timer(hdev); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); set_bit(HCI_MGMT, &hdev->flags); @@ -208,7 +208,7 @@ static int read_controller_info(struct sock *sk, u16 index) memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); @@ -316,7 +316,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); up = test_bit(HCI_UP, &hdev->flags); if ((cp->val && up) || (!cp->val && !up)) { @@ -343,7 +343,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) err = 0; failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; } @@ -368,7 +368,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); @@ -403,7 +403,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -429,7 +429,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN); @@ -463,7 +463,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, mgmt_pending_remove(cmd); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -522,7 +522,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (cp->val) set_bit(HCI_PAIRABLE, &hdev->flags); @@ -538,7 +538,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -739,7 +739,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); if (!uuid) { @@ -763,7 +763,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); failed: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -788,7 +788,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) if (!hdev) return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { err = hci_uuids_clear(hdev); @@ -823,7 +823,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); unlock: - hci_dev_unlock(hdev); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -847,7 +847,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV); - hci_dev_lock(hdev); + hci_dev_lock_bh(hdev); hdev->major_class = cp->major; hdev->minor_class = cp->minor; @@ -857,7 +857,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, |