From f97268fccdd4e76462195216fcab621b8d4a6cd1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:07 +0300 Subject: Bluetooth: A2MP: Create amp_mgr global list Create amp_mgr_list global list which will be used by different hci devices to find amp_mgr. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0760d1fed6f0..3f930608ecfa 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -17,6 +17,10 @@ #include #include +/* Global AMP Manager list */ +LIST_HEAD(amp_mgr_list); +DEFINE_MUTEX(amp_mgr_list_lock); + /* A2MP build & send command helper functions */ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) { @@ -516,6 +520,10 @@ static void amp_mgr_destroy(struct kref *kref) BT_DBG("mgr %p", mgr); + mutex_lock(&_mgr_list_lock); + list_del(&mgr->list); + mutex_unlock(&_mgr_list_lock); + kfree(mgr); } @@ -552,6 +560,10 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) kref_init(&mgr->kref); + mutex_lock(&_mgr_list_lock); + list_add(&mgr->list, &_mgr_list); + mutex_unlock(&_mgr_list_lock); + return mgr; } @@ -570,3 +582,20 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, return mgr->a2mp_chan; } + +struct amp_mgr *amp_mgr_lookup_by_state(u8 state) +{ + struct amp_mgr *mgr; + + mutex_lock(&_mgr_list_lock); + list_for_each_entry(mgr, &_mgr_list, list) { + if (mgr->state == state) { + amp_mgr_get(mgr); + mutex_unlock(&_mgr_list_lock); + return mgr; + } + } + mutex_unlock(&_mgr_list_lock); + + return NULL; +} -- cgit v1.2.3 From 8e2a0d92c56ec6955526a8b60838c9b00f70540d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:08 +0300 Subject: Bluetooth: AMP: Use HCI cmd to Read AMP Info When receiving A2MP Get Info Request execute Read Local AMP Info HCI command to AMP controller with function to be executed upon receiving command complete event. Function will handle A2MP Get Info Response. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ net/bluetooth/a2mp.c | 57 +++++++++++++++++++++++++++++++------------- net/bluetooth/hci_event.c | 6 ++++- 3 files changed, 48 insertions(+), 17 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 316c1c803676..af594685112e 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -132,5 +132,7 @@ int amp_mgr_put(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); struct amp_mgr *amp_mgr_lookup_by_state(u8 state); +void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); +void a2mp_send_getinfo_rsp(struct hci_dev *hdev); #endif /* __A2MP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 3f930608ecfa..0e97b3b42ab1 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -41,8 +41,7 @@ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) return cmd; } -static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, - void *data) +void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) { struct l2cap_chan *chan = mgr->a2mp_chan; struct a2mp_cmd *cmd; @@ -185,7 +184,6 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { struct a2mp_info_req *req = (void *) skb->data; - struct a2mp_info_rsp rsp; struct hci_dev *hdev; if (le16_to_cpu(hdr->len) < sizeof(*req)) @@ -193,23 +191,23 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("id %d", req->id); - rsp.id = req->id; - rsp.status = A2MP_STATUS_INVALID_CTRL_ID; - hdev = hci_dev_get(req->id); - if (hdev && hdev->amp_type != HCI_BREDR) { - rsp.status = 0; - rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); - rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); - rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); - rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); - rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); + if (!hdev) { + struct a2mp_info_rsp rsp; + + rsp.id = req->id; + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), + &rsp); } - if (hdev) - hci_dev_put(hdev); + if (hdev->dev_type != HCI_BREDR) { + mgr->state = READ_LOC_AMP_INFO; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + } - a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp); + hci_dev_put(hdev); skb_pull(skb, sizeof(*req)); return 0; @@ -599,3 +597,30 @@ struct amp_mgr *amp_mgr_lookup_by_state(u8 state) return NULL; } + +void a2mp_send_getinfo_rsp(struct hci_dev *hdev) +{ + struct amp_mgr *mgr; + struct a2mp_info_rsp rsp; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO); + if (!mgr) + return; + + BT_DBG("%s mgr %p", hdev->name, mgr); + + rsp.id = hdev->id; + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + if (hdev->amp_type != HCI_BREDR) { + rsp.status = 0; + rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); + rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); + rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); + rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); + rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); + } + + a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); + amp_mgr_put(mgr); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2022b43c7353..eb45774165f4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -30,6 +30,7 @@ #include #include #include +#include /* Handle HCI Event packets */ @@ -846,7 +847,7 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - return; + goto a2mp_rsp; hdev->amp_status = rp->amp_status; hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); @@ -860,6 +861,9 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); + +a2mp_rsp: + a2mp_send_getinfo_rsp(hdev); } static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, -- cgit v1.2.3 From 903e45411099ae8292f5ce637ad0c72f6fef61db Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:09 +0300 Subject: Bluetooth: AMP: Use HCI cmd to Read Loc AMP Assoc When receiving A2MP Get AMP Assoc Request execute Read Local AMP Assoc HCI command to AMP controller. If the AMP Assoc data is larger than it can fit to HCI event only fragment is read. When all fragments are read send A2MP Get AMP Assoc Response. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ include/net/bluetooth/amp.h | 21 +++++++++++++++ include/net/bluetooth/hci.h | 2 ++ include/net/bluetooth/hci_core.h | 8 ++++++ net/bluetooth/Makefile | 2 +- net/bluetooth/a2mp.c | 56 ++++++++++++++++++++++++++++++++++++---- net/bluetooth/amp.c | 45 ++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 41 +++++++++++++++++++++++++++++ 8 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 include/net/bluetooth/amp.h create mode 100644 net/bluetooth/amp.c (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index af594685112e..d5476719fa4c 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -21,6 +21,7 @@ enum amp_mgr_state { READ_LOC_AMP_INFO, + READ_LOC_AMP_ASSOC, }; struct amp_mgr { @@ -134,5 +135,6 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct amp_mgr *amp_mgr_lookup_by_state(u8 state); void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); +void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); #endif /* __A2MP_H */ diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h new file mode 100644 index 000000000000..e8616751bc81 --- /dev/null +++ b/include/net/bluetooth/amp.h @@ -0,0 +1,21 @@ +/* + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#ifndef __AMP_H +#define __AMP_H + +void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); +void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); +void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); + +#endif /* __AMP_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4be26abf6dad..ccc08e57c107 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -33,6 +33,8 @@ #define HCI_LINK_KEY_SIZE 16 #define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE) +#define HCI_MAX_AMP_ASSOC_SIZE 672 + /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e7d454609881..b1c7d0607244 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -124,6 +124,12 @@ struct le_scan_params { #define HCI_MAX_SHORT_NAME_LENGTH 10 +struct amp_assoc { + __u16 len; + __u16 offset; + __u8 data[HCI_MAX_AMP_ASSOC_SIZE]; +}; + #define NUM_REASSEMBLY 4 struct hci_dev { struct list_head list; @@ -177,6 +183,8 @@ struct hci_dev { __u32 amp_max_flush_to; __u32 amp_be_flush_to; + struct amp_assoc loc_assoc; + __u8 flow_ctl_mode; unsigned int auto_accept_delay; diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index fa6d94a4602a..dea6a287daca 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -10,4 +10,4 @@ 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 l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ - a2mp.o + a2mp.o amp.o diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0e97b3b42ab1..71400612843d 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -16,6 +16,7 @@ #include #include #include +#include /* Global AMP Manager list */ LIST_HEAD(amp_mgr_list); @@ -218,26 +219,37 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, { struct a2mp_amp_assoc_req *req = (void *) skb->data; struct hci_dev *hdev; + struct amp_mgr *tmp; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; BT_DBG("id %d", req->id); + /* Make sure that other request is not processed */ + tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); + hdev = hci_dev_get(req->id); - if (!hdev || hdev->amp_type == HCI_BREDR) { + if (!hdev || hdev->amp_type == HCI_BREDR || tmp) { struct a2mp_amp_assoc_rsp rsp; rsp.id = req->id; - rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + + if (tmp) { + rsp.status = A2MP_STATUS_COLLISION_OCCURED; + amp_mgr_put(tmp); + } else { + rsp.status = A2MP_STATUS_INVALID_CTRL_ID; + } a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), &rsp); - goto clean; + + goto done; } - /* Placeholder for HCI Read AMP Assoc */ + amp_read_loc_assoc(hdev, mgr); -clean: +done: if (hdev) hci_dev_put(hdev); @@ -624,3 +636,37 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev) a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); amp_mgr_put(mgr); } + +void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + struct a2mp_amp_assoc_rsp *rsp; + size_t len; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); + if (!mgr) + return; + + BT_DBG("%s mgr %p", hdev->name, mgr); + + len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len; + rsp = kzalloc(len, GFP_KERNEL); + if (!rsp) { + amp_mgr_put(mgr); + return; + } + + rsp->id = hdev->id; + + if (status) { + rsp->status = A2MP_STATUS_INVALID_CTRL_ID; + } else { + rsp->status = A2MP_STATUS_SUCCESS; + memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len); + } + + a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp); + amp_mgr_put(mgr); + kfree(rsp); +} diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c new file mode 100644 index 000000000000..2d4e79ee06a5 --- /dev/null +++ b/net/bluetooth/amp.c @@ -0,0 +1,45 @@ +/* + Copyright (c) 2011,2012 Intel Corp. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 and + only version 2 as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. +*/ + +#include +#include +#include +#include +#include + +void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) +{ + struct hci_cp_read_local_amp_assoc cp; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + + BT_DBG("%s handle %d", hdev->name, phy_handle); + + cp.phy_handle = phy_handle; + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + cp.len_so_far = cpu_to_le16(loc_assoc->offset); + + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} + +void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) +{ + struct hci_cp_read_local_amp_assoc cp; + + memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc)); + memset(&cp, 0, sizeof(cp)); + + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + + mgr->state = READ_LOC_AMP_ASSOC; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eb45774165f4..a4240f7a142e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -31,6 +31,7 @@ #include #include #include +#include /* Handle HCI Event packets */ @@ -866,6 +867,42 @@ a2mp_rsp: a2mp_send_getinfo_rsp(hdev); } +static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data; + struct amp_assoc *assoc = &hdev->loc_assoc; + size_t rem_len, frag_len; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + goto a2mp_rsp; + + frag_len = skb->len - sizeof(*rp); + rem_len = __le16_to_cpu(rp->rem_len); + + if (rem_len > frag_len) { + BT_DBG("frag_len %d rem_len %d", frag_len, rem_len); + + memcpy(assoc->data + assoc->offset, rp->frag, frag_len); + assoc->offset += frag_len; + + /* Read other fragments */ + amp_read_loc_assoc_frag(hdev, rp->phy_handle); + + return; + } + + memcpy(assoc->data + assoc->offset, rp->frag, rem_len); + assoc->len = assoc->offset + rem_len; + assoc->offset = 0; + +a2mp_rsp: + /* Send A2MP Rsp when all fragments are received */ + a2mp_send_getampassoc_rsp(hdev, rp->status); +} + static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2318,6 +2355,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_local_amp_info(hdev, skb); break; + case HCI_OP_READ_LOCAL_AMP_ASSOC: + hci_cc_read_local_amp_assoc(hdev, skb); + break; + case HCI_OP_DELETE_STORED_LINK_KEY: hci_cc_delete_stored_link_key(hdev, skb); break; -- cgit v1.2.3 From aa09537d80bf7e6282103618eb496f03e76f2953 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:10 +0300 Subject: Bluetooth: A2MP: Process Discover Response When receiving A2MP Discover Response send A2MP Get Info Request for each AMP controller in the discovery list. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 71400612843d..f04c44146663 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -67,6 +67,14 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) kfree(cmd); } +static u8 __next_ident(struct amp_mgr *mgr) +{ + if (++mgr->ident == 0) + mgr->ident = 1; + + return mgr->ident; +} + static inline void __a2mp_cl_bredr(struct a2mp_cl *cl) { cl->id = 0; @@ -165,6 +173,55 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, return 0; } +static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_discov_rsp *rsp = (void *) skb->data; + u16 len = le16_to_cpu(hdr->len); + struct a2mp_cl *cl; + u16 ext_feat; + + if (len < sizeof(*rsp)) + return -EINVAL; + + len -= sizeof(*rsp); + skb_pull(skb, sizeof(*rsp)); + + ext_feat = le16_to_cpu(rsp->ext_feat); + + BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(rsp->mtu), ext_feat); + + /* check that packet is not broken for now */ + while (ext_feat & A2MP_FEAT_EXT) { + if (len < sizeof(ext_feat)) + return -EINVAL; + + ext_feat = get_unaligned_le16(skb->data); + BT_DBG("efm 0x%4.4x", ext_feat); + len -= sizeof(ext_feat); + skb_pull(skb, sizeof(ext_feat)); + } + + cl = (void *) skb->data; + while (len >= sizeof(*cl)) { + BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type, + cl->status); + + if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) { + struct a2mp_info_req req; + + req.id = cl->id; + a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr), + sizeof(req), &req); + } + + len -= sizeof(*cl); + cl = (void *) skb_pull(skb, sizeof(*cl)); + } + + return 0; +} + static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { @@ -391,8 +448,11 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) err = a2mp_discphyslink_req(mgr, skb, hdr); break; - case A2MP_CHANGE_RSP: case A2MP_DISCOVER_RSP: + err = a2mp_discover_rsp(mgr, skb, hdr); + break; + + case A2MP_CHANGE_RSP: case A2MP_GETINFO_RSP: case A2MP_GETAMPASSOC_RSP: case A2MP_CREATEPHYSLINK_RSP: -- cgit v1.2.3 From 3161ae1c72f03b021bc67504c13025626c26d30c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:11 +0300 Subject: Bluetooth: AMP: Physical link struct and helpers Define physical link structures. Physical links are represented by hci_conn structure. For BR/EDR we use type ACL_LINK and for AMP we use AMP_LINK. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 3 +++ include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 1 + net/bluetooth/amp.c | 29 +++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index e8616751bc81..3414dfdc404c 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -14,6 +14,9 @@ #ifndef __AMP_H #define __AMP_H +struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, + u8 remote_id); + void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ccc08e57c107..77b6a197a6f6 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -207,6 +207,7 @@ enum { #define ESCO_LINK 0x02 /* Low Energy links do not have defined link type. Use invented one */ #define LE_LINK 0x80 +#define AMP_LINK 0x81 /* LMP features */ #define LMP_3SLOT 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b1c7d0607244..464eae340419 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -318,6 +318,7 @@ struct hci_conn { __u8 remote_cap; __u8 remote_auth; + __u8 remote_id; bool flush_key; unsigned int sent; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 2d4e79ee06a5..50a7b2fb8bf3 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -17,6 +17,35 @@ #include #include +/* Physical Link interface */ +static u8 __next_handle(struct amp_mgr *mgr) +{ + if (++mgr->handle == 0) + mgr->handle = 1; + + return mgr->handle; +} + +struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, + u8 remote_id) +{ + bdaddr_t *dst = mgr->l2cap_conn->dst; + struct hci_conn *hcon; + + hcon = hci_conn_add(hdev, AMP_LINK, dst); + if (!hcon) + return NULL; + + hcon->state = BT_CONNECT; + hcon->out = true; + hcon->attempt++; + hcon->handle = __next_handle(mgr); + hcon->remote_id = remote_id; + hcon->amp_mgr = mgr; + + return hcon; +} + void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) { struct hci_cp_read_local_amp_assoc cp; -- cgit v1.2.3 From 52c0d6e56b634b195e377192182391d526cdd5e4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:12 +0300 Subject: Bluetooth: AMP: Remote AMP ctrl definitions Create remote AMP controllers structure. It is used to keep information about discovered remote AMP controllers by A2MP protocol. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 3 ++ include/net/bluetooth/amp.h | 15 +++++++++ net/bluetooth/a2mp.c | 5 +++ net/bluetooth/amp.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index d5476719fa4c..99c7389b9189 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -33,6 +33,9 @@ struct amp_mgr { __u8 handle; enum amp_mgr_state state; unsigned long flags; + + struct list_head amp_ctrls; + struct mutex amp_ctrls_lock; }; struct a2mp_cmd { diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 3414dfdc404c..f57854f0786d 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -14,6 +14,21 @@ #ifndef __AMP_H #define __AMP_H +struct amp_ctrl { + struct list_head list; + struct kref kref; + __u8 id; + __u16 assoc_len_so_far; + __u16 assoc_rem_len; + __u16 assoc_len; + __u8 *assoc; +}; + +int amp_ctrl_put(struct amp_ctrl *ctrl); +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr); +struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); +void amp_ctrl_list_flush(struct amp_mgr *mgr); + struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, u8 remote_id); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index f04c44146663..35e188c7a441 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -594,6 +594,7 @@ static void amp_mgr_destroy(struct kref *kref) list_del(&mgr->list); mutex_unlock(&_mgr_list_lock); + amp_ctrl_list_flush(mgr); kfree(mgr); } @@ -630,6 +631,10 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) kref_init(&mgr->kref); + /* Remote AMP ctrl list initialization */ + INIT_LIST_HEAD(&mgr->amp_ctrls); + mutex_init(&mgr->amp_ctrls_lock); + mutex_lock(&_mgr_list_lock); list_add(&mgr->list, &_mgr_list); mutex_unlock(&_mgr_list_lock); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 50a7b2fb8bf3..8ef912c36224 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -17,6 +17,85 @@ #include #include +/* Remote AMP Controllers interface */ +static void amp_ctrl_get(struct amp_ctrl *ctrl) +{ + BT_DBG("ctrl %p orig refcnt %d", ctrl, + atomic_read(&ctrl->kref.refcount)); + + kref_get(&ctrl->kref); +} + +static void amp_ctrl_destroy(struct kref *kref) +{ + struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref); + + BT_DBG("ctrl %p", ctrl); + + kfree(ctrl->assoc); + kfree(ctrl); +} + +int amp_ctrl_put(struct amp_ctrl *ctrl) +{ + BT_DBG("ctrl %p orig refcnt %d", ctrl, + atomic_read(&ctrl->kref.refcount)); + + return kref_put(&ctrl->kref, &_ctrl_destroy); +} + +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr) +{ + struct amp_ctrl *ctrl; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return NULL; + + mutex_lock(&mgr->amp_ctrls_lock); + list_add(&ctrl->list, &mgr->amp_ctrls); + mutex_unlock(&mgr->amp_ctrls_lock); + + kref_init(&ctrl->kref); + + BT_DBG("mgr %p ctrl %p", mgr, ctrl); + + return ctrl; +} + +void amp_ctrl_list_flush(struct amp_mgr *mgr) +{ + struct amp_ctrl *ctrl, *n; + + BT_DBG("mgr %p", mgr); + + mutex_lock(&mgr->amp_ctrls_lock); + list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) { + list_del(&ctrl->list); + amp_ctrl_put(ctrl); + } + mutex_unlock(&mgr->amp_ctrls_lock); +} + +struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id) +{ + struct amp_ctrl *ctrl; + + BT_DBG("mgr %p id %d", mgr, id); + + mutex_lock(&mgr->amp_ctrls_lock); + list_for_each_entry(ctrl, &mgr->amp_ctrls, list) { + if (ctrl->id == id) { + amp_ctrl_get(ctrl); + mutex_unlock(&mgr->amp_ctrls_lock); + return ctrl; + } + } + mutex_unlock(&mgr->amp_ctrls_lock); + + return NULL; +} + /* Physical Link interface */ static u8 __next_handle(struct amp_mgr *mgr) { -- cgit v1.2.3 From cb8488c0b60a947c0ef4a1a94573a7fefd3f20b4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:13 +0300 Subject: Bluetooth: AMP: Handle create / disc phylink req Use hci_conn structure to keep track about AMP physical connections. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 35e188c7a441..0125417695eb 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -321,6 +321,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; + struct hci_conn *hcon; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; @@ -338,7 +339,14 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, /* TODO process physlink create */ - rsp.status = A2MP_STATUS_SUCCESS; + hcon = phylink_add(hdev, mgr, req->local_id); + if (hcon) { + BT_DBG("hcon %p", hcon); + + rsp.status = A2MP_STATUS_SUCCESS; + } else { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + } send_rsp: if (hdev) @@ -357,6 +365,7 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_physlink_req *req = (void *) skb->data; struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; + struct hci_conn *hcon; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; @@ -367,14 +376,22 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, rsp.remote_id = req->local_id; rsp.status = A2MP_STATUS_SUCCESS; - hdev = hci_dev_get(req->local_id); + hdev = hci_dev_get(req->remote_id); if (!hdev) { rsp.status = A2MP_STATUS_INVALID_CTRL_ID; goto send_rsp; } + hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst); + if (!hcon) { + BT_ERR("No phys link exist"); + rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS; + goto clean; + } + /* TODO Disconnect Phys Link here */ +clean: hci_dev_put(hdev); send_rsp: -- cgit v1.2.3 From 0d868de9d8760c76f6d4c6c777935c05ef272caa Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:14 +0300 Subject: Bluetooth: A2MP: Process A2MP Getinfo Rsp Process A2MP Getinfo Response, send Get AMP Assoc Req. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0125417695eb..594df9643365 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -271,6 +271,35 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, return 0; } +static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data; + struct a2mp_amp_assoc_req req; + struct amp_ctrl *ctrl; + + if (le16_to_cpu(hdr->len) < sizeof(*rsp)) + return -EINVAL; + + BT_DBG("id %d status 0x%2.2x", rsp->id, rsp->status); + + if (rsp->status) + return -EINVAL; + + ctrl = amp_ctrl_add(mgr); + if (!ctrl) + return -ENOMEM; + + ctrl->id = rsp->id; + + req.id = rsp->id; + a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req), + &req); + + skb_pull(skb, sizeof(*rsp)); + return 0; +} + static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { @@ -469,8 +498,11 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) err = a2mp_discover_rsp(mgr, skb, hdr); break; - case A2MP_CHANGE_RSP: case A2MP_GETINFO_RSP: + err = a2mp_getinfo_rsp(mgr, skb, hdr); + break; + + case A2MP_CHANGE_RSP: case A2MP_GETAMPASSOC_RSP: case A2MP_CREATEPHYSLINK_RSP: case A2MP_DISCONNPHYSLINK_RSP: -- cgit v1.2.3 From 9a5e94dbb4aa306742a47cbbcb0a44d4fc77a9e4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:15 +0300 Subject: Bluetooth: A2MP: Process A2MP Get AMP Assoc Rsp When receiving A2MP Get AMP Assoc Response save assoc data to remote AMP controller list and prepare for creating physical link. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 594df9643365..d0fde05e8b17 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -343,6 +343,61 @@ done: return 0; } +static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, + struct a2mp_cmd *hdr) +{ + struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data; + u16 len = le16_to_cpu(hdr->len); + struct hci_dev *hdev; + struct amp_ctrl *ctrl; + struct hci_conn *hcon; + + if (len < sizeof(*rsp)) + return -EINVAL; + + BT_DBG("id %d status 0x%2.2x assoc len %u", rsp->id, rsp->status, + len - sizeof(*rsp)); + + if (rsp->status) + return -EINVAL; + + /* Save remote ASSOC data */ + ctrl = amp_ctrl_lookup(mgr, rsp->id); + if (ctrl) { + u8 *assoc, assoc_len = len - sizeof(*rsp); + + assoc = kzalloc(assoc_len, GFP_KERNEL); + if (!assoc) { + amp_ctrl_put(ctrl); + return -ENOMEM; + } + + memcpy(assoc, rsp->amp_assoc, assoc_len); + ctrl->assoc = assoc; + ctrl->assoc_len = assoc_len; + ctrl->assoc_rem_len = assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + } + + /* Create Phys Link */ + hdev = hci_dev_get(rsp->id); + if (!hdev) + return -EINVAL; + + hcon = phylink_add(hdev, mgr, rsp->id); + if (!hcon) + goto done; + + BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); + +done: + hci_dev_put(hdev); + skb_pull(skb, len); + return 0; +} + static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_cmd *hdr) { @@ -502,8 +557,11 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) err = a2mp_getinfo_rsp(mgr, skb, hdr); break; - case A2MP_CHANGE_RSP: case A2MP_GETAMPASSOC_RSP: + err = a2mp_getampassoc_rsp(mgr, skb, hdr); + break; + + case A2MP_CHANGE_RSP: case A2MP_CREATEPHYSLINK_RSP: case A2MP_DISCONNPHYSLINK_RSP: err = a2mp_cmd_rsp(mgr, skb, hdr); -- cgit v1.2.3 From 93c3e8f5c9a0e4dc6b6c93108dcf3ec54ab1191a Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:16 +0300 Subject: Bluetooth: Choose connection based on capabilities Choose which L2CAP connection to establish by checking support for HS and remote side supported features. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 ++ include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 34 +++++++++++++++++++++++++++++----- net/bluetooth/l2cap_core.c | 33 ++++++++++++++++++++++++++++----- 4 files changed, 60 insertions(+), 10 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 99c7389b9189..9fda7c94913f 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -28,6 +28,7 @@ struct amp_mgr { struct list_head list; struct l2cap_conn *l2cap_conn; struct l2cap_chan *a2mp_chan; + struct l2cap_chan *bredr_chan; struct kref kref; __u8 ident; __u8 handle; @@ -137,6 +138,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); struct amp_mgr *amp_mgr_lookup_by_state(u8 state); void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); +void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7ed8e356425a..aba830fc762f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -767,6 +767,7 @@ int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); +void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index d0fde05e8b17..93adaad782ed 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -638,7 +638,7 @@ static struct l2cap_ops a2mp_chan_ops = { .ready = l2cap_chan_no_ready, }; -static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) { struct l2cap_chan *chan; int err; @@ -673,7 +673,10 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn) chan->conf_state = 0; - l2cap_chan_add(conn, chan); + if (locked) + __l2cap_chan_add(conn, chan); + else + l2cap_chan_add(conn, chan); chan->remote_mps = chan->omtu; chan->mps = chan->omtu; @@ -712,7 +715,7 @@ int amp_mgr_put(struct amp_mgr *mgr) return kref_put(&mgr->kref, &_mgr_destroy); } -static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) +static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked) { struct amp_mgr *mgr; struct l2cap_chan *chan; @@ -725,7 +728,7 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn) mgr->l2cap_conn = conn; - chan = a2mp_chan_open(conn); + chan = a2mp_chan_open(conn, locked); if (!chan) { kfree(mgr); return NULL; @@ -754,7 +757,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, { struct amp_mgr *mgr; - mgr = amp_mgr_create(conn); + mgr = amp_mgr_create(conn, false); if (!mgr) { BT_ERR("Could not create AMP manager"); return NULL; @@ -842,3 +845,24 @@ void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) amp_mgr_put(mgr); kfree(rsp); } + +void a2mp_discover_amp(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct amp_mgr *mgr = conn->hcon->amp_mgr; + struct a2mp_discov_req req; + + BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr); + + if (!mgr) { + mgr = amp_mgr_create(conn, true); + if (!mgr) + return; + } + + mgr->bredr_chan = chan; + + req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); + req.ext_feat = 0; + a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req); +} diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7a59e929febc..781a085ae5bc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -455,7 +455,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } -static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) +void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, __le16_to_cpu(chan->psm), chan->dcid); @@ -946,6 +946,18 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); } +static bool __amp_capable(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + + if (enable_hs && + chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && + conn->fixed_chan_mask & L2CAP_FC_A2MP) + return true; + else + return false; +} + static void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -972,6 +984,16 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) chan->ops->ready(chan); } +static void l2cap_start_connection(struct l2cap_chan *chan) +{ + if (__amp_capable(chan)) { + BT_DBG("chan %p AMP capable: discover AMPs", chan); + a2mp_discover_amp(chan); + } else { + l2cap_send_conn_req(chan); + } +} + static void l2cap_do_start(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -986,8 +1008,9 @@ static void l2cap_do_start(struct l2cap_chan *chan) return; if (l2cap_chan_check_security(chan) && - __l2cap_no_conn_pending(chan)) - l2cap_send_conn_req(chan); + __l2cap_no_conn_pending(chan)) { + l2cap_start_connection(chan); + } } else { struct l2cap_info_req req; req.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK); @@ -1082,7 +1105,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) continue; } - l2cap_send_conn_req(chan); + l2cap_start_connection(chan); } else if (chan->state == BT_CONNECT2) { struct l2cap_conn_rsp rsp; @@ -5456,7 +5479,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->state == BT_CONNECT) { if (!status) { - l2cap_send_conn_req(chan); + l2cap_start_connection(chan); } else { __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } -- cgit v1.2.3 From ba221bbabadd5fc2c80677b52178138fd694cc26 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 27 Sep 2012 17:26:17 +0300 Subject: Bluetooth: Add function to derive AMP key using hmac hmac(sha256) will be used for AMP key generation. Signed-off-by: Dmitry Kasatkin Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/amp.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 8ef912c36224..ea4d5ffc1151 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -16,6 +16,7 @@ #include #include #include +#include /* Remote AMP Controllers interface */ static void amp_ctrl_get(struct amp_ctrl *ctrl) @@ -125,6 +126,41 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, return hcon; } +/* AMP crypto key generation interface */ +static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) +{ + int ret = 0; + struct crypto_shash *tfm; + + if (!ksize) + return -EINVAL; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) { + BT_DBG("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + ret = crypto_shash_setkey(tfm, key, ksize); + if (ret) { + BT_DBG("crypto_ahash_setkey failed: err %d", ret); + } else { + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(tfm)]; + } desc; + + desc.shash.tfm = tfm; + desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_digest(&desc.shash, plaintext, psize, + output); + } + + crypto_free_shash(tfm); + return ret; +} + void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) { struct hci_cp_read_local_amp_assoc cp; -- cgit v1.2.3 From 5a349186692950b13896abc3fb2f491d023f95a1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:18 +0300 Subject: Bluetooth: AMP: Add AMP key calculation Function calculates AMP keys using hmac_sha256 helper. Calculated keys are Generic AMP Link Key (gamp) and Dedicated AMP Link Key with keyID "802b" for 802.11 PAL. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 ++ net/bluetooth/Kconfig | 1 + net/bluetooth/amp.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index f57854f0786d..763b4635c304 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -32,6 +32,8 @@ void amp_ctrl_list_flush(struct amp_mgr *mgr); struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, u8 remote_id); +int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); + void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 3537d385035e..1c11d0dcd863 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -11,6 +11,7 @@ menuconfig BT select CRYPTO_BLKCIPHER select CRYPTO_AES select CRYPTO_ECB + select CRYPTO_SHA256 help Bluetooth is low-cost, low-power, short-range wireless technology. It was designed as a replacement for cables and other short-range diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index ea4d5ffc1151..67bc2c2f8783 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -161,6 +161,51 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) return ret; } +int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type) +{ + struct hci_dev *hdev = conn->hdev; + struct link_key *key; + u8 keybuf[HCI_AMP_LINK_KEY_SIZE]; + u8 gamp_key[HCI_AMP_LINK_KEY_SIZE]; + int err; + + if (!hci_conn_check_link_mode(conn)) + return -EACCES; + + BT_DBG("conn %p key_type %d", conn, conn->key_type); + + /* Legacy key */ + if (conn->key_type < 3) { + BT_ERR("Legacy key type %d", conn->key_type); + return -EACCES; + } + + *type = conn->key_type; + *len = HCI_AMP_LINK_KEY_SIZE; + + key = hci_find_link_key(hdev, &conn->dst); + + /* BR/EDR Link Key concatenated together with itself */ + memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE); + memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE); + + /* Derive Generic AMP Link Key (gamp) */ + err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key); + if (err) { + BT_ERR("Could not derive Generic AMP Key: err %d", err); + return err; + } + + if (conn->key_type == HCI_LK_DEBUG_COMBINATION) { + BT_DBG("Use Generic AMP Key (gamp)"); + memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE); + return err; + } + + /* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */ + return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data); +} + void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) { struct hci_cp_read_local_amp_assoc cp; -- cgit v1.2.3 From a02226d6ff5098e6b97590cc55aabe7faf0860ed Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:19 +0300 Subject: Bluetooth: AMP: Create Physical Link When receiving A2MP Get AMP Assoc Response execute HCI Create Physical Link to AMP controller. Define function which will run when receiving HCI Command Status. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 ++ net/bluetooth/a2mp.c | 2 ++ net/bluetooth/amp.c | 19 +++++++++++++++++++ net/bluetooth/hci_event.c | 9 +++++++++ 4 files changed, 32 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 763b4635c304..cadb3d032856 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -37,5 +37,7 @@ int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); +void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon); #endif /* __AMP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 93adaad782ed..773e8fc41670 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -392,6 +392,8 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); + amp_create_phylink(hdev, mgr, hcon); + done: hci_dev_put(hdev); skb_pull(skb, len); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 67bc2c2f8783..657ec736bbc6 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -232,3 +232,22 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) mgr->state = READ_LOC_AMP_ASSOC; hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } + +void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon) +{ + struct hci_cp_create_phy_link cp; + + cp.phy_handle = hcon->handle; + + BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon, + hcon->handle); + + if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len, + &cp.key_type)) { + BT_DBG("Cannot create link key"); + return; + } + + hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a4240f7a142e..bd06ef95693d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1698,6 +1698,11 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, status); } +static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status 0x%2.2x", hdev->name, status); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2512,6 +2517,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_le_start_enc(hdev, ev->status); break; + case HCI_OP_CREATE_PHY_LINK: + hci_cs_create_phylink(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From 93c284ee901f7d7bdd09087e92abefb7496c3777 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:20 +0300 Subject: Bluetooth: AMP: Write remote AMP Assoc When receiving HCI Command Status after HCI Create Physical Link execute HCI Write Remote AMP Assoc command to AMP controller. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 + include/net/bluetooth/hci_core.h | 2 + net/bluetooth/amp.c | 80 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 29 +++++++++++++++ 4 files changed, 113 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index cadb3d032856..8f8032965eaf 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -39,5 +39,7 @@ void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); +void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); +void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); #endif /* __AMP_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 464eae340419..ea1f9340324d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -127,6 +127,8 @@ struct le_scan_params { struct amp_assoc { __u16 len; __u16 offset; + __u16 rem_len; + __u16 len_so_far; __u8 data[HCI_MAX_AMP_ASSOC_SIZE]; }; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 657ec736bbc6..5895ad0d2ac9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -233,6 +233,86 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } + +/* Write AMP Assoc data fragments, returns true with last fragment written*/ +static bool amp_write_rem_assoc_frag(struct hci_dev *hdev, + struct hci_conn *hcon) +{ + struct hci_cp_write_remote_amp_assoc *cp; + struct amp_mgr *mgr = hcon->amp_mgr; + struct amp_ctrl *ctrl; + u16 frag_len, len; + + ctrl = amp_ctrl_lookup(mgr, hcon->remote_id); + if (!ctrl) + return false; + + if (!ctrl->assoc_rem_len) { + BT_DBG("all fragments are written"); + ctrl->assoc_rem_len = ctrl->assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + return true; + } + + frag_len = min_t(u16, 248, ctrl->assoc_rem_len); + len = frag_len + sizeof(*cp); + + cp = kzalloc(len, GFP_KERNEL); + if (!cp) { + amp_ctrl_put(ctrl); + return false; + } + + BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u", + hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len); + + cp->phy_handle = hcon->handle; + cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far); + cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len); + memcpy(cp->frag, ctrl->assoc, frag_len); + + ctrl->assoc_len_so_far += frag_len; + ctrl->assoc_rem_len -= frag_len; + + amp_ctrl_put(ctrl); + + hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp); + + kfree(cp); + + return false; +} + +void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle) +{ + struct hci_conn *hcon; + + BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon) + return; + + amp_write_rem_assoc_frag(hdev, hcon); +} + +void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle) +{ + struct hci_conn *hcon; + + BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon) + return; + + BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon); + + amp_write_rem_assoc_frag(hdev, hcon); +} + void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bd06ef95693d..0b7ba1e39d45 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1215,6 +1215,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); } +static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_write_remote_amp_assoc *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x", + hdev->name, rp->status, rp->phy_handle); + + if (rp->status) + return; + + amp_write_rem_assoc_continue(hdev, rp->phy_handle); +} + static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) { BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -1700,7 +1714,18 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) { + struct hci_cp_create_phy_link *cp; + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK); + if (!cp) + return; + + amp_write_remote_assoc(hdev, cp->phy_handle); } static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -2436,6 +2461,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_le_host_supported(hdev, skb); break; + case HCI_OP_WRITE_REMOTE_AMP_ASSOC: + hci_cc_write_remote_amp_assoc(hdev, skb); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From 2766be48a7181d7f2a84831ca7e7be248fb6fdb5 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:21 +0300 Subject: Bluetooth: A2MP: Add fallback to normal l2cap init sequence When there is no remote AMP controller found fallback to normal L2CAP sequence. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 28 ++++++++++++++++++++++++++++ net/bluetooth/l2cap_core.c | 2 +- 3 files changed, 30 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index aba830fc762f..0967f9e33750 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -769,5 +769,6 @@ int l2cap_ertm_init(struct l2cap_chan *chan); void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); +void l2cap_send_conn_req(struct l2cap_chan *chan); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 773e8fc41670..28d1246958be 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -180,6 +180,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, u16 len = le16_to_cpu(hdr->len); struct a2mp_cl *cl; u16 ext_feat; + bool found = false; if (len < sizeof(*rsp)) return -EINVAL; @@ -210,6 +211,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) { struct a2mp_info_req req; + found = true; req.id = cl->id; a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr), sizeof(req), &req); @@ -219,6 +221,32 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, cl = (void *) skb_pull(skb, sizeof(*cl)); } + /* Fall back to L2CAP init sequence */ + if (!found) { + struct l2cap_conn *conn = mgr->l2cap_conn; + struct l2cap_chan *chan; + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + + BT_DBG("chan %p state %s", chan, + state_to_string(chan->state)); + + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) + continue; + + l2cap_chan_lock(chan); + + if (chan->state == BT_CONNECT) + l2cap_send_conn_req(chan); + + l2cap_chan_unlock(chan); + } + + mutex_unlock(&conn->chan_lock); + } + return 0; } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 781a085ae5bc..a347522b2144 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -958,7 +958,7 @@ static bool __amp_capable(struct l2cap_chan *chan) return false; } -static void l2cap_send_conn_req(struct l2cap_chan *chan) +void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; struct l2cap_conn_req req; -- cgit v1.2.3 From 9495b2ee757f7747d7c28f9ba8d7edc53005ec2d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:22 +0300 Subject: Bluetooth: AMP: Process Chan Selected event Channel Selected event indicates that link information data is available. Read it with Read Local AMP Assoc command. The data shall be sent in the A2MP Create Physical Link Request. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 3 +++ include/net/bluetooth/amp.h | 2 ++ include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/a2mp.c | 41 ++++++++++++++++++++++++++++++++++++++++- net/bluetooth/amp.c | 15 +++++++++++++++ net/bluetooth/hci_event.c | 21 +++++++++++++++++++++ 6 files changed, 83 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index 9fda7c94913f..e776ab2dc572 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -22,6 +22,7 @@ enum amp_mgr_state { READ_LOC_AMP_INFO, READ_LOC_AMP_ASSOC, + READ_LOC_AMP_ASSOC_FINAL, }; struct amp_mgr { @@ -134,6 +135,7 @@ extern struct mutex amp_mgr_list_lock; void amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); +u8 __next_ident(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, struct sk_buff *skb); struct amp_mgr *amp_mgr_lookup_by_state(u8 state); @@ -141,5 +143,6 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); void a2mp_discover_amp(struct l2cap_chan *chan); void a2mp_send_getinfo_rsp(struct hci_dev *hdev); void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); +void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status); #endif /* __A2MP_H */ diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 8f8032965eaf..70496c07afaa 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -37,6 +37,8 @@ int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); +void amp_read_loc_assoc_final_data(struct hci_dev *hdev, + struct hci_conn *hcon); void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0967f9e33750..ab58b81fb7c5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -508,6 +508,8 @@ struct l2cap_chan { __u32 remote_acc_lat; __u32 remote_flush_to; + __u8 ctrl_id; + struct delayed_work chan_timer; struct delayed_work retrans_timer; struct delayed_work monitor_timer; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 28d1246958be..375a67f501d0 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -67,7 +67,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) kfree(cmd); } -static u8 __next_ident(struct amp_mgr *mgr) +u8 __next_ident(struct amp_mgr *mgr) { if (++mgr->ident == 0) mgr->ident = 1; @@ -420,6 +420,8 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); + mgr->bredr_chan->ctrl_id = rsp->id; + amp_create_phylink(hdev, mgr, hcon); done: @@ -876,6 +878,43 @@ void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) kfree(rsp); } +void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) +{ + struct amp_mgr *mgr; + struct amp_assoc *loc_assoc = &hdev->loc_assoc; + struct a2mp_physlink_req *req; + struct l2cap_chan *bredr_chan; + size_t len; + + mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL); + if (!mgr) + return; + + len = sizeof(*req) + loc_assoc->len; + + BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len); + + req = kzalloc(len, GFP_KERNEL); + if (!req) { + amp_mgr_put(mgr); + return; + } + + bredr_chan = mgr->bredr_chan; + if (!bredr_chan) + goto clean; + + req->local_id = hdev->id; + req->remote_id = bredr_chan->ctrl_id; + memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); + + a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); + +clean: + amp_mgr_put(mgr); + kfree(req); +} + void a2mp_discover_amp(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 5895ad0d2ac9..4f7b2647d5e9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -233,6 +233,21 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); } +void amp_read_loc_assoc_final_data(struct hci_dev *hdev, + struct hci_conn *hcon) +{ + struct hci_cp_read_local_amp_assoc cp; + struct amp_mgr *mgr = hcon->amp_mgr; + + cp.phy_handle = hcon->handle; + cp.len_so_far = cpu_to_le16(0); + cp.max_len = cpu_to_le16(hdev->amp_assoc_size); + + mgr->state = READ_LOC_AMP_ASSOC_FINAL; + + /* Read Local AMP Assoc final link information data */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); +} /* Write AMP Assoc data fragments, returns true with last fragment written*/ static bool amp_write_rem_assoc_frag(struct hci_dev *hdev, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0b7ba1e39d45..d702ba1c171c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -901,6 +901,7 @@ static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, a2mp_rsp: /* Send A2MP Rsp when all fragments are received */ a2mp_send_getampassoc_rsp(hdev, rp->status); + a2mp_send_create_phy_link_req(hdev, rp->status); } static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, @@ -3641,6 +3642,22 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_channel_selected *ev = (void *) skb->data; + struct hci_conn *hcon; + + BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle); + + skb_pull(skb, sizeof(*ev)); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) + return; + + amp_read_loc_assoc_final_data(hdev, hcon); +} + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (void *) skb->data; @@ -3805,6 +3822,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_le_meta_evt(hdev, skb); break; + case HCI_EV_CHANNEL_SELECTED: + hci_chan_selected_evt(hdev, skb); + break; + case HCI_EV_REMOTE_OOB_DATA_REQUEST: hci_remote_oob_data_request_evt(hdev, skb); break; -- cgit v1.2.3 From dffa387110025801862d7ad09f4e850d06ff55a9 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:23 +0300 Subject: Bluetooth: AMP: Accept Physical Link When receiving A2MP Create Physical Link message execute HCI Accept Physical Link command to AMP controller. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 ++ net/bluetooth/a2mp.c | 5 +---- net/bluetooth/amp.c | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 70496c07afaa..1b06d7b01359 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -41,6 +41,8 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev, struct hci_conn *hcon); void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); +void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 375a67f501d0..dbfdbbb9707c 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -453,12 +453,9 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, goto send_rsp; } - /* TODO process physlink create */ - hcon = phylink_add(hdev, mgr, req->local_id); if (hcon) { - BT_DBG("hcon %p", hcon); - + amp_accept_phylink(hdev, mgr, hcon); rsp.status = A2MP_STATUS_SUCCESS; } else { rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 4f7b2647d5e9..845e43073c40 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -346,3 +346,22 @@ void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp); } + +void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, + struct hci_conn *hcon) +{ + struct hci_cp_accept_phy_link cp; + + cp.phy_handle = hcon->handle; + + BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon, + hcon->handle); + + if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len, + &cp.key_type)) { + BT_DBG("Cannot create link key"); + return; + } + + hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); +} -- cgit v1.2.3 From 0b26ab9dce74f8ac77d7eef0d683ab1d527e45b1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 27 Sep 2012 17:26:24 +0300 Subject: Bluetooth: AMP: Handle Accept phylink command status evt When receiving HCI Command Status event for Accept Physical Link execute HCI Write Remote AMP Assoc with data saved from A2MP Create Physical Link Request. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 1 + net/bluetooth/a2mp.c | 32 ++++++++++++++++++++++++++++++++ net/bluetooth/amp.c | 2 +- net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 1b06d7b01359..b1e54903dd42 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -25,6 +25,7 @@ struct amp_ctrl { }; int amp_ctrl_put(struct amp_ctrl *ctrl); +void amp_ctrl_get(struct amp_ctrl *ctrl); struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr); struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); void amp_ctrl_list_flush(struct amp_mgr *mgr); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index dbfdbbb9707c..47565d28b27f 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -438,6 +438,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_physlink_rsp rsp; struct hci_dev *hdev; struct hci_conn *hcon; + struct amp_ctrl *ctrl; if (le16_to_cpu(hdr->len) < sizeof(*req)) return -EINVAL; @@ -453,6 +454,37 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, goto send_rsp; } + ctrl = amp_ctrl_lookup(mgr, rsp.remote_id); + if (!ctrl) { + ctrl = amp_ctrl_add(mgr); + if (ctrl) { + amp_ctrl_get(ctrl); + } else { + rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; + goto send_rsp; + } + } + + if (ctrl) { + u8 *assoc, assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); + + ctrl->id = rsp.remote_id; + + assoc = kzalloc(assoc_len, GFP_KERNEL); + if (!assoc) { + amp_ctrl_put(ctrl); + return -ENOMEM; + } + + memcpy(assoc, req->amp_assoc, assoc_len); + ctrl->assoc = assoc; + ctrl->assoc_len = assoc_len; + ctrl->assoc_rem_len = assoc_len; + ctrl->assoc_len_so_far = 0; + + amp_ctrl_put(ctrl); + } + hcon = phylink_add(hdev, mgr, req->local_id); if (hcon) { amp_accept_phylink(hdev, mgr, hcon); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 845e43073c40..5dab2d1c7c82 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -19,7 +19,7 @@ #include /* Remote AMP Controllers interface */ -static void amp_ctrl_get(struct amp_ctrl *ctrl) +void amp_ctrl_get(struct amp_ctrl *ctrl) { BT_DBG("ctrl %p orig refcnt %d", ctrl, atomic_read(&ctrl->kref.refcount)); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d702ba1c171c..7e716698fe64 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1729,6 +1729,22 @@ static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) amp_write_remote_assoc(hdev, cp->phy_handle); } +static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_accept_phy_link *cp; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK); + if (!cp) + return; + + amp_write_remote_assoc(hdev, cp->phy_handle); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2551,6 +2567,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_create_phylink(hdev, ev->status); break; + case HCI_OP_ACCEPT_PHY_LINK: + hci_cs_accept_phylink(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; -- cgit v1.2.3 From 392f44d3e7894f6fe314b85b4a1611b7b1d98226 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Thu, 27 Sep 2012 17:42:07 -0300 Subject: Bluetooth: Fix two warnings in BT_DBG We just need to use the right modifiers Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 2 +- net/bluetooth/hci_event.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 47565d28b27f..61e657ae71e9 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -383,7 +383,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (len < sizeof(*rsp)) return -EINVAL; - BT_DBG("id %d status 0x%2.2x assoc len %u", rsp->id, rsp->status, + BT_DBG("id %d status 0x%2.2x assoc len %lu", rsp->id, rsp->status, len - sizeof(*rsp)); if (rsp->status) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7e716698fe64..2551c4b9d65e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -883,7 +883,7 @@ static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, rem_len = __le16_to_cpu(rp->rem_len); if (rem_len > frag_len) { - BT_DBG("frag_len %d rem_len %d", frag_len, rem_len); + BT_DBG("frag_len %ld rem_len %ld", frag_len, rem_len); memcpy(assoc->data + assoc->offset, rp->frag, frag_len); assoc->offset += frag_len; -- cgit v1.2.3 From 36a75f1b3ecf94dcf140b9d3d2f7a03b7e258480 Mon Sep 17 00:00:00 2001 From: Jefferson Delfes Date: Tue, 18 Sep 2012 13:36:54 -0400 Subject: Bluetooth: Force the process of unpair command if disconnect failed The unpair process tries to disconnect any connection pending with remote. If there are some connection in connecting state, disconnect command will fail and unpair mgmt command will stay pending. That pending mgmt command can cause strange behavior like automatic unpair after a lost connection. Signed-off-by: Jefferson Delfes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8934343be0ea..b127b88015c9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3109,6 +3109,9 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, struct pending_cmd *cmd; int err; + mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, + hdev); + cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return -ENOENT; @@ -3121,8 +3124,6 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, mgmt_pending_remove(cmd); - mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); return err; } -- cgit v1.2.3 From 6ed93dc6427d14cdfe0b272cc0a9ee4685ce9ad7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 25 Sep 2012 12:49:43 +0300 Subject: Bluetooth: Use %pMR in debug instead of batostr Instead of old unsafe batostr function use %pMR print specifier for printing Bluetooth addresses in debug and error statements. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/bnep/core.c | 3 +-- net/bluetooth/hci_conn.c | 6 +++--- net/bluetooth/hci_core.c | 22 +++++++++++----------- net/bluetooth/hci_event.c | 15 +++++++-------- net/bluetooth/l2cap_core.c | 6 +++--- net/bluetooth/rfcomm/core.c | 6 +++--- net/bluetooth/rfcomm/sock.c | 2 +- net/bluetooth/rfcomm/tty.c | 4 ++-- net/bluetooth/sco.c | 8 ++++---- 9 files changed, 35 insertions(+), 37 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 4a6620bc1570..a5b639702637 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -182,8 +182,7 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) a2 = data; data += ETH_ALEN; - BT_DBG("mc filter %s -> %s", - batostr((void *) a1), batostr((void *) a2)); + BT_DBG("mc filter %pMR -> %pMR", a1, a2); /* Iterate from a1 to a2 */ set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b9196a44f759..53202f6733ae 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -320,7 +320,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) { struct hci_conn *conn; - BT_DBG("%s dst %s", hdev->name, batostr(dst)); + BT_DBG("%s dst %pMR", hdev->name, dst); conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL); if (!conn) @@ -437,7 +437,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) int use_src = bacmp(src, BDADDR_ANY); struct hci_dev *hdev = NULL, *d; - BT_DBG("%s -> %s", batostr(src), batostr(dst)); + BT_DBG("%pMR -> %pMR", src, dst); read_lock(&hci_dev_list_lock); @@ -567,7 +567,7 @@ static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type) { - BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type); + BT_DBG("%s dst %pMR type 0x%x", hdev->name, dst, type); switch (type) { case LE_LINK: diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e4070517ff3b..ab4fca2448d5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -405,7 +405,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); + BT_DBG("cache %p, %pMR", cache, bdaddr); list_for_each_entry(e, &cache->all, all) { if (!bacmp(&e->data.bdaddr, bdaddr)) @@ -421,7 +421,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); + BT_DBG("cache %p, %pMR", cache, bdaddr); list_for_each_entry(e, &cache->unknown, list) { if (!bacmp(&e->data.bdaddr, bdaddr)) @@ -438,7 +438,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *e; - BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state); + BT_DBG("cache %p bdaddr %pMR state %d", cache, bdaddr, state); list_for_each_entry(e, &cache->resolve, list) { if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state) @@ -475,7 +475,7 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; - BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); + BT_DBG("cache %p, %pMR", cache, &data->bdaddr); if (ssp) *ssp = data->ssp_mode; @@ -1257,7 +1257,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, list_add(&key->list, &hdev->link_keys); } - BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); + BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type); /* Some buggy controller combinations generate a changed * combination key for legacy pairing even when there's no @@ -1336,7 +1336,7 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) if (!key) return -ENOENT; - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s removing %pMR", hdev->name, bdaddr); list_del(&key->list); kfree(key); @@ -1352,7 +1352,7 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) if (bacmp(bdaddr, &k->bdaddr)) continue; - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s removing %pMR", hdev->name, bdaddr); list_del(&k->list); kfree(k); @@ -1399,7 +1399,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) if (!data) return -ENOENT; - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s removing %pMR", hdev->name, bdaddr); list_del(&data->list); kfree(data); @@ -1438,7 +1438,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, memcpy(data->hash, hash, sizeof(data->hash)); memcpy(data->randomizer, randomizer, sizeof(data->randomizer)); - BT_DBG("%s for %s", hdev->name, batostr(bdaddr)); + BT_DBG("%s for %pMR", hdev->name, bdaddr); return 0; } @@ -2309,8 +2309,8 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) /* Kill stalled connections */ list_for_each_entry_rcu(c, &h->list, list) { if (c->type == type && c->sent) { - BT_ERR("%s killing stalled connection %s", - hdev->name, batostr(&c->dst)); + BT_ERR("%s killing stalled connection %pMR", + hdev->name, &c->dst); hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2551c4b9d65e..3151d8581733 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1266,7 +1266,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - BT_DBG("%s bdaddr %s hcon %p", hdev->name, batostr(&cp->bdaddr), conn); + BT_DBG("%s bdaddr %pMR hcon %p", hdev->name, &cp->bdaddr, conn); if (status) { if (conn && conn->state == BT_CONNECT) { @@ -1695,8 +1695,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) return; } - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&conn->dst), - conn); + BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn); conn->state = BT_CLOSED; mgmt_connect_failed(hdev, &conn->dst, conn->type, @@ -1910,7 +1909,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; - BT_DBG("%s bdaddr %s type 0x%x", hdev->name, batostr(&ev->bdaddr), + BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr, ev->link_type); mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); @@ -2809,13 +2808,13 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) key = hci_find_link_key(hdev, &ev->bdaddr); if (!key) { - BT_DBG("%s link key not found for %s", hdev->name, - batostr(&ev->bdaddr)); + BT_DBG("%s link key not found for %pMR", hdev->name, + &ev->bdaddr); goto not_found; } - BT_DBG("%s found key type %u for %s", hdev->name, key->type, - batostr(&ev->bdaddr)); + BT_DBG("%s found key type %u for %pMR", hdev->name, key->type, + &ev->bdaddr); if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && key->type == HCI_LK_DEBUG_COMBINATION) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a347522b2144..bb42d95f4f41 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1471,7 +1471,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, __u8 auth_type; int err; - BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst), + BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst, dst_type, __le16_to_cpu(psm)); hdev = hci_get_route(dst, src); @@ -5346,7 +5346,7 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) int exact = 0, lm1 = 0, lm2 = 0; struct l2cap_chan *c; - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr); /* Find listening sockets and check their link_mode */ read_lock(&chan_list_lock); @@ -5376,7 +5376,7 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn; - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); + BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); if (!status) { conn = l2cap_conn_add(hcon, status); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index c75107ef8920..f6d066c442eb 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -377,8 +377,8 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, int err = 0; u8 dlci; - BT_DBG("dlc %p state %ld %s %s channel %d", - d, d->state, batostr(src), batostr(dst), channel); + BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d", + d, d->state, src, dst, channel); if (channel < 1 || channel > 30) return -EINVAL; @@ -676,7 +676,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, struct socket *sock; struct sock *sk; - BT_DBG("%s %s", batostr(src), batostr(dst)); + BT_DBG("%pMR -> %pMR", src, dst); *err = rfcomm_l2sock_create(&sock); if (*err < 0) diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 260821a2d6e7..ebd2f577e81b 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -334,7 +334,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr struct sock *sk = sock->sk; int err = 0; - BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); + BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index cb960773c002..0e487e9fe9a9 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -663,8 +663,8 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) if (!dev) return -ENODEV; - BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), - dev->channel, dev->port.count); + BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst, + dev->channel, dev->port.count); spin_lock_irqsave(&dev->port.lock, flags); if (++dev->port.count > 1) { diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index dc42b917aaaf..347d3b954315 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -172,7 +172,7 @@ static int sco_connect(struct sock *sk) struct hci_dev *hdev; int err, type; - BT_DBG("%s -> %s", batostr(src), batostr(dst)); + BT_DBG("%pMR -> %pMR", src, dst); hdev = hci_get_route(dst, src); if (!hdev) @@ -460,7 +460,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le struct sock *sk = sock->sk; int err = 0; - BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); + BT_DBG("sk %p %pMR", sk, &sa->sco_bdaddr); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; @@ -893,7 +893,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) struct hlist_node *node; int lm = 0; - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); + BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr); /* Find listening sockets */ read_lock(&sco_sk_list.lock); @@ -914,7 +914,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) void sco_connect_cfm(struct hci_conn *hcon, __u8 status) { - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); + BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status); if (!status) { struct sco_conn *conn; -- cgit v1.2.3 From fcb73338ed531dcc00cb17ca76fe3e05f774e4e9 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 25 Sep 2012 12:49:44 +0300 Subject: Bluetooth: Use %pMR in sprintf/seq_printf instead of batostr Instead of old unsafe batostr function use %pMR print specifier for printing Bluetooth addresses in sprintf and seq_printf statements. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/cmtp/core.c | 2 +- net/bluetooth/hci_sysfs.c | 10 +++++----- net/bluetooth/hidp/core.c | 8 ++++++-- net/bluetooth/l2cap_core.c | 11 +++++------ net/bluetooth/rfcomm/core.c | 9 ++++----- net/bluetooth/rfcomm/sock.c | 7 +++---- net/bluetooth/rfcomm/tty.c | 2 +- net/bluetooth/sco.c | 4 ++-- 8 files changed, 27 insertions(+), 26 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 6c9c1fd601ca..e0a6ebf2baa6 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -353,7 +353,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) BT_DBG("mtu %d", session->mtu); - sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst)); + sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst); session->sock = sock; session->state = BT_CONFIG; diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index a20e61c3653d..55cceee02a84 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -38,7 +38,7 @@ static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_conn *conn = to_hci_conn(dev); - return sprintf(buf, "%s\n", batostr(&conn->dst)); + return sprintf(buf, "%pMR\n", &conn->dst); } static ssize_t show_link_features(struct device *dev, @@ -224,7 +224,7 @@ static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", batostr(&hdev->bdaddr)); + return sprintf(buf, "%pMR\n", &hdev->bdaddr); } static ssize_t show_features(struct device *dev, @@ -406,8 +406,8 @@ static int inquiry_cache_show(struct seq_file *f, void *p) list_for_each_entry(e, &cache->all, all) { struct inquiry_data *data = &e->data; - seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", - batostr(&data->bdaddr), + seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", + &data->bdaddr, data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode, data->dev_class[2], data->dev_class[1], data->dev_class[0], @@ -440,7 +440,7 @@ static int blacklist_show(struct seq_file *f, void *p) hci_dev_lock(hdev); list_for_each_entry(b, &hdev->blacklist, list) - seq_printf(f, "%s\n", batostr(&b->bdaddr)); + seq_printf(f, "%pMR\n", &b->bdaddr); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ccd985da6518..0c0028463fa3 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -932,8 +932,12 @@ static int hidp_setup_hid(struct hidp_session *session, hid->country = req->country; strncpy(hid->name, req->name, 128); - strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); - strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); + + snprintf(hid->phys, sizeof(hid->phys), "%pMR", + &bt_sk(session->ctrl_sock->sk)->src); + + snprintf(hid->uniq, sizeof(hid->uniq), "%pMR", + &bt_sk(session->ctrl_sock->sk)->dst); hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bb42d95f4f41..c9ba707d7b79 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5640,12 +5640,11 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p) list_for_each_entry(c, &chan_list, global_l) { struct sock *sk = c->sk; - seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - c->state, __le16_to_cpu(c->psm), - c->scid, c->dcid, c->imtu, c->omtu, - c->sec_level, c->mode); + seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", + &bt_sk(sk)->src, &bt_sk(sk)->dst, + c->state, __le16_to_cpu(c->psm), + c->scid, c->dcid, c->imtu, c->omtu, + c->sec_level, c->mode); } read_unlock(&chan_list_lock); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index f6d066c442eb..fb1d83ddc5ab 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -2125,11 +2125,10 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) list_for_each_entry(d, &s->dlcs, list) { struct sock *sk = s->sock->sk; - seq_printf(f, "%s %s %ld %d %d %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - d->state, d->dlci, d->mtu, - d->rx_credits, d->tx_credits); + seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n", + &bt_sk(sk)->src, &bt_sk(sk)->dst, + d->state, d->dlci, d->mtu, + d->rx_credits, d->tx_credits); } } diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index ebd2f577e81b..867a06576d8a 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -973,10 +973,9 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p) read_lock(&rfcomm_sk_list.lock); sk_for_each(sk, node, &rfcomm_sk_list.head) { - seq_printf(f, "%s %s %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - sk->sk_state, rfcomm_pi(sk)->channel); + seq_printf(f, "%pMR %pMR %d %d\n", + &bt_sk(sk)->src, &bt_sk(sk)->dst, + sk->sk_state, rfcomm_pi(sk)->channel); } read_unlock(&rfcomm_sk_list.lock); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 0e487e9fe9a9..6baf64d1a0a8 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -166,7 +166,7 @@ static struct device *rfcomm_get_device(struct rfcomm_dev *dev) static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf) { struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); - return sprintf(buf, "%s\n", batostr(&dev->dst)); + return sprintf(buf, "%pMR\n", &dev->dst); } static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 347d3b954315..450cdcd88e5c 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -959,8 +959,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p) read_lock(&sco_sk_list.lock); sk_for_each(sk, node, &sco_sk_list.head) { - seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), sk->sk_state); + seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src, + &bt_sk(sk)->dst, sk->sk_state); } read_unlock(&sco_sk_list.lock); -- cgit v1.2.3 From 7028a8860f07d0efc5fdd7527fec065b818d108b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 25 Sep 2012 12:49:45 +0300 Subject: Bluetooth: Use %pMR instead of baswap in seq_show Use new bluetooth address print specifier %pMR for printing bluetooth addresses instead of dedicated variable and baswap. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/af_bluetooth.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 9d49ee6d7219..a9ad589f3e72 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -569,7 +569,6 @@ static int bt_seq_show(struct seq_file *seq, void *v) { struct bt_seq_state *s = seq->private; struct bt_sock_list *l = s->l; - bdaddr_t src_baswapped, dst_baswapped; if (v == SEQ_START_TOKEN) { seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent"); @@ -583,18 +582,17 @@ static int bt_seq_show(struct seq_file *seq, void *v) } else { struct sock *sk = sk_entry(v); struct bt_sock *bt = bt_sk(sk); - baswap(&src_baswapped, &bt->src); - baswap(&dst_baswapped, &bt->dst); - seq_printf(seq, "%pK %-6d %-6u %-6u %-6u %-6lu %pM %pM %-6lu", + seq_printf(seq, + "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu", sk, atomic_read(&sk->sk_refcnt), sk_rmem_alloc_get(sk), sk_wmem_alloc_get(sk), sock_i_uid(sk), sock_i_ino(sk), - &src_baswapped, - &dst_baswapped, + &bt->src, + &bt->dst, bt->parent? sock_i_ino(bt->parent): 0LU); if (l->custom_seq_show) { -- cgit v1.2.3 From d945df256a7b2446227fafae2f89db85597412ef Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 25 Sep 2012 12:49:46 +0300 Subject: bluetooth: Remove unneeded batostr function batostr is not needed anymore since for printing Bluetooth addresses we use %pMR specifier. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/bluetooth.h | 1 - net/bluetooth/lib.c | 14 -------------- 2 files changed, 15 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index ede036977ae8..2554b3f5222a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -180,7 +180,6 @@ static inline void bacpy(bdaddr_t *dst, bdaddr_t *src) } void baswap(bdaddr_t *dst, bdaddr_t *src); -char *batostr(bdaddr_t *ba); /* Common socket structures and functions */ diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index e1c97527e16c..b3fbc73516c4 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -41,20 +41,6 @@ void baswap(bdaddr_t *dst, bdaddr_t *src) } EXPORT_SYMBOL(baswap); -char *batostr(bdaddr_t *ba) -{ - static char str[2][18]; - static int i = 1; - - i ^= 1; - sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", - ba->b[5], ba->b[4], ba->b[3], - ba->b[2], ba->b[1], ba->b[0]); - - return str[i]; -} -EXPORT_SYMBOL(batostr); - /* Bluetooth error codes to Unix errno mapping */ int bt_to_errno(__u16 code) { -- cgit v1.2.3 From ee22be7ef4f1d010a76fb41dc8fc687d38e606a3 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 21 Sep 2012 12:30:04 +0300 Subject: Bluetooth: Factor out hci_queue_acl Use hci_chan as parameter instead of hci_conn as we need logical handle from hci_chan for AMP link. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ab4fca2448d5..bd26cb52aaa9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2151,9 +2151,10 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) hdr->dlen = cpu_to_le16(len); } -static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, +static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, struct sk_buff *skb, __u16 flags) { + struct hci_conn *conn = chan->conn; struct hci_dev *hdev = conn->hdev; struct sk_buff *list; @@ -2200,14 +2201,13 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) { - struct hci_conn *conn = chan->conn; - struct hci_dev *hdev = conn->hdev; + struct hci_dev *hdev = chan->conn->hdev; BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags); skb->dev = (void *) hdev; - hci_queue_acl(conn, &chan->data_q, skb, flags); + hci_queue_acl(chan, &chan->data_q, skb, flags); queue_work(hdev->workqueue, &hdev->tx_work); } -- cgit v1.2.3 From 29d8a5909bba10accb82eb805c884a2943d7710f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 21 Sep 2012 12:30:05 +0300 Subject: Bluetooth: Factor out Create Configuration Response Use function to factor out similar code. For BR/EDR send EFS Configuration Response immediately, for HS response will be sent after receiving HCI Logical Link Complete event in the following patches. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c9ba707d7b79..b4e707bfc08d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3582,6 +3582,22 @@ static inline void set_default_fcs(struct l2cap_chan *chan) chan->fcs = L2CAP_FCS_CRC16; } +static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data, + u8 ident, u16 flags) +{ + struct l2cap_conn *conn = chan->conn; + + BT_DBG("conn %p chan %p ident %d flags 0x%4.4x", conn, chan, ident, + flags); + + clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + l2cap_send_cmd(conn, ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(chan, data, + L2CAP_CONF_SUCCESS, flags), data); +} + static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) { struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; @@ -3673,16 +3689,11 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* Got Conf Rsp PENDING from remote side and asume we sent Conf Rsp PENDING in the code above */ if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && - test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { + test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { /* check compatibility */ - clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, flags), rsp); + l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); } unlock: @@ -3730,12 +3741,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* check compatibility */ - clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, buf, - L2CAP_CONF_SUCCESS, 0x0000), buf); + l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, 0); } goto done; -- cgit v1.2.3 From 2e430be386e97224c37086a5a48fa05a9f9b79bb Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 28 Sep 2012 14:44:23 +0300 Subject: Bluetooth: Use %zu print specifier for size_t type Correct warnings Reported-by: Fengguang Wu Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3151d8581733..82e478a459ff 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -883,7 +883,7 @@ static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, rem_len = __le16_to_cpu(rp->rem_len); if (rem_len > frag_len) { - BT_DBG("frag_len %ld rem_len %ld", frag_len, rem_len); + BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len); memcpy(assoc->data + assoc->offset, rp->frag, frag_len); assoc->offset += frag_len; -- cgit v1.2.3 From 13465c0aeb9c56a4e4799f25aeff41d05f8a3fab Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 28 Sep 2012 16:55:00 +0300 Subject: Bluetooth: A2MP: Correct assoc_len size Correct assoc_len and fix warning for x86-64 by using %zu specifier. Reported-by: Fengguang Wu Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 61e657ae71e9..42788cdbb4fe 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -379,12 +379,15 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, struct hci_dev *hdev; struct amp_ctrl *ctrl; struct hci_conn *hcon; + size_t assoc_len; if (len < sizeof(*rsp)) return -EINVAL; - BT_DBG("id %d status 0x%2.2x assoc len %lu", rsp->id, rsp->status, - len - sizeof(*rsp)); + assoc_len = len - sizeof(*rsp); + + BT_DBG("id %d status 0x%2.2x assoc len %zu", rsp->id, rsp->status, + assoc_len); if (rsp->status) return -EINVAL; @@ -392,7 +395,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, /* Save remote ASSOC data */ ctrl = amp_ctrl_lookup(mgr, rsp->id); if (ctrl) { - u8 *assoc, assoc_len = len - sizeof(*rsp); + u8 *assoc; assoc = kzalloc(assoc_len, GFP_KERNEL); if (!assoc) { @@ -466,7 +469,8 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, } if (ctrl) { - u8 *assoc, assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); + size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); + u8 *assoc; ctrl->id = rsp.remote_id; -- cgit v1.2.3 From bc8dce4f7b4908bac69acac34d19b4234ba19cee Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 28 Sep 2012 14:28:50 +0300 Subject: Bluetooth: A2MP: Fix potential NULL dereference Return INVALID_CTRL_ID for unknown AMP controller and for BR/EDR controller and fixes dereference possible NULL pointer. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 42788cdbb4fe..d4946b591b71 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -278,7 +278,7 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("id %d", req->id); hdev = hci_dev_get(req->id); - if (!hdev) { + if (!hdev || hdev->dev_type != HCI_AMP) { struct a2mp_info_rsp rsp; rsp.id = req->id; @@ -286,14 +286,16 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp); - } - if (hdev->dev_type != HCI_BREDR) { - mgr->state = READ_LOC_AMP_INFO; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + goto done; } - hci_dev_put(hdev); + mgr->state = READ_LOC_AMP_INFO; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + +done: + if (hdev) + hci_dev_put(hdev); skb_pull(skb, sizeof(*req)); return 0; -- cgit v1.2.3 From 079db0c6e3854b3af9808268f3b884fa48ed0034 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:53 +0300 Subject: Bluetooth: AMP: Fix possible NULL dereference Check that link key exist before accessing. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/amp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 5dab2d1c7c82..b6e1c3ac74f1 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -184,6 +184,10 @@ int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type) *len = HCI_AMP_LINK_KEY_SIZE; key = hci_find_link_key(hdev, &conn->dst); + if (!key) { + BT_DBG("No Link key for conn %p dst %pMR", conn, &conn->dst); + return -EACCES; + } /* BR/EDR Link Key concatenated together with itself */ memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE); -- cgit v1.2.3 From 85e34368dea6fc8a2d16464e01c85d3b7bd682bd Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:54 +0300 Subject: Bluetooth: Fix dereference after NULL check Move code dereferencing possible NULL pointer to the check branch. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_sock.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 2542abd3336f..a71c4089d175 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -382,13 +382,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch } memset(&sec, 0, sizeof(sec)); - if (chan->conn) + if (chan->conn) { sec.level = chan->conn->hcon->sec_level; - else - sec.level = chan->sec_level; - if (sk->sk_state == BT_CONNECTED) - sec.key_size = chan->conn->hcon->enc_key_size; + if (sk->sk_state == BT_CONNECTED) + sec.key_size = chan->conn->hcon->enc_key_size; + } else { + sec.level = chan->sec_level; + } len = min_t(unsigned int, len, sizeof(sec)); if (copy_to_user(optval, (char *) &sec, len)) -- cgit v1.2.3 From fa4ebc66c432d0e0ec947cb754d4144c4a681f28 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:55 +0300 Subject: Bluetooth: AMP: Factor out amp_ctrl_add Add ctrl_id parameter to amp_ctrl_add since we always set it after function ctrl is created. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 +- net/bluetooth/a2mp.c | 8 ++------ net/bluetooth/amp.c | 7 ++++--- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index b1e54903dd42..ae2c3e536473 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -26,7 +26,7 @@ struct amp_ctrl { int amp_ctrl_put(struct amp_ctrl *ctrl); void amp_ctrl_get(struct amp_ctrl *ctrl); -struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr); +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id); struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); void amp_ctrl_list_flush(struct amp_mgr *mgr); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index d4946b591b71..88a4b583d218 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -316,12 +316,10 @@ static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (rsp->status) return -EINVAL; - ctrl = amp_ctrl_add(mgr); + ctrl = amp_ctrl_add(mgr, rsp->id); if (!ctrl) return -ENOMEM; - ctrl->id = rsp->id; - req.id = rsp->id; a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req), &req); @@ -461,7 +459,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, ctrl = amp_ctrl_lookup(mgr, rsp.remote_id); if (!ctrl) { - ctrl = amp_ctrl_add(mgr); + ctrl = amp_ctrl_add(mgr, rsp.remote_id); if (ctrl) { amp_ctrl_get(ctrl); } else { @@ -474,8 +472,6 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); u8 *assoc; - ctrl->id = rsp.remote_id; - assoc = kzalloc(assoc_len, GFP_KERNEL); if (!assoc) { amp_ctrl_put(ctrl); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index b6e1c3ac74f1..2fc5562a84b9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -45,7 +45,7 @@ int amp_ctrl_put(struct amp_ctrl *ctrl) return kref_put(&ctrl->kref, &_ctrl_destroy); } -struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr) +struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id) { struct amp_ctrl *ctrl; @@ -53,12 +53,13 @@ struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr) if (!ctrl) return NULL; + kref_init(&ctrl->kref); + ctrl->id = id; + mutex_lock(&mgr->amp_ctrls_lock); list_add(&ctrl->list, &mgr->amp_ctrls); mutex_unlock(&mgr->amp_ctrls_lock); - kref_init(&ctrl->kref); - BT_DBG("mgr %p ctrl %p", mgr, ctrl); return ctrl; -- cgit v1.2.3 From a0c234fe8972aa6a5afe2db6c27a3f5d5fbd88e7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:56 +0300 Subject: Bluetooth: AMP: Factor out phylink_add Add direction parameter to phylink_add since it is anyway set later. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 2 +- net/bluetooth/a2mp.c | 4 ++-- net/bluetooth/amp.c | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index ae2c3e536473..2e7c79ea0463 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -31,7 +31,7 @@ struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id); void amp_ctrl_list_flush(struct amp_mgr *mgr); struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, - u8 remote_id); + u8 remote_id, bool out); int phylink_gen_key(struct hci_conn *hcon, u8 *data, u8 *len, u8 *type); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 88a4b583d218..3ff4dc928bf5 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -417,7 +417,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (!hdev) return -EINVAL; - hcon = phylink_add(hdev, mgr, rsp->id); + hcon = phylink_add(hdev, mgr, rsp->id, true); if (!hcon) goto done; @@ -487,7 +487,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, amp_ctrl_put(ctrl); } - hcon = phylink_add(hdev, mgr, req->local_id); + hcon = phylink_add(hdev, mgr, req->local_id, false); if (hcon) { amp_accept_phylink(hdev, mgr, hcon); rsp.status = A2MP_STATUS_SUCCESS; diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 2fc5562a84b9..59da0f15818e 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -108,7 +108,7 @@ static u8 __next_handle(struct amp_mgr *mgr) } struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, - u8 remote_id) + u8 remote_id, bool out) { bdaddr_t *dst = mgr->l2cap_conn->dst; struct hci_conn *hcon; @@ -117,12 +117,14 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, if (!hcon) return NULL; + BT_DBG("hcon %p dst %pMR", hcon, dst); + hcon->state = BT_CONNECT; - hcon->out = true; hcon->attempt++; hcon->handle = __next_handle(mgr); hcon->remote_id = remote_id; hcon->amp_mgr = mgr; + hcon->out = out; return hcon; } -- cgit v1.2.3 From dcc042d56f66fb2a9db6a2683e6aa4815782da8b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 5 Oct 2012 16:56:57 +0300 Subject: Bluetooth: AMP: Use block_mtu for AMP controller Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b4e707bfc08d..d605bbfddec1 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1390,10 +1390,22 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); - if (hcon->hdev->le_mtu && hcon->type == LE_LINK) - conn->mtu = hcon->hdev->le_mtu; - else + switch (hcon->type) { + case AMP_LINK: + conn->mtu = hcon->hdev->block_mtu; + break; + + case LE_LINK: + if (hcon->hdev->le_mtu) { + conn->mtu = hcon->hdev->le_mtu; + break; + } + /* fall through */ + + default: conn->mtu = hcon->hdev->acl_mtu; + break; + } conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; -- cgit v1.2.3 From 8936fa6d1c202abeb94c51c68897342e8714dd69 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 8 Oct 2012 11:14:41 +0300 Subject: Bluetooth: L2CAP: Fix using default Flush Timeout for EFS There are two Flush Timeouts: one is old Flush Timeot Option which is 2 octets and the second is Flush Timeout inside EFS which is 4 octets long. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/l2cap_core.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7002f0d656ed..caab98c45121 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -32,7 +32,8 @@ /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MIN_MTU 48 -#define L2CAP_DEFAULT_FLUSH_TO 0xffff +#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF +#define L2CAP_EFS_DEFAULT_FLUSH_TO 0xFFFFFFFF #define L2CAP_DEFAULT_TX_WINDOW 63 #define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF #define L2CAP_DEFAULT_MAX_TX 3 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d605bbfddec1..d42cdb145c6d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -504,7 +504,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; - chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; + chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO; l2cap_chan_hold(chan); @@ -2727,7 +2727,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) efs.msdu = cpu_to_le16(chan->local_msdu); efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); efs.acc_lat = __constant_cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); - efs.flush_to = __constant_cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); + efs.flush_to = __constant_cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO); break; case L2CAP_MODE_STREAMING: @@ -2744,7 +2744,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) } l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), - (unsigned long) &efs); + (unsigned long) &efs); } static void l2cap_ack_timeout(struct work_struct *work) -- cgit v1.2.3 From 23d3a86948b65055345dc1fe778513e4bf3b07d6 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Mon, 8 Oct 2012 16:48:32 -0400 Subject: Bluetooth: don't attempt to free a channel that wasn't created We may currently attempt to free a channel which wasn't created due to an error in the initialization path, this would cause a NULL ptr deref. This would cause the following oops: [ 12.919073] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010 [ 12.919131] IP: [] l2cap_chan_put+0x34/0x50 [ 12.919135] PGD 0 [ 12.919138] Oops: 0002 [#1] PREEMPT SMP DEBUG_PAGEALLOC [ 12.919193] Dumping ftrace buffer: [ 12.919242] (ftrace buffer empty) [ 12.919314] Modules linked in: [ 12.919318] CPU 1 [ 12.919319] Pid: 6210, comm: krfcommd Tainted: G W 3.6.0-next-20121004-sasha-00005-gb010653-dirty #30 [ 12.919374] RIP: 0010:[] [] l2cap_chan_put+0x34/0x50 [ 12.919377] RSP: 0000:ffff880066933c38 EFLAGS: 00010246 [ 12.919378] RAX: ffffffff8366c780 RBX: 0000000000000000 RCX: 6666666666666667 [ 12.919379] RDX: 0000000000000fa0 RSI: ffffffff84d3f79e RDI: 0000000000000010 [ 12.919381] RBP: ffff880066933c48 R08: ffffffff859989f8 R09: 0000000000000001 [ 12.919382] R10: 0000000000000000 R11: 7fffffffffffffff R12: 0000000000000000 [ 12.919383] R13: ffff88009b00a200 R14: ffff88009b00a200 R15: 0000000000000001 [ 12.919385] FS: 0000000000000000(0000) GS:ffff880033600000(0000) knlGS:0000000000000000 [ 12.919437] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 12.919440] CR2: 0000000000000010 CR3: 0000000005026000 CR4: 00000000000406e0 [ 12.919446] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 12.919451] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 12.919504] Process krfcommd (pid: 6210, threadinfo ffff880066932000, task ffff880065c4b000) [ 12.919506] Stack: [ 12.919510] ffff88009b00a200 ffff880032084000 ffff880066933c68 ffffffff8366c7bc [ 12.919513] 7fffffffffffffff ffff880032084000 ffff880066933c98 ffffffff833ae0ae [ 12.919516] ffff880066933ca8 0000000000000000 0000000000000000 ffff88009b00a200 [ 12.919517] Call Trace: [ 12.919522] [] l2cap_sock_destruct+0x3c/0x80 [ 12.919527] [] __sk_free+0x1e/0x1f0 [ 12.919530] [] sk_free+0x17/0x20 [ 12.919585] [] l2cap_sock_alloc.constprop.5+0x9e/0xd0 [ 12.919591] [] l2cap_sock_create+0x7e/0x100 [ 12.919652] [] ? _raw_read_lock+0x6a/0x80 [ 12.919658] [] ? bt_sock_create+0x74/0x110 [ 12.919660] [] bt_sock_create+0xb8/0x110 [ 12.919664] [] __sock_create+0x282/0x3b0 [ 12.919720] [] ? __sock_create+0x100/0x3b0 [ 12.919725] [] ? rfcomm_process_sessions+0x17e0/0x17e0 [ 12.919779] [] sock_create_kern+0x1f/0x30 [ 12.919784] [] rfcomm_l2sock_create+0x44/0x70 [ 12.919787] [] ? rfcomm_process_sessions+0x17e0/0x17e0 [ 12.919790] [] rfcomm_run+0x4e/0x1f0 [ 12.919846] [] ? rfcomm_process_sessions+0x17e0/0x17e0 [ 12.919852] [] kthread+0xe3/0xf0 [ 12.919908] [] ? put_lock_stats.isra.14+0xe/0x40 [ 12.919914] [] ? flush_kthread_work+0x1f0/0x1f0 [ 12.919968] [] ret_from_fork+0x7c/0x90 [ 12.919973] [] ? flush_kthread_work+0x1f0/0x1f0 [ 12.920161] Code: 83 ec 08 f6 05 ff 58 44 02 04 74 1b 8b 4f 10 48 89 fa 48 c7 c6 d9 d7 d4 84 48 c7 c7 80 9e aa 85 31 c0 e8 80 ac 3a fe 48 8d 7b 10 83 6b 10 01 0f 94 c0 84 c0 74 05 e8 8b e0 ff ff 48 83 c4 08 [ 12.920165] RIP [] l2cap_chan_put+0x34/0x50 [ 12.920166] RSP [ 12.920167] CR2: 0000000000000010 [ 12.920417] ---[ end trace 5a9114e8a158ab84 ]--- Introduced in commit 61d6ef3e ("Bluetooth: Make better use of l2cap_chan reference counting"). Signed-off-by: Sasha Levin Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_sock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index a71c4089d175..af467ce2cf2b 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1083,7 +1083,8 @@ static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); - l2cap_chan_put(l2cap_pi(sk)->chan); + if (l2cap_pi(sk)->chan) + l2cap_chan_put(l2cap_pi(sk)->chan); if (l2cap_pi(sk)->rx_busy_skb) { kfree_skb(l2cap_pi(sk)->rx_busy_skb); l2cap_pi(sk)->rx_busy_skb = NULL; -- cgit v1.2.3 From d8aece2af380a251daf2351d92dd38b9edf10b84 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Wed, 10 Oct 2012 22:09:28 +0530 Subject: Bluetooth: Use __constant modifier for L2CAP SMP CID Since the L2CAP_CID_SMP is constant, __constant_cpu_to_le16() is the right go here. Signed-off-by: Syam Sidhardhan Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 8c225ef349cd..9b54fea080c9 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -165,7 +165,7 @@ static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(sizeof(code) + dlen); - lh->cid = cpu_to_le16(L2CAP_CID_SMP); + lh->cid = __constant_cpu_to_le16(L2CAP_CID_SMP); memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); -- cgit v1.2.3 From 5bcb80944dffe68055c1745dc919d22ee1e276a5 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Wed, 10 Oct 2012 22:09:29 +0530 Subject: Bluetooth: Use __constant modifier for RFCOMM PSM Since the RFCOMM_PSM is constant, __constant_cpu_to_le16() is the right go here. Signed-off-by: Syam Sidhardhan Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index fb1d83ddc5ab..201fdf737209 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -709,7 +709,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bacpy(&addr.l2_bdaddr, dst); addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); + addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM); addr.l2_cid = 0; *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); if (*err == 0 || *err == -EINPROGRESS) @@ -1987,7 +1987,7 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Bind socket */ bacpy(&addr.l2_bdaddr, ba); addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); + addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM); addr.l2_cid = 0; err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); if (err < 0) { -- cgit v1.2.3 From 12d5978165944a9c5b059a8258685539387fcf90 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:26 +0300 Subject: Bluetooth: Allow to set flush timeout Enable setting of flush timeout via setsockopt Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_sock.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index af467ce2cf2b..ed2dfc942c3c 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -529,6 +529,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->fcs = opts.fcs; chan->max_tx = opts.max_tx; chan->tx_win = opts.txwin_size; + chan->flush_to = opts.flush_to; break; case L2CAP_LM: -- cgit v1.2.3 From 53502d69be49e3dd5bc95ab0f2deeaea260bd617 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:27 +0300 Subject: Bluetooth: AMP: Handle AMP_LINK timeout When AMP_LINK timeouts execute HCI_OP_DISCONN_PHY_LINK as analog to HCI_OP_DISCONNECT for ACL_LINK. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 90ae4f0a4fdc..dfa108c4abec 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -285,6 +285,8 @@ struct hci_dev { int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); }; +#define HCI_PHY_HANDLE(handle) (handle & 0xff) + struct hci_conn { struct list_head list; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 53202f6733ae..64875794dd9b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -130,6 +130,20 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } +static void hci_amp_disconn(struct hci_conn *conn, __u8 reason) +{ + struct hci_cp_disconn_phy_link cp; + + BT_DBG("hcon %p", conn); + + conn->state = BT_DISCONN; + + cp.phy_handle = HCI_PHY_HANDLE(conn->handle); + cp.reason = reason; + hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK, + sizeof(cp), &cp); +} + static void hci_add_sco(struct hci_conn *conn, __u16 handle) { struct hci_dev *hdev = conn->hdev; @@ -230,11 +244,24 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) } } +static void hci_conn_disconnect(struct hci_conn *conn) +{ + __u8 reason = hci_proto_disconn_ind(conn); + + switch (conn->type) { + case ACL_LINK: + hci_acl_disconn(conn, reason); + break; + case AMP_LINK: + hci_amp_disconn(conn, reason); + break; + } +} + static void hci_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, disc_work.work); - __u8 reason; BT_DBG("hcon %p state %s", conn, state_to_string(conn->state)); @@ -253,8 +280,7 @@ static void hci_conn_timeout(struct work_struct *work) break; case BT_CONFIG: case BT_CONNECTED: - reason = hci_proto_disconn_ind(conn); - hci_acl_disconn(conn, reason); + hci_conn_disconnect(conn); break; default: conn->state = BT_CLOSED; -- cgit v1.2.3 From 42c4e53e7ac3d4069105e852d1ee24e6ee9e57b8 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:28 +0300 Subject: Bluetooth: AMP: Add handle to hci_chan structure hci_chan will be identified by handle used in logical link creation process. This handle is used in AMP ACL-U packet handle field. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dfa108c4abec..b697ef342020 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -350,7 +350,7 @@ struct hci_conn { struct hci_chan { struct list_head list; - + __u16 handle; struct hci_conn *conn; struct sk_buff_head data_q; unsigned int sent; @@ -567,6 +567,7 @@ void hci_conn_check_pending(struct hci_dev *hdev); struct hci_chan *hci_chan_create(struct hci_conn *conn); void hci_chan_del(struct hci_chan *chan); void hci_chan_list_flush(struct hci_conn *conn); +struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 dst_type, __u8 sec_level, __u8 auth_type); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 64875794dd9b..fe646211c61f 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -989,3 +989,35 @@ void hci_chan_list_flush(struct hci_conn *conn) list_for_each_entry_safe(chan, n, &conn->chan_list, list) hci_chan_del(chan); } + +static struct hci_chan *__hci_chan_lookup_handle(struct hci_conn *hcon, + __u16 handle) +{ + struct hci_chan *hchan; + + list_for_each_entry(hchan, &hcon->chan_list, list) { + if (hchan->handle == handle) + return hchan; + } + + return NULL; +} + +struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *hcon; + struct hci_chan *hchan = NULL; + + rcu_read_lock(); + + list_for_each_entry_rcu(hcon, &h->list, list) { + hchan = __hci_chan_lookup_handle(hcon, handle); + if (hchan) + break; + } + + rcu_read_unlock(); + + return hchan; +} -- cgit v1.2.3 From 76ef7cf7722331097f5f47d23342128b1b5d072d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:29 +0300 Subject: Bluetooth: AMP: Handle number of compl blocks for AMP_LINK Add handling blocks count for AMP link. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 82e478a459ff..5c0b6c161a01 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2677,6 +2677,27 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) queue_work(hdev->workqueue, &hdev->tx_work); } +static struct hci_conn *__hci_conn_lookup_handle(struct hci_dev *hdev, + __u16 handle) +{ + struct hci_chan *chan; + + switch (hdev->dev_type) { + case HCI_BREDR: + return hci_conn_hash_lookup_handle(hdev, handle); + case HCI_AMP: + chan = hci_chan_lookup_handle(hdev, handle); + if (chan) + return chan->conn; + break; + default: + BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type); + break; + } + + return NULL; +} + static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_num_comp_blocks *ev = (void *) skb->data; @@ -2698,13 +2719,13 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) for (i = 0; i < ev->num_hndl; i++) { struct hci_comp_blocks_info *info = &ev->handles[i]; - struct hci_conn *conn; + struct hci_conn *conn = NULL; __u16 handle, block_count; handle = __le16_to_cpu(info->handle); block_count = __le16_to_cpu(info->blocks); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = __hci_conn_lookup_handle(hdev, handle); if (!conn) continue; -- cgit v1.2.3 From bd1eb66ba4eee21de3be24212b135f57101ad930 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 10 Oct 2012 17:38:30 +0300 Subject: Bluetooth: AMP: Handle AMP_LINK connection AMP_LINK represents physical link between AMP controllers. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 13 +++++++++++++ net/bluetooth/hci_core.c | 22 +++++++++++++++++++--- net/bluetooth/hci_event.c | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b697ef342020..d5ed054d77cf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -73,6 +73,7 @@ struct discovery_state { struct hci_conn_hash { struct list_head list; unsigned int acl_num; + unsigned int amp_num; unsigned int sco_num; unsigned int le_num; }; @@ -449,6 +450,9 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c) case ACL_LINK: h->acl_num++; break; + case AMP_LINK: + h->amp_num++; + break; case LE_LINK: h->le_num++; break; @@ -470,6 +474,9 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) case ACL_LINK: h->acl_num--; break; + case AMP_LINK: + h->amp_num--; + break; case LE_LINK: h->le_num--; break; @@ -486,6 +493,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) switch (type) { case ACL_LINK: return h->acl_num; + case AMP_LINK: + return h->amp_num; case LE_LINK: return h->le_num; case SCO_LINK: @@ -801,6 +810,10 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason) sco_disconn_cfm(conn, reason); break; + /* L2CAP would be handled for BREDR chan */ + case AMP_LINK: + break; + default: BT_ERR("unknown link type %d", conn->type); break; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bd26cb52aaa9..2e72c410fb47 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2379,6 +2379,9 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, case ACL_LINK: cnt = hdev->acl_cnt; break; + case AMP_LINK: + cnt = hdev->block_cnt; + break; case SCO_LINK: case ESCO_LINK: cnt = hdev->sco_cnt; @@ -2508,11 +2511,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) struct hci_chan *chan; struct sk_buff *skb; int quote; + u8 type; __check_timeout(hdev, cnt); + BT_DBG("%s", hdev->name); + + if (hdev->dev_type == HCI_AMP) + type = AMP_LINK; + else + type = ACL_LINK; + while (hdev->block_cnt > 0 && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + (chan = hci_chan_sent(hdev, type, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote > 0 && (skb = skb_peek(&chan->data_q))) { int blocks; @@ -2545,14 +2556,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) } if (cnt != hdev->block_cnt) - hci_prio_recalculate(hdev, ACL_LINK); + hci_prio_recalculate(hdev, type); } static void hci_sched_acl(struct hci_dev *hdev) { BT_DBG("%s", hdev->name); - if (!hci_conn_num(hdev, ACL_LINK)) + /* No ACL link over BR/EDR controller */ + if (!hci_conn_num(hdev, ACL_LINK) && hdev->dev_type == HCI_BREDR) + return; + + /* No AMP link over AMP controller */ + if (!hci_conn_num(hdev, AMP_LINK) && hdev->dev_type == HCI_AMP) return; switch (hdev->flow_ctl_mode) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5c0b6c161a01..0383635f91fb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2733,6 +2733,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) switch (conn->type) { case ACL_LINK: + case AMP_LINK: hdev->block_cnt += block_count; if (hdev->block_cnt > hdev->num_blocks) hdev->block_cnt = hdev->num_blocks; -- cgit v1.2.3 From f5a2598d80fda49756f234a43bc332a13ce95128 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 11 Oct 2012 17:48:21 +0300 Subject: Bluetooth: Process create response and connect response identically Signed-off-by: Mat Martineau Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d42cdb145c6d..54f5a4e98d33 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4033,14 +4033,6 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, return 0; } -static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, void *data) -{ - BT_DBG("conn %p", conn); - - return l2cap_connect_rsp(conn, cmd, data); -} - static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, u16 icid, u16 result) { @@ -4244,6 +4236,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_CONN_RSP: + case L2CAP_CREATE_CHAN_RSP: err = l2cap_connect_rsp(conn, cmd, data); break; @@ -4282,10 +4275,6 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, err = l2cap_create_channel_req(conn, cmd, cmd_len, data); break; - case L2CAP_CREATE_CHAN_RSP: - err = l2cap_create_channel_rsp(conn, cmd, data); - break; - case L2CAP_MOVE_CHAN_REQ: err = l2cap_move_channel_req(conn, cmd, cmd_len, data); break; -- cgit v1.2.3 From 4c89b6aad5b7c5c56dadca66af6ceae0addbf2bf Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Thu, 11 Oct 2012 17:48:22 +0300 Subject: Bluetooth: Factor out common L2CAP connection code L2CAP connect requests and create channel requests share a significant amount of code. This change moves common code to a new function. Signed-off-by: Mat Martineau Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 54f5a4e98d33..867eda23435b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3394,7 +3394,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd return 0; } -static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, + u8 *data, u8 rsp_code, u8 amp_id) { struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; @@ -3488,7 +3489,7 @@ sendresp: rsp.dcid = cpu_to_le16(dcid); rsp.result = cpu_to_le16(result); rsp.status = cpu_to_le16(status); - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp); if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { struct l2cap_info_req info; @@ -3511,7 +3512,12 @@ sendresp: l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } +} +static int l2cap_connect_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) +{ + __l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); return 0; } -- cgit v1.2.3 From 2d7928184c3d8724064de282be4e3c3d70f39d32 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Sat, 6 Oct 2012 10:07:01 +0100 Subject: Bluetooth: Fix L2CAP coding style Follow the net subsystem coding style Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 316 ++++++++++++++++++++++++--------------------- net/bluetooth/l2cap_sock.c | 62 +++++---- 2 files changed, 207 insertions(+), 171 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 867eda23435b..a032741c96e6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -48,19 +48,20 @@ static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, - u8 code, u8 ident, u16 dlen, void *data); + u8 code, u8 ident, u16 dlen, void *data); static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, - void *data); + void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, - struct sk_buff_head *skbs, u8 event); + struct sk_buff_head *skbs, u8 event); /* ---- L2CAP channels ---- */ -static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) +static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, + u16 cid) { struct l2cap_chan *c; @@ -71,7 +72,8 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 return NULL; } -static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) +static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, + u16 cid) { struct l2cap_chan *c; @@ -84,7 +86,8 @@ static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 /* Find channel with given SCID. * Returns locked channel. */ -static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) +static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, + u16 cid) { struct l2cap_chan *c; @@ -97,7 +100,8 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci return c; } -static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) +static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, + u8 ident) { struct l2cap_chan *c; @@ -178,7 +182,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void __l2cap_state_change(struct l2cap_chan *chan, int state) { BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state), - state_to_string(state)); + state_to_string(state)); chan->state = state; chan->ops->state_change(chan, state); @@ -361,7 +365,7 @@ static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq) static void l2cap_chan_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - chan_timer.work); + chan_timer.work); struct l2cap_conn *conn = chan->conn; int reason; @@ -373,7 +377,7 @@ static void l2cap_chan_timeout(struct work_struct *work) if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) reason = ECONNREFUSED; else if (chan->state == BT_CONNECT && - chan->sec_level != BT_SECURITY_SDP) + chan->sec_level != BT_SECURITY_SDP) reason = ECONNREFUSED; else reason = ETIMEDOUT; @@ -573,8 +577,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) struct l2cap_conn *conn = chan->conn; struct sock *sk = chan->sk; - BT_DBG("chan %p state %s sk %p", chan, - state_to_string(chan->state), sk); + BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state), + sk); switch (chan->state) { case BT_LISTEN: @@ -585,7 +589,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONNECTED: case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { + conn->hcon->type == ACL_LINK) { __set_chan_timer(chan, sk->sk_sndtimeo); l2cap_send_disconn_req(conn, chan, reason); } else @@ -594,7 +598,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONNECT2: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { + conn->hcon->type == ACL_LINK) { struct l2cap_conn_rsp rsp; __u16 result; @@ -609,7 +613,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) rsp.result = cpu_to_le16(result); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); } l2cap_chan_del(chan, reason); @@ -691,7 +695,8 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn) return id; } -static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +static 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; @@ -718,10 +723,10 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) u16 flags; BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, - skb->priority); + skb->priority); if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && - lmp_no_flush_capable(hcon->hdev)) + lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; else flags = ACL_START; @@ -1008,7 +1013,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) return; if (l2cap_chan_check_security(chan) && - __l2cap_no_conn_pending(chan)) { + __l2cap_no_conn_pending(chan)) { l2cap_start_connection(chan); } } else { @@ -1020,8 +1025,8 @@ static void l2cap_do_start(struct l2cap_chan *chan) schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); + l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, + sizeof(req), &req); } } @@ -1041,7 +1046,8 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) +static void l2cap_send_disconn_req(struct l2cap_conn *conn, + struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; struct l2cap_disconn_req req; @@ -1062,8 +1068,8 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c req.dcid = cpu_to_le16(chan->dcid); req.scid = cpu_to_le16(chan->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, + sizeof(req), &req); lock_sock(sk); __l2cap_state_change(chan, BT_DISCONN); @@ -1092,13 +1098,13 @@ static void l2cap_conn_start(struct l2cap_conn *conn) if (chan->state == BT_CONNECT) { if (!l2cap_chan_check_security(chan) || - !__l2cap_no_conn_pending(chan)) { + !__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); continue; } if (!l2cap_mode_supported(chan->mode, conn->feat_mask) - && test_bit(CONF_STATE2_DEVICE, + && test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { l2cap_chan_close(chan, ECONNRESET); l2cap_chan_unlock(chan); @@ -1135,17 +1141,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn) } l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); if (test_bit(CONF_REQ_SENT, &chan->conf_state) || - rsp.result != L2CAP_CR_SUCCESS) { + rsp.result != L2CAP_CR_SUCCESS) { l2cap_chan_unlock(chan); continue; } set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } @@ -1302,7 +1308,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) static void l2cap_info_timeout(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - info_timer.work); + info_timer.work); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; @@ -1356,7 +1362,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) static void security_timeout(struct work_struct *work) { struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - security_timer.work); + security_timer.work); BT_DBG("conn %p", conn); @@ -1496,7 +1502,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, /* PSM must be odd and lsb of upper byte must be 0 */ if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; goto done; } @@ -1805,7 +1811,7 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq); if (!skb) { BT_DBG("Error: Can't retransmit seq %d, frame missing", - seq); + seq); continue; } @@ -1890,7 +1896,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan, if (chan->unacked_frames) { skb_queue_walk(&chan->tx_q, skb) { if (bt_cb(skb)->control.txseq == control->reqseq || - skb == chan->tx_send_head) + skb == chan->tx_send_head) break; } @@ -2191,7 +2197,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, } int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority) + u32 priority) { struct sk_buff *skb; int err; @@ -2653,7 +2659,8 @@ fail: return NULL; } -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) +static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, + unsigned long *val) { struct l2cap_conf_opt *opt = *ptr; int len; @@ -2833,13 +2840,13 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __l2cap_ews_supported(chan)) { + __l2cap_ews_supported(chan)) { /* use extended control field */ set_bit(FLAG_EXT_CTRL, &chan->flags); chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; } else { chan->tx_win = min_t(u16, chan->tx_win, - L2CAP_DEFAULT_TX_WINDOW); + L2CAP_DEFAULT_TX_WINDOW); chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; } chan->ack_win = chan->tx_win; @@ -2879,7 +2886,7 @@ done: switch (chan->mode) { case L2CAP_MODE_BASIC: if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && - !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) + !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; rfc.mode = L2CAP_MODE_BASIC; @@ -2890,7 +2897,7 @@ done: rfc.max_pdu_size = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); + (unsigned long) &rfc); break; case L2CAP_MODE_ERTM: @@ -2900,18 +2907,17 @@ done: rfc.monitor_timeout = 0; size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); l2cap_txwin_setup(chan); rfc.txwin_size = min_t(u16, chan->tx_win, - L2CAP_DEFAULT_TX_WINDOW); + L2CAP_DEFAULT_TX_WINDOW); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); + (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); @@ -2920,14 +2926,14 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } if (test_bit(FLAG_EXT_CTRL, &chan->flags)) l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, - chan->tx_win); + chan->tx_win); break; case L2CAP_MODE_STREAMING: @@ -2939,13 +2945,12 @@ done: rfc.monitor_timeout = 0; size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); + (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); @@ -2954,7 +2959,7 @@ done: break; if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } @@ -3046,7 +3051,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_MODE_ERTM: if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { chan->mode = l2cap_select_mode(rfc.mode, - chan->conn->feat_mask); + chan->conn->feat_mask); break; } @@ -3071,8 +3076,8 @@ done: if (chan->num_conf_rsp == 1) return -ECONNREFUSED; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); } if (result == L2CAP_CONF_SUCCESS) { @@ -3089,8 +3094,8 @@ done: if (remote_efs) { if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) { + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) { result = L2CAP_CONF_UNACCEPT; @@ -3098,8 +3103,8 @@ done: return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), - (unsigned long) &efs); + sizeof(efs), + (unsigned long) &efs); } else { /* Send PENDING Conf Rsp */ result = L2CAP_CONF_PENDING; @@ -3122,10 +3127,8 @@ done: chan->remote_max_tx = rfc.max_transmit; size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), - chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + chan->conn->mtu - L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; @@ -3137,36 +3140,35 @@ done: set_bit(CONF_MODE_DONE, &chan->conf_state); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + sizeof(rfc), (unsigned long) &rfc); if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { chan->remote_id = efs.id; chan->remote_stype = efs.stype; chan->remote_msdu = le16_to_cpu(efs.msdu); chan->remote_flush_to = - le32_to_cpu(efs.flush_to); + le32_to_cpu(efs.flush_to); chan->remote_acc_lat = - le32_to_cpu(efs.acc_lat); + le32_to_cpu(efs.acc_lat); chan->remote_sdu_itime = le32_to_cpu(efs.sdu_itime); l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), (unsigned long) &efs); + sizeof(efs), + (unsigned long) &efs); } break; case L2CAP_MODE_STREAMING: size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), - chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); + chan->conn->mtu - L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE); rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; set_bit(CONF_MODE_DONE, &chan->conf_state); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); break; @@ -3187,7 +3189,8 @@ done: return ptr - data; } -static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result) +static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, + void *data, u16 *result) { struct l2cap_conf_req *req = data; void *ptr = req->data; @@ -3214,7 +3217,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi case L2CAP_CONF_FLUSH_TO: chan->flush_to = val; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, - 2, chan->flush_to); + 2, chan->flush_to); break; case L2CAP_CONF_RFC: @@ -3222,13 +3225,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi memcpy(&rfc, (void *)val, olen); if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && - rfc.mode != chan->mode) + rfc.mode != chan->mode) return -ECONNREFUSED; chan->fcs = 0; l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); + sizeof(rfc), (unsigned long) &rfc); break; case L2CAP_CONF_EWS: @@ -3242,12 +3245,12 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi memcpy(&efs, (void *)val, olen); if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) return -ECONNREFUSED; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), (unsigned long) &efs); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), + (unsigned long) &efs); break; } } @@ -3270,10 +3273,10 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { chan->local_msdu = le16_to_cpu(efs.msdu); chan->local_sdu_itime = - le32_to_cpu(efs.sdu_itime); + le32_to_cpu(efs.sdu_itime); chan->local_acc_lat = le32_to_cpu(efs.acc_lat); chan->local_flush_to = - le32_to_cpu(efs.flush_to); + le32_to_cpu(efs.flush_to); } break; @@ -3288,7 +3291,8 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi return ptr - data; } -static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags) +static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, + u16 result, u16 flags) { struct l2cap_conf_rsp *rsp = data; void *ptr = rsp->data; @@ -3312,14 +3316,13 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) rsp.dcid = cpu_to_le16(chan->scid); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } @@ -3374,7 +3377,8 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) } } -static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_command_rej(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data; @@ -3382,7 +3386,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd return 0; if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && - cmd->ident == conn->info_ident) { + cmd->ident == conn->info_ident) { cancel_delayed_work(&conn->info_timer); conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; @@ -3422,7 +3426,7 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, /* Check if the ACL is secure enough (if not SDP) */ if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) && - !hci_conn_check_link_mode(conn->hcon)) { + !hci_conn_check_link_mode(conn->hcon)) { conn->disc_reason = HCI_ERROR_AUTH_FAILURE; result = L2CAP_CR_SEC_BLOCK; goto response; @@ -3500,16 +3504,16 @@ sendresp: schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(info), &info); + l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ, + sizeof(info), &info); } if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) && - result == L2CAP_CR_SUCCESS) { + result == L2CAP_CR_SUCCESS) { u8 buf[128]; set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } } @@ -3521,7 +3525,8 @@ static int l2cap_connect_req(struct l2cap_conn *conn, return 0; } -static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_connect_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; u16 scid, dcid, result, status; @@ -3535,7 +3540,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd status = __le16_to_cpu(rsp->status); BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", - dcid, scid, result, status); + dcid, scid, result, status); mutex_lock(&conn->chan_lock); @@ -3568,7 +3573,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd break; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, req), req); + l2cap_build_conf_req(chan, req), req); chan->num_conf_req++; break; @@ -3616,7 +3621,9 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data, L2CAP_CONF_SUCCESS, flags), data); } -static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) +static inline int l2cap_config_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) { struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; u16 dcid, flags; @@ -3641,7 +3648,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr rej.dcid = cpu_to_le16(chan->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); + sizeof(rej), &rej); goto unlock; } @@ -3649,8 +3656,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr len = cmd_len - sizeof(*req); if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) { l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_REJECT, flags), rsp); + l2cap_build_conf_rsp(chan, rsp, + L2CAP_CONF_REJECT, flags), rsp); goto unlock; } @@ -3661,8 +3668,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (flags & L2CAP_CONF_FLAG_CONTINUATION) { /* Incomplete config. Send empty response. */ l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, flags), rsp); + l2cap_build_conf_rsp(chan, rsp, + L2CAP_CONF_SUCCESS, flags), rsp); goto unlock; } @@ -3700,7 +3707,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) { u8 buf[64]; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); + l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } @@ -3719,7 +3726,8 @@ unlock: return err; } -static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_config_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; u16 scid, flags, result; @@ -3751,7 +3759,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr char buf[64]; len = l2cap_parse_conf_rsp(chan, rsp->data, len, - buf, &result); + buf, &result); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; @@ -3775,14 +3783,14 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr /* throw out any old stored conf requests */ result = L2CAP_CONF_SUCCESS; len = l2cap_parse_conf_rsp(chan, rsp->data, len, - req, &result); + req, &result); if (len < 0) { l2cap_send_disconn_req(conn, chan, ECONNRESET); goto done; } l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_CONF_REQ, len, req); + L2CAP_CONF_REQ, len, req); chan->num_conf_req++; if (result != L2CAP_CONF_SUCCESS) goto done; @@ -3820,7 +3828,8 @@ done: return err; } -static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_disconnect_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data; struct l2cap_disconn_rsp rsp; @@ -3866,7 +3875,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd return 0; } -static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data; u16 dcid, scid; @@ -3900,7 +3910,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd return 0; } -static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_information_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_req *req = (struct l2cap_info_req *) data; u16 type; @@ -3917,14 +3928,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS); if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING - | L2CAP_FEAT_FCS; + | L2CAP_FEAT_FCS; if (enable_hs) feat_mask |= L2CAP_FEAT_EXT_FLOW - | L2CAP_FEAT_EXT_WINDOW; + | L2CAP_FEAT_EXT_WINDOW; put_unaligned_le32(feat_mask, rsp->data); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(buf), buf); + l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), + buf); } else if (type == L2CAP_IT_FIXED_CHAN) { u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; @@ -3937,20 +3948,21 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm rsp->type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS); memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(buf), buf); + l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), + buf); } else { struct l2cap_info_rsp rsp; rsp.type = cpu_to_le16(type); rsp.result = __constant_cpu_to_le16(L2CAP_IR_NOTSUPP); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), + &rsp); } return 0; } -static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +static inline int l2cap_information_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data; u16 type, result; @@ -3962,7 +3974,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm /* L2CAP Info req/rsp are unbound to channels, add extra checks */ if (cmd->ident != conn->info_ident || - conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) + conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) return 0; cancel_delayed_work(&conn->info_timer); @@ -3987,7 +3999,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm conn->info_ident = l2cap_get_ident(conn); l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); + L2CAP_INFO_REQ, sizeof(req), &req); } else { conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; conn->info_ident = 0; @@ -4009,8 +4021,8 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm } static inline int l2cap_create_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, - void *data) + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_create_chan_req *req = data; struct l2cap_create_chan_rsp rsp; @@ -4039,6 +4051,15 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, return 0; } +static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + void *data) +{ + BT_DBG("conn %p", conn); + + return l2cap_connect_rsp(conn, cmd, data); +} + static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, u16 icid, u16 result) { @@ -4165,7 +4186,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, } static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, - u16 to_multiplier) + u16 to_multiplier) { u16 max_latency; @@ -4186,7 +4207,8 @@ static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, } static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) + struct l2cap_cmd_hdr *cmd, + u8 *data) { struct hci_conn *hcon = conn->hcon; struct l2cap_conn_param_update_req *req; @@ -4208,7 +4230,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, to_multiplier = __le16_to_cpu(req->to_multiplier); BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x", - min, max, latency, to_multiplier); + min, max, latency, to_multiplier); memset(&rsp, 0, sizeof(rsp)); @@ -4219,7 +4241,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); if (!err) hci_le_conn_update(hcon, min, max, latency, to_multiplier); @@ -4228,7 +4250,8 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, } static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) { int err = 0; @@ -4307,7 +4330,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, } static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) + struct l2cap_cmd_hdr *cmd, u8 *data) { switch (cmd->code) { case L2CAP_COMMAND_REJ: @@ -4326,7 +4349,7 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, } static inline void l2cap_sig_channel(struct l2cap_conn *conn, - struct sk_buff *skb) + struct sk_buff *skb) { u8 *data = skb->data; int len = skb->len; @@ -4343,7 +4366,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, cmd_len = le16_to_cpu(cmd.len); - BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident); + BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, + cmd.ident); if (cmd_len > len || !cmd.ident) { BT_DBG("corrupted command"); @@ -4362,7 +4386,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, /* FIXME: Map err to a valid reason */ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); - l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); + l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, + sizeof(rej), &rej); } data += cmd_len; @@ -4427,8 +4452,8 @@ static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) } } -static void append_skb_frag(struct sk_buff *skb, - struct sk_buff *new_frag, struct sk_buff **last_frag) +static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag, + struct sk_buff **last_frag) { /* skb->len reflects data in skb as well as all fragments * skb->data_len reflects only data in fragments @@ -4677,7 +4702,7 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq) if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) { if (__seq_offset(chan, txseq, chan->last_acked_seq) >= - chan->tx_win) { + chan->tx_win) { /* See notes below regarding "double poll" and * invalid packets. */ @@ -4718,8 +4743,7 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq) } if (__seq_offset(chan, txseq, chan->last_acked_seq) < - __seq_offset(chan, chan->expected_tx_seq, - chan->last_acked_seq)){ + __seq_offset(chan, chan->expected_tx_seq, chan->last_acked_seq)) { BT_DBG("Duplicate - expected_tx_seq later than txseq"); return L2CAP_TXSEQ_DUPLICATE; } @@ -5479,7 +5503,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) } if (!status && (chan->state == BT_CONNECTED || - chan->state == BT_CONFIG)) { + chan->state == BT_CONFIG)) { struct sock *sk = chan->sk; clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); @@ -5530,7 +5554,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) rsp.result = cpu_to_le16(res); rsp.status = cpu_to_le16(stat); l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); + sizeof(rsp), &rsp); if (!test_bit(CONF_REQ_SENT, &chan->conf_state) && res == L2CAP_CR_SUCCESS) { @@ -5596,7 +5620,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) if (skb->len > len) { BT_ERR("Frame is too long (len %d, expected len %d)", - skb->len, len); + skb->len, len); l2cap_conn_unreliable(conn, ECOMM); goto drop; } @@ -5607,7 +5631,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) goto drop; skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len = len - skb->len; } else { BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); @@ -5620,7 +5644,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) if (skb->len > conn->rx_len) { BT_ERR("Fragment is too long (len %d, expected %d)", - skb->len, conn->rx_len); + skb->len, conn->rx_len); kfree_skb(conn->rx_skb); conn->rx_skb = NULL; conn->rx_len = 0; @@ -5629,7 +5653,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) } skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); + skb->len); conn->rx_len -= skb->len; if (!conn->rx_len) { @@ -5688,8 +5712,8 @@ int __init l2cap_init(void) return err; if (bt_debugfs) { - l2cap_debugfs = debugfs_create_file("l2cap", 0444, - bt_debugfs, NULL, &l2cap_debugfs_fops); + l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, + NULL, &l2cap_debugfs_fops); if (!l2cap_debugfs) BT_ERR("Failed to create L2CAP debug file"); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index ed2dfc942c3c..f95fc7ed0a09 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -40,7 +40,8 @@ static struct bt_sock_list l2cap_sk_list = { static const struct proto_ops l2cap_sock_ops; static void l2cap_sock_init(struct sock *sk, struct sock *parent); -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, + int proto, gfp_t prio); static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) { @@ -106,7 +107,8 @@ done: return err; } -static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) +static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, + int alen, int flags) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -134,7 +136,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al lock_sock(sk); err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); + sock_sndtimeo(sk, flags & O_NONBLOCK)); release_sock(sk); @@ -185,7 +187,8 @@ done: return err; } -static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) +static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, + int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; @@ -241,7 +244,8 @@ done: return err; } -static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) +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; @@ -265,7 +269,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l return 0; } -static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) +static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, + char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -308,7 +313,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; case BT_SECURITY_HIGH: opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | - L2CAP_LM_SECURE; + L2CAP_LM_SECURE; break; default: opt = 0; @@ -352,7 +357,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us return err; } -static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) +static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -376,7 +382,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -411,14 +417,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch case BT_FLUSHABLE: if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), - (u32 __user *) optval)) + (u32 __user *) optval)) err = -EFAULT; break; case BT_POWER: if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { + && sk->sk_type != SOCK_RAW) { err = -EINVAL; break; } @@ -466,7 +472,8 @@ static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu) return true; } -static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) +static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, + char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -565,7 +572,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us return err; } -static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) +static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -588,7 +596,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch switch (optname) { case BT_SECURITY: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -602,7 +610,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } if (sec.level < BT_SECURITY_LOW || - sec.level > BT_SECURITY_HIGH) { + sec.level > BT_SECURITY_HIGH) { err = -EINVAL; break; } @@ -628,7 +636,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch /* or for ACL link */ } else if ((sk->sk_state == BT_CONNECT2 && - test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) || + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) || sk->sk_state == BT_CONNECTED) { if (!l2cap_chan_check_security(chan)) set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); @@ -685,7 +693,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch case BT_POWER: if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { + chan->chan_type != L2CAP_CHAN_RAW) { err = -EINVAL; break; } @@ -721,7 +729,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } if (chan->mode != L2CAP_MODE_ERTM && - chan->mode != L2CAP_MODE_STREAMING) { + chan->mode != L2CAP_MODE_STREAMING) { err = -EOPNOTSUPP; break; } @@ -738,7 +746,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch return err; } -static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) +static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; struct l2cap_chan *chan = l2cap_pi(sk)->chan; @@ -763,7 +772,8 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return err; } -static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) +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); @@ -867,7 +877,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); + sk->sk_lingertime); } if (!err && sk->sk_err) @@ -931,7 +941,7 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) } sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC); + GFP_ATOMIC); if (!sk) return NULL; @@ -1161,7 +1171,8 @@ static struct proto l2cap_proto = { .obj_size = sizeof(struct l2cap_pinfo) }; -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) +static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, + int proto, gfp_t prio) { struct sock *sk; struct l2cap_chan *chan; @@ -1206,7 +1217,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, sock->state = SS_UNCONNECTED; if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && - sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) + sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) @@ -1263,7 +1274,8 @@ int __init l2cap_init_sockets(void) goto error; } - err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, NULL); + err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, + NULL); if (err < 0) { BT_ERR("Failed to create L2CAP proc file"); bt_sock_unregister(BTPROTO_L2CAP); -- cgit v1.2.3 From 8bcde1f2ab732a7d7db1de854dcc0747ffecb7c2 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Mon, 28 May 2012 19:18:14 -0300 Subject: Bluetooth: Remove GFP_ATOMIC usage from l2cap_core.c Since we change the Bluetooth core to run in process context we don't need to use GFP_ATOMIC in many of places we were using it. The we just replace by GFP_KERNEL. Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a032741c96e6..207b4a868485 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1384,7 +1384,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) if (!hchan) return NULL; - conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); + conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL); if (!conn) { hci_chan_del(hchan); return NULL; @@ -1836,9 +1836,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) /* Cloned sk_buffs are read-only, so we need a * writeable copy */ - tx_skb = skb_copy(skb, GFP_ATOMIC); + tx_skb = skb_copy(skb, GFP_KERNEL); } else { - tx_skb = skb_clone(skb, GFP_ATOMIC); + tx_skb = skb_clone(skb, GFP_KERNEL); } if (!tx_skb) { @@ -2584,7 +2584,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) /* Don't send frame to the socket it came from */ if (skb->sk == sk) continue; - nskb = skb_clone(skb, GFP_ATOMIC); + nskb = skb_clone(skb, GFP_KERNEL); if (!nskb) continue; @@ -2610,7 +2610,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; count = min_t(unsigned int, conn->mtu, len); - skb = bt_skb_alloc(count, GFP_ATOMIC); + skb = bt_skb_alloc(count, GFP_KERNEL); if (!skb) return NULL; @@ -2640,7 +2640,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, while (len) { count = min_t(unsigned int, conn->mtu, len); - *frag = bt_skb_alloc(count, GFP_ATOMIC); + *frag = bt_skb_alloc(count, GFP_KERNEL); if (!*frag) goto fail; @@ -5626,7 +5626,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) } /* Allocate skb for the complete frame (with header) */ - conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC); + conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL); if (!conn->rx_skb) goto drop; -- cgit v1.2.3 From 1d8b1fd55aa1dfa9edd74ebb467db9358fa56f8a Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Sat, 6 Oct 2012 11:34:52 +0100 Subject: Bluetooth: use l2cap_chan_set_err() l2cap_conn_unreliable() doesn't take the sk lock, so we need to take it using l2cap_chan_set_err(). Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 207b4a868485..bca3dd91bcbc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1299,7 +1299,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) list_for_each_entry(chan, &conn->chan_l, list) { if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) - __l2cap_chan_set_err(chan, err); + l2cap_chan_set_err(chan, err); } mutex_unlock(&conn->chan_lock); -- cgit v1.2.3 From d117773ce5c73eaa51f3283f89802f4491b27b2e Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Sat, 6 Oct 2012 11:47:38 +0100 Subject: Bluetooth: Use locked l2cap_state_change() No one was protecting the state set in l2cap_send_disconn_req() Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bca3dd91bcbc..7c453d6f7354 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1062,7 +1062,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, } if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) { - __l2cap_state_change(chan, BT_DISCONN); + l2cap_state_change(chan, BT_DISCONN); return; } -- cgit v1.2.3 From b699ec0d99825713d30a057c432d0e29f07c5490 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Sat, 6 Oct 2012 11:51:54 +0100 Subject: Bluetooth: Call ops->teardown() without checking for NULL Users that don't implement teardown() should use l2cap_chan_no_teardown() Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7c453d6f7354..2fb37dee7754 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -542,8 +542,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) hci_conn_put(conn->hcon); } - if (chan->ops->teardown) - chan->ops->teardown(chan, err); + chan->ops->teardown(chan, err); if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) return; @@ -582,8 +581,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) switch (chan->state) { case BT_LISTEN: - if (chan->ops->teardown) - chan->ops->teardown(chan, 0); + chan->ops->teardown(chan, 0); break; case BT_CONNECTED: @@ -625,8 +623,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) break; default: - if (chan->ops->teardown) - chan->ops->teardown(chan, 0); + chan->ops->teardown(chan, 0); break; } } -- cgit v1.2.3 From 644912e18ac1d27f57f6673b0236b568ff750fd1 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 12 Oct 2012 19:35:23 +0800 Subject: Bluetooth: Move bt_accept_enqueue() to l2cap_sock.c This is part of the move the parent socket usage to l2cap_sock.c The change is safe when it comes to locking, bt_accept_enqueue() is still protected by the parent socket lock inside the l2cap_sock_new_connection_cb() code. Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 ---- net/bluetooth/l2cap_sock.c | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2fb37dee7754..9d84050bed2c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1230,8 +1230,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) bacpy(&bt_sk(sk)->src, conn->src); bacpy(&bt_sk(sk)->dst, conn->dst); - bt_accept_enqueue(parent, sk); - l2cap_chan_add(conn, chan); l2cap_chan_ready(chan); @@ -3448,8 +3446,6 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, chan->psm = psm; chan->dcid = scid; - bt_accept_enqueue(parent, sk); - __l2cap_chan_add(conn, chan); dcid = chan->scid; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index f95fc7ed0a09..d5093b853b05 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -949,6 +949,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) l2cap_sock_init(sk, parent); + bt_accept_enqueue(parent, sk); + return l2cap_pi(sk)->chan; } -- cgit v1.2.3 From 2dc4e5105f012bda7eef2f459ed3d5299ded9672 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 12 Oct 2012 19:35:24 +0800 Subject: Bluetooth: Add chan->ops->defer() When DEFER_SETUP is set defer() will trigger an authorization request to the userspace. l2cap_chan_no_defer() is meant to be used when one does not want to support DEFER_SETUP (A2MP for example). Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 5 +++++ net/bluetooth/a2mp.c | 1 + net/bluetooth/l2cap_core.c | 10 +++------- net/bluetooth/l2cap_sock.c | 10 ++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index caab98c45121..6e23afdf65c1 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -541,6 +541,7 @@ struct l2cap_ops { void (*state_change) (struct l2cap_chan *chan, int state); void (*ready) (struct l2cap_chan *chan); + void (*defer) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long len, int nb); }; @@ -748,6 +749,10 @@ static inline void l2cap_chan_no_ready(struct l2cap_chan *chan) { } +static inline void l2cap_chan_no_defer(struct l2cap_chan *chan) +{ +} + extern bool disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 3ff4dc928bf5..7bf9a10d8e46 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -699,6 +699,7 @@ static struct l2cap_ops a2mp_chan_ops = { .new_connection = l2cap_chan_no_new_connection, .teardown = l2cap_chan_no_teardown, .ready = l2cap_chan_no_ready, + .defer = l2cap_chan_no_defer, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9d84050bed2c..314d95580d73 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1120,11 +1120,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn) lock_sock(sk); if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - struct sock *parent = bt_sk(sk)->parent; rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND); - if (parent) - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); @@ -3460,7 +3458,7 @@ static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, __l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; @@ -5523,11 +5521,9 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (!status) { if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - struct sock *parent = bt_sk(sk)->parent; res = L2CAP_CR_PEND; stat = L2CAP_CS_AUTHOR_PEND; - if (parent) - parent->sk_data_ready(parent, 0); + chan->ops->defer(chan); } else { __l2cap_state_change(chan, BT_CONFIG); res = L2CAP_CR_SUCCESS; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index d5093b853b05..5fae2bd879a1 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1081,6 +1081,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan) release_sock(sk); } +static void l2cap_sock_defer_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + struct sock *parent = bt_sk(sk)->parent; + + if (parent) + parent->sk_data_ready(parent, 0); +} + static struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, @@ -1089,6 +1098,7 @@ static struct l2cap_ops l2cap_chan_ops = { .teardown = l2cap_sock_teardown_cb, .state_change = l2cap_sock_state_change_cb, .ready = l2cap_sock_ready_cb, + .defer = l2cap_sock_defer_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; -- cgit v1.2.3 From 300229f962b36e292524b26c86962d7339010b44 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 12 Oct 2012 19:40:40 +0800 Subject: Bluetooth: Rename __l2cap_connect() to l2cap_connect() Use of "__" usually means we need to call the function with a lock held, which is not the case here. Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 314d95580d73..fca407e17f16 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3391,8 +3391,8 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } -static void __l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, - u8 *data, u8 rsp_code, u8 amp_id) +static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, + u8 *data, u8 rsp_code, u8 amp_id) { struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; @@ -3512,7 +3512,7 @@ sendresp: static int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { - __l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); + l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); return 0; } -- cgit v1.2.3 From 204a6e54280d53e6990e536998fbf8dfba41ecd3 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 15 Oct 2012 11:58:39 +0300 Subject: Bluetooth: AMP: Use Loglink handle in ACL Handle field For AMP HCI controller use Logical Link handle in HCI ACL Handle field. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2e72c410fb47..0ec776a4ee09 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2162,7 +2162,18 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, skb->data_len = 0; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); + + switch (hdev->dev_type) { + case HCI_BREDR: + hci_add_acl_hdr(skb, conn->handle, flags); + break; + case HCI_AMP: + hci_add_acl_hdr(skb, chan->handle, flags); + break; + default: + BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type); + return; + } list = skb_shinfo(skb)->frag_list; if (!list) { -- cgit v1.2.3 From d73a098804b4d1d254b1caf1d114e5b707dee060 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 15 Oct 2012 11:58:40 +0300 Subject: Bluetooth: AMP: Handle complete frames in l2cap Check flags type in switch statement and handle new frame type ACL_COMPLETE used for High Speed data over AMP. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 1 + net/bluetooth/l2cap_core.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 77b6a197a6f6..88cbbda61027 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -198,6 +198,7 @@ enum { #define ACL_START_NO_FLUSH 0x00 #define ACL_CONT 0x01 #define ACL_START 0x02 +#define ACL_COMPLETE 0x03 #define ACL_ACTIVE_BCAST 0x04 #define ACL_PICO_BCAST 0x08 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fca407e17f16..8faa3121bb44 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5568,6 +5568,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) { struct l2cap_conn *conn = hcon->l2cap_data; + struct l2cap_hdr *hdr; + int len; if (!conn) conn = l2cap_conn_add(hcon, 0); @@ -5577,10 +5579,10 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - if (!(flags & ACL_CONT)) { - struct l2cap_hdr *hdr; - int len; - + switch (flags) { + case ACL_START: + case ACL_START_NO_FLUSH: + case ACL_COMPLETE: if (conn->rx_len) { BT_ERR("Unexpected start frame (len %d)", skb->len); kfree_skb(conn->rx_skb); @@ -5622,7 +5624,9 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len = len - skb->len; - } else { + break; + + case ACL_CONT: BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); if (!conn->rx_len) { @@ -5650,6 +5654,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) l2cap_recv_frame(conn, conn->rx_skb); conn->rx_skb = NULL; } + break; } drop: -- cgit v1.2.3 From 1d13a254e14565e6a2d91f7552203406a30d1ee2 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 15 Oct 2012 11:58:41 +0300 Subject: Bluetooth: AMP: Drop packets when no l2cap conn exist High Speed hci_conn should always have l2cap_conn associated with it. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8faa3121bb44..99176730bf2c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5571,6 +5571,10 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) struct l2cap_hdr *hdr; int len; + /* For AMP controller do not create l2cap conn */ + if (!conn && hcon->hdev->dev_type != HCI_BREDR) + goto drop; + if (!conn) conn = l2cap_conn_add(hcon, 0); -- cgit v1.2.3 From 79de886d9c40be1d82d414ccc5640268c705af39 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 15 Oct 2012 11:58:42 +0300 Subject: Bluetooth: Send EFS Conf Rsp only for BR/EDR chan Do not send EFS Configuration Response for High Speed channel yet. It will be sent after receiving Logical Link Complete event. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 99176730bf2c..603742fc17a7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3709,7 +3709,11 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, /* check compatibility */ - l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); + /* Send rsp for BR/EDR channel */ + if (!chan->ctrl_id) + l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); + else + chan->ident = cmd->ident; } unlock: @@ -3758,7 +3762,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, /* check compatibility */ - l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, 0); + if (!chan->ctrl_id) + l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, + 0); + else + chan->ident = cmd->ident; } goto done; -- cgit v1.2.3 From 56f6098441adb9487f6e0439429fc536afcf9e71 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 15 Oct 2012 11:58:44 +0300 Subject: Bluetooth: Zero bredr pointer when chan is deleted If BREDR L2CAP chan is deleted and this chan is the channel through which High Speed traffic is routed to AMP then zero pointer to the chan in amp_mgr to prevent accessing it. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 603742fc17a7..f873619fdcfd 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -531,6 +531,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) BT_DBG("chan %p, conn %p, err %d", chan, conn, err); if (conn) { + struct amp_mgr *mgr = conn->hcon->amp_mgr; /* Delete from channel list */ list_del(&chan->list); @@ -540,6 +541,9 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP) hci_conn_put(conn->hcon); + + if (mgr && mgr->bredr_chan == chan) + mgr->bredr_chan = NULL; } chan->ops->teardown(chan, err); -- cgit v1.2.3 From f706adfeade767d2194c9f39c0f75e944b0bdd23 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 18 Oct 2012 13:16:19 +0300 Subject: Bluetooth: AMP: Get amp_mgr reference in HS hci_conn When assigning amp_mgr in hci_conn (type AMP_LINK) get also reference. In hci_conn_del those references would be put for both conn types AMP_LINK and ACL_LINK associated with amp_mgr. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/a2mp.h | 2 +- net/bluetooth/a2mp.c | 4 +++- net/bluetooth/amp.c | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index e776ab2dc572..42f21766c538 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h @@ -133,7 +133,7 @@ struct a2mp_physlink_rsp { extern struct list_head amp_mgr_list; extern struct mutex amp_mgr_list_lock; -void amp_mgr_get(struct amp_mgr *mgr); +struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr); int amp_mgr_put(struct amp_mgr *mgr); u8 __next_ident(struct amp_mgr *mgr); struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 7bf9a10d8e46..d5136cfb57e2 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -751,11 +751,13 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) } /* AMP Manager functions */ -void amp_mgr_get(struct amp_mgr *mgr) +struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr) { BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount)); kref_get(&mgr->kref); + + return mgr; } static void amp_mgr_destroy(struct kref *kref) diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 59da0f15818e..231d7ef53ecb 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -123,7 +123,7 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, hcon->attempt++; hcon->handle = __next_handle(mgr); hcon->remote_id = remote_id; - hcon->amp_mgr = mgr; + hcon->amp_mgr = amp_mgr_get(mgr); hcon->out = out; return hcon; -- cgit v1.2.3 From 08333283a7347c33589f31c9b1d1b7a4f3c3f7a3 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:06 -0700 Subject: Bluetooth: Add new l2cap_chan struct members for high speed channels An L2CAP channel using high speed continues to be associated with a BR/EDR l2cap_conn, while also tracking an additional hci_conn (representing a physical link on a high speed controller) and hci_chan (representing a logical link). There may only be one physical link between two high speed controllers. Each physical link may contain several logical links, with each logical link representing a channel with specific quality of service. During a channel move, the destination channel id, current move state, and role (initiator vs. responder) are tracked and used by the channel move state machine. The ident value associated with a move request must also be stored in order to use it in later move responses. The active channel is stored in local_amp_id. Signed-off-by: Mat Martineau Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 29 +++++++++++++++++++++++++++++ net/bluetooth/l2cap_core.c | 5 +++++ 2 files changed, 34 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6e23afdf65c1..6d3615eed97c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -434,6 +434,8 @@ struct l2cap_chan { struct sock *sk; struct l2cap_conn *conn; + struct hci_conn *hs_hcon; + struct hci_chan *hs_hchan; struct kref kref; __u8 state; @@ -477,6 +479,11 @@ struct l2cap_chan { unsigned long conn_state; unsigned long flags; + __u8 local_amp_id; + __u8 move_id; + __u8 move_state; + __u8 move_role; + __u16 next_tx_seq; __u16 expected_ack_seq; __u16 expected_tx_seq; @@ -644,6 +651,9 @@ enum { enum { L2CAP_RX_STATE_RECV, L2CAP_RX_STATE_SREJ_SENT, + L2CAP_RX_STATE_MOVE, + L2CAP_RX_STATE_WAIT_P, + L2CAP_RX_STATE_WAIT_F, }; enum { @@ -674,6 +684,25 @@ enum { L2CAP_EV_RECV_FRAME, }; +enum { + L2CAP_MOVE_ROLE_NONE, + L2CAP_MOVE_ROLE_INITIATOR, + L2CAP_MOVE_ROLE_RESPONDER, +}; + +enum { + L2CAP_MOVE_STABLE, + L2CAP_MOVE_WAIT_REQ, + L2CAP_MOVE_WAIT_RSP, + L2CAP_MOVE_WAIT_RSP_SUCCESS, + L2CAP_MOVE_WAIT_CONFIRM, + L2CAP_MOVE_WAIT_CONFIRM_RSP, + L2CAP_MOVE_WAIT_LOGICAL_COMP, + L2CAP_MOVE_WAIT_LOGICAL_CFM, + L2CAP_MOVE_WAIT_LOCAL_BUSY, + L2CAP_MOVE_WAIT_PREPARE, +}; + void l2cap_chan_hold(struct l2cap_chan *c); void l2cap_chan_put(struct l2cap_chan *c); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 08efc256c931..c1b169f3ae0d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2788,6 +2788,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan) skb_queue_head_init(&chan->tx_q); + chan->local_amp_id = 0; + chan->move_id = 0; + chan->move_state = L2CAP_MOVE_STABLE; + chan->move_role = L2CAP_MOVE_ROLE_NONE; + if (chan->mode != L2CAP_MODE_ERTM) return 0; -- cgit v1.2.3 From 1700915fef115b13c43fe3974d0dbb619e6a187d Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:07 -0700 Subject: Bluetooth: Add L2CAP create channel request handling The L2CAP create channel request is very similar to an L2CAP connect request, but it has an additional parameter for the controller ID. If the controller id is 0, the channel is set up on the BR/EDR controller (just like a connect request). Using a valid high speed controller ID will cause the channel to be initially created on that high speed controller. While the L2CAP data will be initially routed over the AMP controller, the L2CAP fixed signaling channel only uses BR/EDR. When a create channel request is received for a high speed controller, a pending response is always sent first. After the high speed physical and logical links are complete a success response will be sent. Signed-off-by: Mat Martineau Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 63 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 15 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c1b169f3ae0d..2b3eef706899 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3400,8 +3400,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } -static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, - u8 *data, u8 rsp_code, u8 amp_id) +static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u8 *data, u8 rsp_code, u8 amp_id) { struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; @@ -3452,6 +3453,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, bacpy(&bt_sk(sk)->dst, conn->dst); chan->psm = psm; chan->dcid = scid; + chan->local_amp_id = amp_id; __l2cap_chan_add(conn, chan); @@ -3469,8 +3471,17 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, status = L2CAP_CS_AUTHOR_PEND; chan->ops->defer(chan); } else { - __l2cap_state_change(chan, BT_CONFIG); - result = L2CAP_CR_SUCCESS; + /* Force pending result for AMP controllers. + * The connection will succeed after the + * physical link is up. + */ + if (amp_id) { + __l2cap_state_change(chan, BT_CONNECT2); + result = L2CAP_CR_PEND; + } else { + __l2cap_state_change(chan, BT_CONFIG); + result = L2CAP_CR_SUCCESS; + } status = L2CAP_CS_NO_INFO; } } else { @@ -3516,6 +3527,8 @@ sendresp: l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } + + return chan; } static int l2cap_connect_req(struct l2cap_conn *conn, @@ -4028,12 +4041,12 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, return 0; } -static inline int l2cap_create_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - u16 cmd_len, void *data) +static int l2cap_create_channel_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_create_chan_req *req = data; - struct l2cap_create_chan_rsp rsp; + struct l2cap_chan *chan; u16 psm, scid; if (cmd_len != sizeof(*req)) @@ -4047,14 +4060,34 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id); - /* Placeholder: Always reject */ - rsp.dcid = 0; - rsp.scid = cpu_to_le16(scid); - rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM); - rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); + if (req->amp_id) { + struct hci_dev *hdev; - l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, - sizeof(rsp), &rsp); + /* Validate AMP controller id */ + hdev = hci_dev_get(req->amp_id); + if (!hdev || hdev->dev_type != HCI_AMP || + !test_bit(HCI_UP, &hdev->flags)) { + struct l2cap_create_chan_rsp rsp; + + rsp.dcid = 0; + rsp.scid = cpu_to_le16(scid); + rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP); + rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, + sizeof(rsp), &rsp); + + if (hdev) + hci_dev_put(hdev); + + return 0; + } + + hci_dev_put(hdev); + } + + chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, + req->amp_id); return 0; } -- cgit v1.2.3 From 5909cf30f380d13bb59e81e4cb17c3714cb94e68 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:08 -0700 Subject: Bluetooth: Remove unnecessary intermediate function Resolves a conflict resolution issue in "Bluetooth: Fix L2CAP coding style". The remaining connect and create channel response handler is renamed to better reflect its use for both response types. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2b3eef706899..31750945298b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3538,7 +3538,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn, return 0; } -static inline int l2cap_connect_rsp(struct l2cap_conn *conn, +static int l2cap_connect_create_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; @@ -4092,15 +4092,6 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, return 0; } -static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - void *data) -{ - BT_DBG("conn %p", conn); - - return l2cap_connect_rsp(conn, cmd, data); -} - static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, u16 icid, u16 result) { @@ -4307,7 +4298,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, case L2CAP_CONN_RSP: case L2CAP_CREATE_CHAN_RSP: - err = l2cap_connect_rsp(conn, cmd, data); + err = l2cap_connect_create_rsp(conn, cmd, data); break; case L2CAP_CONF_REQ: -- cgit v1.2.3 From b1a130b7d372c5ccc2001d4ee08928b5324f0a76 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:09 -0700 Subject: Bluetooth: Lookup channel structure based on DCID Processing a move channel request involves getting the channel structure using the destination channel ID. Previous code could only look up using the source channel ID. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 31750945298b..03daae8ab269 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -100,6 +100,23 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, return c; } +/* Find channel with given DCID. + * Returns locked channel. + */ +static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn, + u16 cid) +{ + struct l2cap_chan *c; + + mutex_lock(&conn->chan_lock); + c = __l2cap_get_chan_by_dcid(conn, cid); + if (c) + l2cap_chan_lock(c); + mutex_unlock(&conn->chan_lock); + + return c; +} + static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) { -- cgit v1.2.3 From 02b0fbb92dbb0e3c50f1c955547444e3997c80e3 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:10 -0700 Subject: Bluetooth: Channel move request handling On receipt of a channel move request, the request must be validated based on the L2CAP mode, connection state, and controller capabilities. ERTM channels must have their state machines cleared and transmission paused while the channel move takes place. If the channel is being moved to an AMP controller then an AMP physical link must be prepared. Moving the channel back to BR/EDR proceeds immediately. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 113 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 03daae8ab269..24729f54bfd0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -735,6 +735,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, hci_send_acl(conn->hchan, skb, flags); } +static bool __chan_is_moving(struct l2cap_chan *chan) +{ + return chan->move_state != L2CAP_MOVE_STABLE && + chan->move_state != L2CAP_MOVE_WAIT_PREPARE; +} + static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) { struct hci_conn *hcon = chan->conn->hcon; @@ -996,6 +1002,41 @@ void l2cap_send_conn_req(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } +static void l2cap_move_setup(struct l2cap_chan *chan) +{ + struct sk_buff *skb; + + BT_DBG("chan %p", chan); + + if (chan->mode != L2CAP_MODE_ERTM) + return; + + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); + + chan->retry_count = 0; + skb_queue_walk(&chan->tx_q, skb) { + if (bt_cb(skb)->control.retries) + bt_cb(skb)->control.retries = 1; + else + break; + } + + chan->expected_tx_seq = chan->buffer_seq; + + clear_bit(CONN_REJ_ACT, &chan->conn_state); + clear_bit(CONN_SREJ_ACT, &chan->conn_state); + l2cap_seq_list_clear(&chan->retrans_list); + l2cap_seq_list_clear(&chan->srej_list); + skb_queue_purge(&chan->srej_q); + + chan->tx_state = L2CAP_TX_STATE_XMIT; + chan->rx_state = L2CAP_RX_STATE_MOVE; + + set_bit(CONN_REMOTE_BUSY, &chan->conn_state); +} + static void l2cap_chan_ready(struct l2cap_chan *chan) { /* This clears all conf flags, including CONF_NOT_COMPLETE */ @@ -4157,6 +4198,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, u16 cmd_len, void *data) { struct l2cap_move_chan_req *req = data; + struct l2cap_chan *chan; u16 icid = 0; u16 result = L2CAP_MR_NOT_ALLOWED; @@ -4170,9 +4212,78 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, if (!enable_hs) return -EINVAL; - /* Placeholder: Always refuse */ + chan = l2cap_get_chan_by_dcid(conn, icid); + if (!chan) { + l2cap_send_move_chan_rsp(conn, cmd->ident, icid, + L2CAP_MR_NOT_ALLOWED); + return 0; + } + + if (chan->scid < L2CAP_CID_DYN_START || + chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY || + (chan->mode != L2CAP_MODE_ERTM && + chan->mode != L2CAP_MODE_STREAMING)) { + result = L2CAP_MR_NOT_ALLOWED; + goto send_move_response; + } + + if (chan->local_amp_id == req->dest_amp_id) { + result = L2CAP_MR_SAME_ID; + goto send_move_response; + } + + if (req->dest_amp_id) { + struct hci_dev *hdev; + hdev = hci_dev_get(req->dest_amp_id); + if (!hdev || hdev->dev_type != HCI_AMP || + !test_bit(HCI_UP, &hdev->flags)) { + if (hdev) + hci_dev_put(hdev); + + result = L2CAP_MR_BAD_ID; + goto send_move_response; + } + hci_dev_put(hdev); + } + + /* Detect a move collision. Only send a collision response + * if this side has "lost", otherwise proceed with the move. + * The winner has the larger bd_addr. + */ + if ((__chan_is_moving(chan) || + chan->move_role != L2CAP_MOVE_ROLE_NONE) && + bacmp(conn->src, conn->dst) > 0) { + result = L2CAP_MR_COLLISION; + goto send_move_response; + } + + chan->ident = cmd->ident; + chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; + l2cap_move_setup(chan); + chan->move_id = req->dest_amp_id; + icid = chan->dcid; + + if (!req->dest_amp_id) { + /* Moving to BR/EDR */ + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { + chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; + result = L2CAP_MR_PEND; + } else { + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; + result = L2CAP_MR_SUCCESS; + } + } else { + chan->move_state = L2CAP_MOVE_WAIT_PREPARE; + /* Placeholder - uncomment when amp functions are available */ + /*amp_accept_physical(chan, req->dest_amp_id);*/ + result = L2CAP_MR_PEND; + } + +send_move_response: l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); + l2cap_chan_unlock(chan); + return 0; } -- cgit v1.2.3 From 32b32735ca1439e2ead658dd63234c0c380af8ac Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:11 -0700 Subject: Bluetooth: Add new ERTM receive states for channel move Two new states are required to implement channel moves with the ERTM receive state machine. The "WAIT_P" state is used by a move responder to wait for a "poll" flag after a move is completed (success or failure). "WAIT_F" is similarly used by a move initiator to wait for a "final" flag when the move is completing. In either state, the reqseq value in the poll/final frame tells the state machine exactly which frame should be expected next. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 102 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 24729f54bfd0..b9a91bf3d95e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4713,6 +4713,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, return err; } +static int l2cap_resegment(struct l2cap_chan *chan) +{ + /* Placeholder */ + return 0; +} + void l2cap_chan_busy(struct l2cap_chan *chan, int busy) { u8 event; @@ -5218,6 +5224,96 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, return err; } +static int l2cap_finish_move(struct l2cap_chan *chan) +{ + BT_DBG("chan %p", chan); + + chan->rx_state = L2CAP_RX_STATE_RECV; + + if (chan->hs_hcon) + chan->conn->mtu = chan->hs_hcon->hdev->block_mtu; + else + chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + + return l2cap_resegment(chan); +} + +static int l2cap_rx_state_wait_p(struct l2cap_chan *chan, + struct l2cap_ctrl *control, + struct sk_buff *skb, u8 event) +{ + int err; + + BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb, + event); + + if (!control->poll) + return -EPROTO; + + l2cap_process_reqseq(chan, control->reqseq); + + if (!skb_queue_empty(&chan->tx_q)) + chan->tx_send_head = skb_peek(&chan->tx_q); + else + chan->tx_send_head = NULL; + + /* Rewind next_tx_seq to the point expected + * by the receiver. + */ + chan->next_tx_seq = control->reqseq; + chan->unacked_frames = 0; + + err = l2cap_finish_move(chan); + if (err) + return err; + + set_bit(CONN_SEND_FBIT, &chan->conn_state); + l2cap_send_i_or_rr_or_rnr(chan); + + if (event == L2CAP_EV_RECV_IFRAME) + return -EPROTO; + + return l2cap_rx_state_recv(chan, control, NULL, event); +} + +static int l2cap_rx_state_wait_f(struct l2cap_chan *chan, + struct l2cap_ctrl *control, + struct sk_buff *skb, u8 event) +{ + int err; + + if (!control->final) + return -EPROTO; + + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); + + chan->rx_state = L2CAP_RX_STATE_RECV; + l2cap_process_reqseq(chan, control->reqseq); + + if (!skb_queue_empty(&chan->tx_q)) + chan->tx_send_head = skb_peek(&chan->tx_q); + else + chan->tx_send_head = NULL; + + /* Rewind next_tx_seq to the point expected + * by the receiver. + */ + chan->next_tx_seq = control->reqseq; + chan->unacked_frames = 0; + + if (chan->hs_hcon) + chan->conn->mtu = chan->hs_hcon->hdev->block_mtu; + else + chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + + err = l2cap_resegment(chan); + + if (!err) + err = l2cap_rx_state_recv(chan, control, skb, event); + + return err; +} + static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq) { /* Make sure reqseq is for a packet that has been sent but not acked */ @@ -5244,6 +5340,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, err = l2cap_rx_state_srej_sent(chan, control, skb, event); break; + case L2CAP_RX_STATE_WAIT_P: + err = l2cap_rx_state_wait_p(chan, control, skb, event); + break; + case L2CAP_RX_STATE_WAIT_F: + err = l2cap_rx_state_wait_f(chan, control, skb, event); + break; default: /* shut it down */ break; -- cgit v1.2.3 From 5f3847a4788e7205a6ad2ac363f968c9618074f1 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:12 -0700 Subject: Bluetooth: Add move channel confirm handling After sending a move channel response, a move responder waits for a move channel confirm command. If the received command has a "confirmed" result the move is proceeding, and "unconfirmed" means the move has failed and the channel will not change controllers. Signed-off-by: Mat Martineau Acked-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 58 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b9a91bf3d95e..fef0394add18 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1037,6 +1037,28 @@ static void l2cap_move_setup(struct l2cap_chan *chan) set_bit(CONN_REMOTE_BUSY, &chan->conn_state); } +static void l2cap_move_done(struct l2cap_chan *chan) +{ + u8 move_role = chan->move_role; + BT_DBG("chan %p", chan); + + chan->move_state = L2CAP_MOVE_STABLE; + chan->move_role = L2CAP_MOVE_ROLE_NONE; + + if (chan->mode != L2CAP_MODE_ERTM) + return; + + switch (move_role) { + case L2CAP_MOVE_ROLE_INITIATOR: + l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL); + chan->rx_state = L2CAP_RX_STATE_WAIT_F; + break; + case L2CAP_MOVE_ROLE_RESPONDER: + chan->rx_state = L2CAP_RX_STATE_WAIT_P; + break; + } +} + static void l2cap_chan_ready(struct l2cap_chan *chan) { /* This clears all conf flags, including CONF_NOT_COMPLETE */ @@ -4193,6 +4215,14 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); } +static void __release_logical_link(struct l2cap_chan *chan) +{ + chan->hs_hchan = NULL; + chan->hs_hcon = NULL; + + /* Placeholder - release the logical link */ +} + static inline int l2cap_move_channel_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) @@ -4308,11 +4338,12 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, return 0; } -static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - u16 cmd_len, void *data) +static int l2cap_move_channel_confirm(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_move_chan_cfm *cfm = data; + struct l2cap_chan *chan; u16 icid, result; if (cmd_len != sizeof(*cfm)) @@ -4323,8 +4354,29 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); + chan = l2cap_get_chan_by_dcid(conn, icid); + if (!chan) { + /* Spec requires a response even if the icid was not found */ + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); + return 0; + } + + if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) { + if (result == L2CAP_MC_CONFIRMED) { + chan->local_amp_id = chan->move_id; + if (!chan->local_amp_id) + __release_logical_link(chan); + } else { + chan->move_id = chan->local_amp_id; + } + + l2cap_move_done(chan); + } + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); + l2cap_chan_unlock(chan); + return 0; } -- cgit v1.2.3 From 168df8e57e7c1afce3f86a86ae106f82ff7c18d8 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:13 -0700 Subject: Bluetooth: Add state to hci_chan On an AMP controller, hci_chan maps to a logical link. When a channel is being moved, the logical link may or may not be connected already. The hci_chan->state is used to determine the existance of a useable logical link so the link can be either used or requested. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 1 + 2 files changed, 2 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9fe8e2dec870..00abc5246cbf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -355,6 +355,7 @@ struct hci_chan { struct hci_conn *conn; struct sk_buff_head data_q; unsigned int sent; + __u8 state; }; extern struct list_head hci_dev_list; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index fe646211c61f..6dcf4523df3c 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -959,6 +959,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) chan->conn = conn; skb_queue_head_init(&chan->data_q); + chan->state = BT_CONNECTED; list_add_rcu(&chan->list, &conn->chan_list); -- cgit v1.2.3 From 5b155ef960202b20a5cae43b9e675f4326e2375c Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:14 -0700 Subject: Bluetooth: Move channel response The move response command includes a result code indicating "pending", "success", or "failure" status. A pending result is received when the remote address is still setting up a physical link, and will be followed by success or failure. On success, logical link setup will proceed. On failure, the move is stopped. The receiver of a move channel response must always follow up by sending a move channel confirm command. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 + net/bluetooth/l2cap_core.c | 183 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 170 insertions(+), 15 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6d3615eed97c..b4c3c65c1f58 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -52,6 +52,8 @@ #define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000) #define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000) #define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000) +#define L2CAP_MOVE_TIMEOUT msecs_to_jiffies(4000) +#define L2CAP_MOVE_ERTX_TIMEOUT msecs_to_jiffies(60000) #define L2CAP_A2MP_DEFAULT_MTU 670 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fef0394add18..2277ed504283 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -129,6 +129,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, return NULL; } +static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, + u8 ident) +{ + struct l2cap_chan *c; + + mutex_lock(&conn->chan_lock); + c = __l2cap_get_chan_by_ident(conn, ident); + if (c) + l2cap_chan_lock(c); + mutex_unlock(&conn->chan_lock); + + return c; +} + static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) { struct l2cap_chan *c; @@ -4185,23 +4199,34 @@ static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); } -static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, - struct l2cap_chan *chan, - u16 icid, u16 result) +static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result) { struct l2cap_move_chan_cfm cfm; - u8 ident; - BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); + BT_DBG("chan %p, result 0x%4.4x", chan, result); - ident = l2cap_get_ident(conn); - if (chan) - chan->ident = ident; + chan->ident = l2cap_get_ident(chan->conn); - cfm.icid = cpu_to_le16(icid); + cfm.icid = cpu_to_le16(chan->scid); cfm.result = cpu_to_le16(result); - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM, + sizeof(cfm), &cfm); + + __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT); +} + +static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid) +{ + struct l2cap_move_chan_cfm cfm; + + BT_DBG("conn %p, icid 0x%4.4x", conn, icid); + + cfm.icid = cpu_to_le16(icid); + cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED); + + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM, + sizeof(cfm), &cfm); } static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, @@ -4223,6 +4248,13 @@ static void __release_logical_link(struct l2cap_chan *chan) /* Placeholder - release the logical link */ } +static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, + u8 status) +{ + /* Placeholder */ + return; +} + static inline int l2cap_move_channel_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) @@ -4317,9 +4349,128 @@ send_move_response: return 0; } -static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - u16 cmd_len, void *data) +static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result) +{ + struct l2cap_chan *chan; + struct hci_chan *hchan = NULL; + + chan = l2cap_get_chan_by_scid(conn, icid); + if (!chan) { + l2cap_send_move_chan_cfm_icid(conn, icid); + return; + } + + __clear_chan_timer(chan); + if (result == L2CAP_MR_PEND) + __set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT); + + switch (chan->move_state) { + case L2CAP_MOVE_WAIT_LOGICAL_COMP: + /* Move confirm will be sent when logical link + * is complete. + */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; + break; + case L2CAP_MOVE_WAIT_RSP_SUCCESS: + if (result == L2CAP_MR_PEND) { + break; + } else if (test_bit(CONN_LOCAL_BUSY, + &chan->conn_state)) { + chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; + } else { + /* Logical link is up or moving to BR/EDR, + * proceed with move + */ + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP; + l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); + } + break; + case L2CAP_MOVE_WAIT_RSP: + /* Moving to AMP */ + if (result == L2CAP_MR_SUCCESS) { + /* Remote is ready, send confirm immediately + * after logical link is ready + */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; + } else { + /* Both logical link and move success + * are required to confirm + */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP; + } + + /* Placeholder - get hci_chan for logical link */ + if (!hchan) { + /* Logical link not available */ + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + break; + } + + /* If the logical link is not yet connected, do not + * send confirmation. + */ + if (hchan->state != BT_CONNECTED) + break; + + /* Logical link is already ready to go */ + + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + + if (result == L2CAP_MR_SUCCESS) { + /* Can confirm now */ + l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); + } else { + /* Now only need move success + * to confirm + */ + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; + } + + l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS); + break; + default: + /* Any other amp move state means the move failed. */ + chan->move_id = chan->local_amp_id; + l2cap_move_done(chan); + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + } + + l2cap_chan_unlock(chan); +} + +static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid, + u16 result) +{ + struct l2cap_chan *chan; + + chan = l2cap_get_chan_by_ident(conn, ident); + if (!chan) { + /* Could not locate channel, icid is best guess */ + l2cap_send_move_chan_cfm_icid(conn, icid); + return; + } + + __clear_chan_timer(chan); + + if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) { + if (result == L2CAP_MR_COLLISION) { + chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; + } else { + /* Cleanup - cancel move */ + chan->move_id = chan->local_amp_id; + l2cap_move_done(chan); + } + } + + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + + l2cap_chan_unlock(chan); +} + +static int l2cap_move_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_move_chan_rsp *rsp = data; u16 icid, result; @@ -4332,8 +4483,10 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); - /* Placeholder: Always unconfirmed */ - l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); + if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND) + l2cap_move_continue(conn, icid, result); + else + l2cap_move_fail(conn, cmd->ident, icid, result); return 0; } -- cgit v1.2.3 From 1500109bbc6cc42ec6c8445f1cf04d25fa54a57b Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:15 -0700 Subject: Bluetooth: Add logical link confirm The logical link confirm callback is executed when the AMP controller completes its logical link setup. During a channel move, a newly formed logical link allows a move responder to send a move channel response. A move initiator will send a move channel confirm. A failed logical link will end the channel move and send an appropriate response or confirm command indicating a failure. If the channel is being created on an AMP controller, L2CAP configuration is completed after the logical link is set up. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 134 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 11 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2277ed504283..4d240c23e9dc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3787,6 +3787,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, goto unlock; } + chan->ident = cmd->ident; l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); chan->num_conf_rsp++; @@ -4186,17 +4187,17 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, return 0; } -static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, - u16 icid, u16 result) +static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result) { struct l2cap_move_chan_rsp rsp; - BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); + BT_DBG("chan %p, result 0x%4.4x", chan, result); - rsp.icid = cpu_to_le16(icid); + rsp.icid = cpu_to_le16(chan->dcid); rsp.result = cpu_to_le16(result); - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP, + sizeof(rsp), &rsp); } static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result) @@ -4248,11 +4249,118 @@ static void __release_logical_link(struct l2cap_chan *chan) /* Placeholder - release the logical link */ } +static void l2cap_logical_fail(struct l2cap_chan *chan) +{ + /* Logical link setup failed */ + if (chan->state != BT_CONNECTED) { + /* Create channel failure, disconnect */ + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + return; + } + + switch (chan->move_role) { + case L2CAP_MOVE_ROLE_RESPONDER: + l2cap_move_done(chan); + l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP); + break; + case L2CAP_MOVE_ROLE_INITIATOR: + if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP || + chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) { + /* Remote has only sent pending or + * success responses, clean up + */ + l2cap_move_done(chan); + } + + /* Other amp move states imply that the move + * has already aborted + */ + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + break; + } +} + +static void l2cap_logical_finish_create(struct l2cap_chan *chan, + struct hci_chan *hchan) +{ + struct l2cap_conf_rsp rsp; + u8 code; + + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + + code = l2cap_build_conf_rsp(chan, &rsp, + L2CAP_CONF_SUCCESS, 0); + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CONF_RSP, code, + &rsp); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { + int err = 0; + + set_default_fcs(chan); + + err = l2cap_ertm_init(chan); + if (err < 0) + l2cap_send_disconn_req(chan->conn, chan, -err); + else + l2cap_chan_ready(chan); + } +} + +static void l2cap_logical_finish_move(struct l2cap_chan *chan, + struct hci_chan *hchan) +{ + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + + BT_DBG("move_state %d", chan->move_state); + + switch (chan->move_state) { + case L2CAP_MOVE_WAIT_LOGICAL_COMP: + /* Move confirm will be sent after a success + * response is received + */ + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; + break; + case L2CAP_MOVE_WAIT_LOGICAL_CFM: + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { + chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; + } else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) { + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP; + l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); + } else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) { + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; + l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS); + } + break; + default: + /* Move was not in expected state, free the channel */ + __release_logical_link(chan); + + chan->move_state = L2CAP_MOVE_STABLE; + } +} + +/* Call with chan locked */ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, u8 status) { - /* Placeholder */ - return; + BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status); + + if (status) { + l2cap_logical_fail(chan); + __release_logical_link(chan); + return; + } + + if (chan->state != BT_CONNECTED) { + /* Ignore logical link if channel is on BR/EDR */ + if (chan->local_amp_id) + l2cap_logical_finish_create(chan, hchan); + } else { + l2cap_logical_finish_move(chan, hchan); + } } static inline int l2cap_move_channel_req(struct l2cap_conn *conn, @@ -4260,6 +4368,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, u16 cmd_len, void *data) { struct l2cap_move_chan_req *req = data; + struct l2cap_move_chan_rsp rsp; struct l2cap_chan *chan; u16 icid = 0; u16 result = L2CAP_MR_NOT_ALLOWED; @@ -4276,11 +4385,15 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, chan = l2cap_get_chan_by_dcid(conn, icid); if (!chan) { - l2cap_send_move_chan_rsp(conn, cmd->ident, icid, - L2CAP_MR_NOT_ALLOWED); + rsp.icid = cpu_to_le16(icid); + rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED); + l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP, + sizeof(rsp), &rsp); return 0; } + chan->ident = cmd->ident; + if (chan->scid < L2CAP_CID_DYN_START || chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY || (chan->mode != L2CAP_MODE_ERTM && @@ -4319,7 +4432,6 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, goto send_move_response; } - chan->ident = cmd->ident; chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; l2cap_move_setup(chan); chan->move_id = req->dest_amp_id; @@ -4342,7 +4454,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, } send_move_response: - l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); + l2cap_send_move_chan_rsp(chan, result); l2cap_chan_unlock(chan); -- cgit v1.2.3 From 3fd71a0a438aa5bd43f52f3feec24a4cb3b799d3 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:16 -0700 Subject: Bluetooth: Add move confirm response handling The move confirm response concludes the channel move command sequence. Receipt of this command indicates that data may begin to flow again. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4d240c23e9dc..7663a1e6f1cc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4650,6 +4650,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, u16 cmd_len, void *data) { struct l2cap_move_chan_cfm_rsp *rsp = data; + struct l2cap_chan *chan; u16 icid; if (cmd_len != sizeof(*rsp)) @@ -4659,6 +4660,23 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x", icid); + chan = l2cap_get_chan_by_scid(conn, icid); + if (!chan) + return 0; + + __clear_chan_timer(chan); + + if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) { + chan->local_amp_id = chan->move_id; + + if (!chan->local_amp_id && chan->hs_hchan) + __release_logical_link(chan); + + l2cap_move_done(chan); + } + + l2cap_chan_unlock(chan); + return 0; } -- cgit v1.2.3 From 8eb200bd2f1c772dcb7f108f690ef03b054be04e Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:17 -0700 Subject: Bluetooth: Handle physical link completion Several different actions may be taken when an AMP physical link becomes available. A channel being created on an AMP controller must continue the connection process. A channel being moved needs to either send a move request or a move response. A failed physical link will revert to using a BR/EDR controller if possible. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7663a1e6f1cc..898529d102f2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1016,6 +1016,19 @@ void l2cap_send_conn_req(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } +static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id) +{ + struct l2cap_create_chan_req req; + req.scid = cpu_to_le16(chan->scid); + req.psm = chan->psm; + req.amp_id = amp_id; + + chan->ident = l2cap_get_ident(chan->conn); + + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ, + sizeof(req), &req); +} + static void l2cap_move_setup(struct l2cap_chan *chan) { struct sk_buff *skb; @@ -4187,6 +4200,25 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, return 0; } +static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id) +{ + struct l2cap_move_chan_req req; + u8 ident; + + BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id); + + ident = l2cap_get_ident(chan->conn); + chan->ident = ident; + + req.icid = cpu_to_le16(chan->scid); + req.dest_amp_id = dest_amp_id; + + l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req), + &req); + + __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT); +} + static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result) { struct l2cap_move_chan_rsp rsp; @@ -4363,6 +4395,138 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, } } +static void l2cap_do_create(struct l2cap_chan *chan, int result, + u8 local_amp_id, u8 remote_amp_id) +{ + if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { + struct l2cap_conn_rsp rsp; + char buf[128]; + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); + + /* Incoming channel on AMP */ + if (result == L2CAP_CR_SUCCESS) { + /* Send successful response */ + rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + } else { + /* Send negative response */ + rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM); + rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + } + + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP, + sizeof(rsp), &rsp); + + if (result == L2CAP_CR_SUCCESS) { + __l2cap_state_change(chan, BT_CONFIG); + set_bit(CONF_REQ_SENT, &chan->conf_state); + l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn), + L2CAP_CONF_REQ, + l2cap_build_conf_req(chan, buf), buf); + chan->num_conf_req++; + } + } else { + /* Outgoing channel on AMP */ + if (result == L2CAP_CR_SUCCESS) { + chan->local_amp_id = local_amp_id; + l2cap_send_create_chan_req(chan, remote_amp_id); + } else { + /* Revert to BR/EDR connect */ + l2cap_send_conn_req(chan); + } + } +} + +static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id, + u8 remote_amp_id) +{ + l2cap_move_setup(chan); + chan->move_id = local_amp_id; + chan->move_state = L2CAP_MOVE_WAIT_RSP; + + l2cap_send_move_chan_req(chan, remote_amp_id); +} + +static void l2cap_do_move_respond(struct l2cap_chan *chan, int result) +{ + struct hci_chan *hchan = NULL; + + /* Placeholder - get hci_chan for logical link */ + + if (hchan) { + if (hchan->state == BT_CONNECTED) { + /* Logical link is ready to go */ + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; + l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS); + + l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS); + } else { + /* Wait for logical link to be ready */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; + } + } else { + /* Logical link not available */ + l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED); + } +} + +static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result) +{ + if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) { + u8 rsp_result; + if (result == -EINVAL) + rsp_result = L2CAP_MR_BAD_ID; + else + rsp_result = L2CAP_MR_NOT_ALLOWED; + + l2cap_send_move_chan_rsp(chan, rsp_result); + } + + chan->move_role = L2CAP_MOVE_ROLE_NONE; + chan->move_state = L2CAP_MOVE_STABLE; + + /* Restart data transmission */ + l2cap_ertm_send(chan); +} + +void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id, + u8 remote_amp_id) +{ + BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d", + chan, result, local_amp_id, remote_amp_id); + + l2cap_chan_lock(chan); + + if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) { + l2cap_chan_unlock(chan); + return; + } + + if (chan->state != BT_CONNECTED) { + l2cap_do_create(chan, result, local_amp_id, remote_amp_id); + } else if (result != L2CAP_MR_SUCCESS) { + l2cap_do_move_cancel(chan, result); + } else { + switch (chan->move_role) { + case L2CAP_MOVE_ROLE_INITIATOR: + l2cap_do_move_initiate(chan, local_amp_id, + remote_amp_id); + break; + case L2CAP_MOVE_ROLE_RESPONDER: + l2cap_do_move_respond(chan, result); + break; + default: + l2cap_do_move_cancel(chan, result); + break; + } + } + + l2cap_chan_unlock(chan); +} + static inline int l2cap_move_channel_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -- cgit v1.2.3 From d5f8a75d88ecef3987158a94e8070bdfb46b09bd Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:18 -0700 Subject: Bluetooth: Flag ACL frames as complete for AMP controllers AMP controllers expect to transmit only "complete" ACL frames. These frames have both the "start" and "cont" bits set. AMP does not allow fragmented ACLs. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 898529d102f2..22f3768aa0be 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -763,6 +763,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, skb->priority); + if (chan->hs_hcon && !__chan_is_moving(chan)) { + if (chan->hs_hchan) + hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE); + else + kfree_skb(skb); + + return; + } + if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; -- cgit v1.2.3 From b99e13ade709274104f5c2b8a26dc7d2953fc58e Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:19 -0700 Subject: Bluetooth: Do not send data during channel move Outgoing ERTM data is queued during a channel move. The ERTM state machine is partially reset at the start of a move, and must be resynchronized with the remote state machine at the end of the move. Data is not sent so that there are no state transitions between the partial reset and the resync. Streaming mode frames are dropped during a move. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 22f3768aa0be..4eb3ca84de2f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -947,6 +947,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan, if (!control->sframe) return; + if (__chan_is_moving(chan)) + return; + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) && !control->poll) control->final = 1; @@ -1811,6 +1814,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan, BT_DBG("chan %p, skbs %p", chan, skbs); + if (__chan_is_moving(chan)) + return; + skb_queue_splice_tail_init(skbs, &chan->tx_q); while (!skb_queue_empty(&chan->tx_q)) { @@ -1853,6 +1859,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return 0; + if (__chan_is_moving(chan)) + return 0; + while (chan->tx_send_head && chan->unacked_frames < chan->remote_tx_win && chan->tx_state == L2CAP_TX_STATE_XMIT) { @@ -1918,6 +1927,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return; + if (__chan_is_moving(chan)) + return; + while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) { seq = l2cap_seq_list_pop(&chan->retrans_list); -- cgit v1.2.3 From 36c86c8566cec67924ae6f372d9066cc9e92ad0e Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:20 -0700 Subject: Bluetooth: Configure appropriate timeouts for AMP controllers The L2CAP spec recommends specific retransmit and monitor timeouts for ERTM channels that are on AMP controllers. These timeouts are calculated from the AMP controller's best effort flush timeout. BR/EDR controllers use the default retransmit and monitor timeouts. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 47 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4eb3ca84de2f..6662ee34e754 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2967,6 +2967,44 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; } +static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan, + struct l2cap_conf_rfc *rfc) +{ + if (chan->local_amp_id && chan->hs_hcon) { + u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to; + + /* Class 1 devices have must have ERTM timeouts + * exceeding the Link Supervision Timeout. The + * default Link Supervision Timeout for AMP + * controllers is 10 seconds. + * + * Class 1 devices use 0xffffffff for their + * best-effort flush timeout, so the clamping logic + * will result in a timeout that meets the above + * requirement. ERTM timeouts are 16-bit values, so + * the maximum timeout is 65.535 seconds. + */ + + /* Convert timeout to milliseconds and round */ + ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000); + + /* This is the recommended formula for class 2 devices + * that start ERTM timers when packets are sent to the + * controller. + */ + ertm_to = 3 * ertm_to + 500; + + if (ertm_to > 0xffff) + ertm_to = 0xffff; + + rfc->retrans_timeout = cpu_to_le16((u16) ertm_to); + rfc->monitor_timeout = rfc->retrans_timeout; + } else { + rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); + rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + } +} + static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && @@ -3033,8 +3071,8 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; rfc.max_transmit = chan->max_tx; - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; + + __l2cap_set_ertm_timeouts(chan, &rfc); size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - @@ -3262,10 +3300,7 @@ done: rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; - rfc.retrans_timeout = - __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = - __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + __l2cap_set_ertm_timeouts(chan, &rfc); set_bit(CONF_MODE_DONE, &chan->conf_state); -- cgit v1.2.3 From a549574da39f0a6df68ffdb72dd015d04a8486de Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:21 -0700 Subject: Bluetooth: Ignore BR/EDR packet size constraints when fragmenting for AMP When operating over BR/EDR, ERTM accounts for the maximum over-the-air packet size when setting the PDU size. AMP controllers do not use the same over-the-air packets, so the PDU size should only be based on the HCI MTU of the AMP controller. Signed-off-by: Mat Martineau Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6662ee34e754..ef86ebb92a00 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2272,7 +2272,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, /* PDU size is derived from the HCI MTU */ pdu_len = chan->conn->mtu; - pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD); + /* Constrain PDU size for BR/EDR connections */ + if (!chan->hs_hcon) + pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD); /* Adjust for largest possible L2CAP overhead. */ if (chan->fcs) -- cgit v1.2.3 From e6a3ee6e8aa27d0a38be7ead0c1624041697ffbc Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:22 -0700 Subject: Bluetooth: Do not retransmit data during a channel move Do not retransmit previously-sent data when a "receiver ready" s-frame with the "final" flag is received during a move. The ERTM state machines will resynchronize at the end of a channel move, and the state machine needs to avoid state changes during a move. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ef86ebb92a00..93f1ebbd7502 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5591,8 +5591,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, if (control->final) { clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (!test_and_clear_bit(CONN_REJ_ACT, - &chan->conn_state)) { + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) && + !__chan_is_moving(chan)) { control->final = 0; l2cap_retransmit_all(chan, control); } -- cgit v1.2.3 From 3f7a56c4ff438f4727439cb048034f56320dd228 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 23 Oct 2012 15:24:23 -0700 Subject: Bluetooth: Start channel move when socket option is changed Channel moves are triggered by changes to the BT_CHANNEL_POLICY sockopt when an ERTM or streaming-mode channel is connected. Moves are only started if enable_hs is true. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 19 +++++++++++++++++++ net/bluetooth/l2cap_sock.c | 5 +++++ 3 files changed, 25 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index b4c3c65c1f58..49783e948856 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -809,5 +809,6 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); void l2cap_send_conn_req(struct l2cap_chan *chan); +void l2cap_move_start(struct l2cap_chan *chan); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 93f1ebbd7502..fae0c70e8e10 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4453,6 +4453,25 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, } } +void l2cap_move_start(struct l2cap_chan *chan) +{ + BT_DBG("chan %p", chan); + + if (chan->local_amp_id == HCI_BREDR_ID) { + if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED) + return; + chan->move_role = L2CAP_MOVE_ROLE_INITIATOR; + chan->move_state = L2CAP_MOVE_WAIT_PREPARE; + /* Placeholder - start physical link setup */ + } else { + chan->move_role = L2CAP_MOVE_ROLE_INITIATOR; + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; + chan->move_id = 0; + l2cap_move_setup(chan); + l2cap_send_move_chan_req(chan, 0); + } +} + static void l2cap_do_create(struct l2cap_chan *chan, int result, u8 local_amp_id, u8 remote_amp_id) { diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 89f1472939ec..1bcfb8422fdc 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -736,6 +736,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, } chan->chan_policy = (u8) opt; + + if (sk->sk_state == BT_CONNECTED && + chan->move_role == L2CAP_MOVE_ROLE_NONE) + l2cap_move_start(chan); + break; default: -- cgit v1.2.3 From ea5a5c73a278b24f05022b6f073bf5d196a2b271 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 23 Oct 2012 19:02:16 +0530 Subject: Bluetooth: trivial: Remove newline before EOF Trivial fix. Signed-off-by: Syam Sidhardhan Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 1c11d0dcd863..d3f3f7b1d32c 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -48,4 +48,3 @@ source "net/bluetooth/cmtp/Kconfig" source "net/bluetooth/hidp/Kconfig" source "drivers/bluetooth/Kconfig" - -- cgit v1.2.3 From 2ad8f54bc86809c2a8de3830e3ed275fcc6401ed Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 23 Oct 2012 19:02:18 +0530 Subject: Bluetooth: Replace include linux/module.h with linux/export.h include is the right to go here. Signed-off-by: Syam Sidhardhan Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/cmtp/capi.c | 2 +- net/bluetooth/cmtp/sock.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c index 50f0d135eb8f..a4a9d4b6816c 100644 --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -20,7 +20,7 @@ SOFTWARE IS DISCLAIMED. */ -#include +#include #include #include #include diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c index aacb802d1ee4..1c57482112b6 100644 --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -20,7 +20,7 @@ SOFTWARE IS DISCLAIMED. */ -#include +#include #include #include -- cgit v1.2.3 From e1171e8d9c50c38a9adba72bb23949d9b975335c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Oct 2012 20:57:45 +0300 Subject: Bluetooth: Add initial support for LE-only controllers This patch splits off most the HCI init sequence commands from a fixed set into a conditional one that is sent once the HCI_Read_Local_Features and HCI_Read_Local_Version_Information commands complete. This is necessary since many of the current fixed commands are not allowed for LE-only controllers. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 47 -------------------------------------- net/bluetooth/hci_event.c | 58 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 52 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5a3f941b610f..854202679c49 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -178,48 +178,13 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) static void bredr_init(struct hci_dev *hdev) { - struct hci_cp_delete_stored_link_key cp; - __le16 param; - __u8 flt_type; - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; - /* Mandatory initialization */ - /* Read Local Supported Features */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); /* Read Local Version */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); - - /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); - - /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); - - /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); - - /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); - - /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); - - /* Optional initialization */ - - /* Clear Event Filters */ - flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); - - /* Connection accept timeout ~20 secs */ - param = __constant_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); } static void amp_init(struct hci_dev *hdev) @@ -273,14 +238,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) } } -static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt) -{ - BT_DBG("%s", hdev->name); - - /* Read LE buffer size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); -} - static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) { __u8 scan = opt; @@ -687,10 +644,6 @@ int hci_dev_open(__u16 dev) ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); - if (lmp_host_le_capable(hdev)) - ret = __hci_request(hdev, hci_le_init_req, 0, - HCI_INIT_TIMEOUT); - clear_bit(HCI_INIT, &hdev->flags); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0383635f91fb..f4f0b8bfdee6 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -507,11 +507,13 @@ static void hci_setup_event_mask(struct hci_dev *hdev) if (hdev->hci_ver < BLUETOOTH_VER_1_2) return; - 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 (lmp_bredr_capable(hdev)) { + 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] |= 0x02; /* Inquiry Result with RSSI */ @@ -550,11 +552,57 @@ static void hci_setup_event_mask(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); } +static void bredr_init(struct hci_dev *hdev) +{ + struct hci_cp_delete_stored_link_key cp; + __le16 param; + __u8 flt_type; + + /* Read Buffer Size (ACL mtu, max pkt, etc.) */ + hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); + + /* Read Class of Device */ + hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); + + /* Read Local Name */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); + + /* Read Voice Setting */ + hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); + + /* Clear Event Filters */ + flt_type = HCI_FLT_CLEAR_ALL; + hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); + + /* Connection accept timeout ~20 secs */ + param = __constant_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); +} + +static void le_init(struct hci_dev *hdev) +{ + /* Read LE Buffer Size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); +} + static void hci_setup(struct hci_dev *hdev) { if (hdev->dev_type != HCI_BREDR) return; + /* Read BD Address */ + hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); + + if (lmp_bredr_capable(hdev)) + bredr_init(hdev); + + if (lmp_le_capable(hdev)) + le_init(hdev); + hci_setup_event_mask(hdev); if (hdev->hci_ver > BLUETOOTH_VER_1_1) -- cgit v1.2.3 From 572c7f8429e3c015dd8931b2d3f71b512a7f15f1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Oct 2012 20:57:46 +0300 Subject: Bluetooth: Fix LE MTU reporting for HCIGETDEVINFO This patch fixes the use of le_mtu and le_pkts values in the HCIGETDEVINFO ioctl for LE-only controllers. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 854202679c49..5a3400d8a6e5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -992,10 +992,17 @@ int hci_get_dev_info(void __user *arg) di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); di.flags = hdev->flags; di.pkt_type = hdev->pkt_type; - di.acl_mtu = hdev->acl_mtu; - di.acl_pkts = hdev->acl_pkts; - di.sco_mtu = hdev->sco_mtu; - di.sco_pkts = hdev->sco_pkts; + if (lmp_bredr_capable(hdev)) { + di.acl_mtu = hdev->acl_mtu; + di.acl_pkts = hdev->acl_pkts; + di.sco_mtu = hdev->sco_mtu; + di.sco_pkts = hdev->sco_pkts; + } else { + di.acl_mtu = hdev->le_mtu; + di.acl_pkts = hdev->le_pkts; + di.sco_mtu = 0; + di.sco_pkts = 0; + } di.link_policy = hdev->link_policy; di.link_mode = hdev->link_mode; -- cgit v1.2.3 From e36b04c805e452689d468f9783e5dffa61e38be7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Oct 2012 20:57:47 +0300 Subject: Bluetooth: Add setting of the LE event mask This patch adds setting of the LE event mask to the HCI init procedure for LE-capable controllers. Right now we only set the default mask which is good enough for the events available in the 4.0 core specification. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f4f0b8bfdee6..78f1af52ed81 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -550,6 +550,13 @@ static void hci_setup_event_mask(struct hci_dev *hdev) events[7] |= 0x20; /* LE Meta-Event */ hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); + + if (lmp_le_capable(hdev)) { + memset(events, 0, sizeof(events)); + events[0] = 0x1f; + hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK, + sizeof(events), events); + } } static void bredr_init(struct hci_dev *hdev) @@ -1066,6 +1073,15 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } +static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status); +} + static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; @@ -2489,6 +2505,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_buffer_size(hdev, skb); break; + case HCI_OP_LE_SET_EVENT_MASK: + hci_cc_le_set_event_mask(hdev, skb); + break; + case HCI_OP_USER_CONFIRM_REPLY: hci_cc_user_confirm_reply(hdev, skb); break; -- cgit v1.2.3 From 8fa19098ebc700f14b0f8d0fb957e7748e14c44b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Oct 2012 20:57:49 +0300 Subject: Bluetooth: Read adversiting channel TX power during init sequence This patch adds the reading of the LE advertising channel TX power to the HCI init sequence of LE-capable controllers. This data will be used e.g. for inclusion in the advertising data packets when advertising is enabled. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 6 ++++++ include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_event.c | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 88cbbda61027..348f4bfeaadb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -932,6 +932,12 @@ struct hci_rp_le_read_buffer_size { __u8 le_max_pkt; } __packed; +#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 +struct hci_rp_le_read_adv_tx_power { + __u8 status; + __s8 tx_power; +} __packed; + #define HCI_OP_LE_SET_SCAN_PARAM 0x200b struct hci_cp_le_set_scan_param { __u8 type; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 00abc5246cbf..5ab80b7e8369 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -278,6 +278,8 @@ struct hci_dev { struct work_struct le_scan; struct le_scan_params le_scan_params; + __s8 adv_tx_power; + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 78f1af52ed81..fd5a51ccb8e4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -594,6 +594,9 @@ static void le_init(struct hci_dev *hdev) { /* Read LE Buffer Size */ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + + /* Read LE Advertising Channel TX Power */ + hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); } static void hci_setup(struct hci_dev *hdev) @@ -1073,6 +1076,19 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } +static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_adv_tx_power *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (!rp->status) + hdev->adv_tx_power = rp->tx_power; + + hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status); +} + static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2505,6 +2521,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_buffer_size(hdev, skb); break; + case HCI_OP_LE_READ_ADV_TX_POWER: + hci_cc_le_read_adv_tx_power(hdev, skb); + break; + case HCI_OP_LE_SET_EVENT_MASK: hci_cc_le_set_event_mask(hdev, skb); break; -- cgit v1.2.3 From 4611dfa85ece8a26ff46b099a6d91df733066c73 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 24 Oct 2012 11:18:41 -0200 Subject: Bluetooth: Replace *_init() for *_setup() le_init() and bredr_init() are now called le_setup() and bredr_setup() to avoid duplicates names over the tree even if they are all static. Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index fd5a51ccb8e4..0b9e646d1758 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -559,7 +559,7 @@ static void hci_setup_event_mask(struct hci_dev *hdev) } } -static void bredr_init(struct hci_dev *hdev) +static void bredr_setup(struct hci_dev *hdev) { struct hci_cp_delete_stored_link_key cp; __le16 param; @@ -590,7 +590,7 @@ static void bredr_init(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } -static void le_init(struct hci_dev *hdev) +static void le_setup(struct hci_dev *hdev) { /* Read LE Buffer Size */ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); @@ -608,10 +608,10 @@ static void hci_setup(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); if (lmp_bredr_capable(hdev)) - bredr_init(hdev); + bredr_setup(hdev); if (lmp_le_capable(hdev)) - le_init(hdev); + le_setup(hdev); hci_setup_event_mask(hdev); -- cgit v1.2.3 From 7f0ae647b29ba80d5a1127804ba478e3de8a6b4c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2012 21:11:57 +0300 Subject: Bluetooth: Fix HCI command sending when powering on LE-only adapters This patch makes sure that we don't send BR/EDR-only commands for LE-only adapters when they get powered on. Doing this would just cause command errors. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 399e5024b5bd..e86d08731b4e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2871,6 +2871,21 @@ static void settings_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_free(cmd); } +static int set_bredr_scan(struct hci_dev *hdev) +{ + u8 scan = 0; + + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + scan |= SCAN_PAGE; + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + if (!scan) + return 0; + + return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct cmd_lookup match = { NULL, hdev }; @@ -2882,16 +2897,6 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); if (powered) { - u8 scan = 0; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - scan |= SCAN_PAGE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - if (scan) - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 ssp = 1; @@ -2908,9 +2913,12 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) sizeof(cp), &cp); } - update_class(hdev); - update_name(hdev, hdev->dev_name); - update_eir(hdev); + if (lmp_bredr_capable(hdev)) { + set_bredr_scan(hdev); + update_class(hdev); + update_name(hdev, hdev->dev_name); + update_eir(hdev); + } } else { u8 status = MGMT_STATUS_NOT_POWERED; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); -- cgit v1.2.3 From 33c525c0a37abd136f014f473f5a2efddba58167 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2012 21:11:58 +0300 Subject: Bluetooth: mgmt: Restrict BR/EDR settings to BR/EDR-only adapters This patch makes sure that settings which are specific for BR/EDR capable adapters are not allowed for non-BR/EDR (e.g. LE-only) adapters. Instead, a "not supported" error is returned of such a setting is attempted to be set for a non-BR/EDR adapter. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e86d08731b4e..11cabe7ef3c6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -376,15 +376,15 @@ static u32 get_supported_settings(struct hci_dev *hdev) u32 settings = 0; settings |= MGMT_SETTING_POWERED; - settings |= MGMT_SETTING_CONNECTABLE; - settings |= MGMT_SETTING_FAST_CONNECTABLE; - settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_PAIRABLE; if (lmp_ssp_capable(hdev)) settings |= MGMT_SETTING_SSP; if (lmp_bredr_capable(hdev)) { + settings |= MGMT_SETTING_CONNECTABLE; + settings |= MGMT_SETTING_FAST_CONNECTABLE; + settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; } @@ -867,6 +867,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_NOT_SUPPORTED); + timeout = __le16_to_cpu(cp->timeout); if (!cp->val && timeout > 0) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, @@ -962,6 +966,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_NOT_SUPPORTED); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1060,6 +1068,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_NOT_SUPPORTED); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -2594,6 +2606,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_NOT_SUPPORTED); + if (!hdev_is_powered(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_POWERED); -- cgit v1.2.3 From 53b2caabbe97193e6231dbcc0d48f7e1888fa38e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2012 21:11:59 +0300 Subject: Bluetooth: Fix updating host feature bits for LE When LE has been enabled with the simultaneous BR/EDR & LE parameter set to true we should also update the host features stored in struct hci_dev accordingly. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0b9e646d1758..aa325eec40db 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1287,6 +1287,11 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hdev->host_features[0] |= LMP_HOST_LE; else hdev->host_features[0] &= ~LMP_HOST_LE; + + if (sent->simul) + hdev->host_features[0] |= LMP_HOST_LE_BREDR; + else + hdev->host_features[0] &= ~LMP_HOST_LE_BREDR; } if (test_bit(HCI_MGMT, &hdev->dev_flags) && -- cgit v1.2.3 From 976eb20e61e33dd3e7840bc26bc5d33ab9ca9c5c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2012 21:12:01 +0300 Subject: Bluetooth: Make use feature test macros For better code readability and avoiding simple bugs of checking the wrong byte of the features make use of feature test macros whenever possible. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 26 +++++++++++++------------- net/bluetooth/mgmt.c | 10 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index aa325eec40db..aae80531f8ce 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -460,10 +460,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) static u8 hci_get_inquiry_mode(struct hci_dev *hdev) { - if (hdev->features[6] & LMP_EXT_INQ) + if (lmp_ext_inq_capable(hdev)) return 2; - if (hdev->features[3] & LMP_RSSI_INQ) + if (lmp_inq_rssi_capable(hdev)) return 1; if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && @@ -515,22 +515,22 @@ static void hci_setup_event_mask(struct hci_dev *hdev) events[5] |= 0x10; /* Synchronous Connection Changed */ } - if (hdev->features[3] & LMP_RSSI_INQ) + if (lmp_inq_rssi_capable(hdev)) events[4] |= 0x02; /* Inquiry Result with RSSI */ if (lmp_sniffsubr_capable(hdev)) events[5] |= 0x20; /* Sniff Subrating */ - if (hdev->features[5] & LMP_PAUSE_ENC) + if (lmp_pause_enc_capable(hdev)) events[5] |= 0x80; /* Encryption Key Refresh Complete */ - if (hdev->features[6] & LMP_EXT_INQ) + if (lmp_ext_inq_capable(hdev)) events[5] |= 0x40; /* Extended Inquiry Result */ if (lmp_no_flush_capable(hdev)) events[7] |= 0x01; /* Enhanced Flush Complete */ - if (hdev->features[7] & LMP_LSTO) + if (lmp_lsto_capable(hdev)) events[6] |= 0x80; /* Link Supervision Timeout Changed */ if (lmp_ssp_capable(hdev)) { @@ -633,13 +633,13 @@ static void hci_setup(struct hci_dev *hdev) } } - if (hdev->features[3] & LMP_RSSI_INQ) + if (lmp_inq_rssi_capable(hdev)) hci_setup_inquiry_mode(hdev); - if (hdev->features[7] & LMP_INQ_TX_PWR) + if (lmp_inq_tx_pwr_capable(hdev)) hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); - if (hdev->features[7] & LMP_EXTFEATURES) { + if (lmp_ext_feat_capable(hdev)) { struct hci_cp_read_local_ext_features cp; cp.page = 0x01; @@ -686,11 +686,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev) if (lmp_rswitch_capable(hdev)) link_policy |= HCI_LP_RSWITCH; - if (hdev->features[0] & LMP_HOLD) + if (lmp_hold_capable(hdev)) link_policy |= HCI_LP_HOLD; if (lmp_sniff_capable(hdev)) link_policy |= HCI_LP_SNIFF; - if (hdev->features[1] & LMP_PARK) + if (lmp_park_capable(hdev)) link_policy |= HCI_LP_PARK; cp.policy = cpu_to_le16(link_policy); @@ -780,10 +780,10 @@ static void hci_set_le_support(struct hci_dev *hdev) if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { cp.le = 1; - cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + cp.simul = !!lmp_le_br_capable(hdev); } - if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) + if (cp.le != !!lmp_host_le_capable(hdev)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 11cabe7ef3c6..585654bd250f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -565,7 +565,7 @@ static int update_eir(struct hci_dev *hdev) if (!hdev_is_powered(hdev)) return 0; - if (!(hdev->features[6] & LMP_EXT_INQ)) + if (!lmp_ext_inq_capable(hdev)) return 0; if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) @@ -1225,7 +1225,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } val = !!cp->val; - enabled = !!(hdev->host_features[0] & LMP_HOST_LE); + enabled = !!lmp_host_le_capable(hdev); if (!hdev_is_powered(hdev) || val == enabled) { bool changed = false; @@ -1261,7 +1261,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (val) { hci_cp.le = val; - hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + hci_cp.simul = !!lmp_le_br_capable(hdev); } err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), @@ -2923,7 +2923,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) struct hci_cp_write_le_host_supported cp; cp.le = 1; - cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + cp.simul = !!lmp_le_br_capable(hdev); hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); @@ -3383,7 +3383,7 @@ static int clear_eir(struct hci_dev *hdev) { struct hci_cp_write_eir cp; - if (!(hdev->features[6] & LMP_EXT_INQ)) + if (!lmp_ext_inq_capable(hdev)) return 0; memset(hdev->eir, 0, sizeof(hdev->eir)); -- cgit v1.2.3 From f15504788d7b1613ef2ef0a673cfe250c16a6b0d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 24 Oct 2012 21:12:03 +0300 Subject: Bluetooth: Disallow LE scanning and connecting in peripheral role When an adapter is in the LE peripheral role scanning for other devices or initiating connections to them is not allowed. This patch makes sure that such attempts will result in appropriate error returns. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_conn.c | 3 +++ net/bluetooth/hci_core.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6dcf4523df3c..dc331ceca471 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -502,6 +502,9 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *le; + if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags)) + return ERR_PTR(-ENOTSUPP); + le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); if (!le) { le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5a3400d8a6e5..515d0c394f35 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1577,6 +1577,9 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, BT_DBG("%s", hdev->name); + if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) + return -ENOTSUPP; + if (work_busy(&hdev->le_scan)) return -EINPROGRESS; -- cgit v1.2.3 From 5ed8eb2f6b5710c09bd1adb40aa795a424e21143 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 25 Oct 2012 00:09:51 +0300 Subject: Bluetooth: Fix setting host feature bits for SSP When we get a successful command complete for HCI_Write_SSP_Mode we need to update the host feature bits for the hdev struct accordingly. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index aae80531f8ce..dc60d3161824 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -440,7 +440,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); - void *sent; + struct hci_cp_write_ssp_mode *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -448,10 +448,17 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + if (!status) { + if (sent->mode) + hdev->host_features[0] |= LMP_HOST_SSP; + else + hdev->host_features[0] &= ~LMP_HOST_SSP; + } + if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); + mgmt_ssp_enable_complete(hdev, sent->mode, status); else if (!status) { - if (*((u8 *) sent)) + if (sent->mode) set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); else clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); -- cgit v1.2.3 From 6b4b73ee75bd65c4a47b1a323cb7c5180a6d2ea7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 25 Oct 2012 00:09:52 +0300 Subject: Bluetooth: Fix sending unnecessary HCI_Write_SSP_Mode command This patch fixes sending an unnecessary HCI_Write_SSP_Mode command if the command has already been sent as part of the default HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/mgmt.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 6642b3c91d33..b3490c634db7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -770,6 +770,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_ext_feat_capable(dev) ((dev)->features[7] & LMP_EXTFEATURES) /* ----- Extended LMP capabilities ----- */ +#define lmp_host_ssp_capable(dev) ((dev)->host_features[0] & LMP_HOST_SSP) #define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) #define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 585654bd250f..9017287224d3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2913,7 +2913,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); if (powered) { - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + !lmp_host_ssp_capable(hdev)) { u8 ssp = 1; hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp); -- cgit v1.2.3 From 1225a6bdf87446134789f3fc70ca75a056bbb1ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 25 Oct 2012 00:09:54 +0300 Subject: Bluetooth: Fix unnecessary EIR update during powering on When powered on the EIR data gets updated as the last step by mgmt. Therefore avoid an update when getting a local name update as that's part of the normal HCI init sequence. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9017287224d3..bcf7690a23cb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3516,7 +3516,12 @@ send_event: err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); - update_eir(hdev); + /* EIR is taken care of separately when powering on the + * adapter so only update them here if this is a name change + * unrelated to power on. + */ + if (!test_bit(HCI_INIT, &hdev->flags)) + update_eir(hdev); failed: if (cmd) -- cgit v1.2.3 From 430a61b8037e3184eb7e59220bb4a43530b8d0f6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 25 Oct 2012 00:09:53 +0300 Subject: Bluetooth: Fix sending unnecessary HCI_LE_Host_Enable This patch fixes sending an unnecessary HCI_LE_Host_Enable command if the command has already been sent as part of the default HCI init sequence. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bcf7690a23cb..1a304e2d5a74 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2926,8 +2926,14 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) cp.le = 1; cp.simul = !!lmp_le_br_capable(hdev); - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, - sizeof(cp), &cp); + /* Check first if we already have the right + * host state (host features set) + */ + if (cp.le != !!lmp_host_le_capable(hdev) || + cp.simul != !!lmp_host_le_br_capable(hdev)) + hci_send_cmd(hdev, + HCI_OP_WRITE_LE_HOST_SUPPORTED, + sizeof(cp), &cp); } if (lmp_bredr_capable(hdev)) { -- cgit v1.2.3 From fe79c6fea30a7b6d1a8757c9bfce2f6426c68436 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 25 Oct 2012 15:20:42 +0300 Subject: Bluetooth: trivial: Remove unneeded assignment Assignment is not needed here since err is always gets value. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fae0c70e8e10..962a322c8aed 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4386,7 +4386,7 @@ static void l2cap_logical_finish_create(struct l2cap_chan *chan, set_bit(CONF_OUTPUT_DONE, &chan->conf_state); if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { - int err = 0; + int err; set_default_fcs(chan); -- cgit v1.2.3 From 35ba9561b9b5dc1e5921b927440bd6d3844f0577 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 25 Oct 2012 15:20:43 +0300 Subject: Bluetooth: Use helper function sending EFS conf rsp There is helper function used to send EFS Configuration Response. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 962a322c8aed..600d8080f234 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4374,16 +4374,11 @@ static void l2cap_logical_finish_create(struct l2cap_chan *chan, struct hci_chan *hchan) { struct l2cap_conf_rsp rsp; - u8 code; chan->hs_hcon = hchan->conn; chan->hs_hcon->l2cap_data = chan->conn; - code = l2cap_build_conf_rsp(chan, &rsp, - L2CAP_CONF_SUCCESS, 0); - l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CONF_RSP, code, - &rsp); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0); if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { int err; -- cgit v1.2.3 From d5e911928bd8325918bda3df59e84b0c17a0cdab Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 25 Oct 2012 15:20:44 +0300 Subject: Bluetooth: AMP: Process Physical Link Complete evt Add processing for HCI Physical Link Complete event. Upon successful status received start L2CAP create channel process. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index dc60d3161824..d2c45591f06d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3644,6 +3644,57 @@ unlock: hci_dev_unlock(hdev); } +static void hci_phy_link_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_phy_link_complete *ev = (void *) skb->data; + struct hci_conn *hcon, *bredr_hcon; + + BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle, + ev->status); + + hci_dev_lock(hdev); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) { + hci_dev_unlock(hdev); + return; + } + + if (ev->status) { + hci_conn_del(hcon); + hci_dev_unlock(hdev); + return; + } + + bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon; + + hcon->state = BT_CONNECTED; + bacpy(&hcon->dst, &bredr_hcon->dst); + + hci_conn_hold(hcon); + hcon->disc_timeout = HCI_DISCONN_TIMEOUT; + hci_conn_put(hcon); + + hci_conn_hold_device(hcon); + hci_conn_add_sysfs(hcon); + + hci_dev_unlock(hdev); + + if (hcon->out) { + struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev); + + if (!bredr_hdev) + return; + + /* Placeholder - create chan req + l2cap_chan_create_cfm(bredr_hcon, hcon->remote_id); + */ + + hci_dev_put(bredr_hdev); + } +} + static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; @@ -3971,6 +4022,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_remote_oob_data_request_evt(hdev, skb); break; + case HCI_EV_PHY_LINK_COMPLETE: + hci_phy_link_complete_evt(hdev, skb); + break; + case HCI_EV_NUM_COMP_BLOCKS: hci_num_comp_blocks_evt(hdev, skb); break; -- cgit v1.2.3 From 27695fb415ab150e1972a882c2538bf9bf130cb0 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 25 Oct 2012 15:20:45 +0300 Subject: Bluetooth: AMP: Process Logical Link complete evt After receiving HCI Logical Link Complete event finish EFS configuration by sending L2CAP Conf Response with success code. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 ++ net/bluetooth/hci_event.c | 42 ++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/l2cap_core.c | 4 ++-- 3 files changed, 46 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 49783e948856..24c61ef933b5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -810,5 +810,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); void l2cap_send_conn_req(struct l2cap_chan *chan); void l2cap_move_start(struct l2cap_chan *chan); +void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, + u8 status); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d2c45591f06d..aa79ed278959 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3695,6 +3695,44 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev, } } +static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_logical_link_complete *ev = (void *) skb->data; + struct hci_conn *hcon; + struct hci_chan *hchan; + struct amp_mgr *mgr; + + BT_DBG("%s log_handle 0x%4.4x phy_handle 0x%2.2x status 0x%2.2x", + hdev->name, le16_to_cpu(ev->handle), ev->phy_handle, + ev->status); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) + return; + + /* Create AMP hchan */ + hchan = hci_chan_create(hcon); + if (!hchan) + return; + + hchan->handle = le16_to_cpu(ev->handle); + + BT_DBG("hcon %p mgr %p hchan %p", hcon, hcon->amp_mgr, hchan); + + mgr = hcon->amp_mgr; + if (mgr && mgr->bredr_chan) { + struct l2cap_chan *bredr_chan = mgr->bredr_chan; + + l2cap_chan_lock(bredr_chan); + + bredr_chan->conn->mtu = hdev->block_mtu; + l2cap_logical_cfm(bredr_chan, hchan, 0); + hci_conn_hold(hcon); + + l2cap_chan_unlock(bredr_chan); + } +} + static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; @@ -4026,6 +4064,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_phy_link_complete_evt(hdev, skb); break; + case HCI_EV_LOGICAL_LINK_COMPLETE: + hci_loglink_complete_evt(hdev, skb); + break; + case HCI_EV_NUM_COMP_BLOCKS: hci_num_comp_blocks_evt(hdev, skb); break; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 600d8080f234..d1728af8e84f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4428,8 +4428,8 @@ static void l2cap_logical_finish_move(struct l2cap_chan *chan, } /* Call with chan locked */ -static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, - u8 status) +void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, + u8 status) { BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status); -- cgit v1.2.3 From e9b02748ffc043e8a36f7893bbf58bb886f0b7e4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 25 Oct 2012 15:20:51 +0300 Subject: Bluetooth: Add put(hcon) when deleting hchan When refcnt reaches zero disconnect timeout will run and hci_conn will be disconnected. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_conn.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index dc331ceca471..25bfce0666eb 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -980,6 +980,8 @@ void hci_chan_del(struct hci_chan *chan) synchronize_rcu(); + hci_conn_put(conn); + skb_queue_purge(&chan->data_q); kfree(chan); } -- cgit v1.2.3 From 258c4ed076387fc900ea52869eab9b7ee67ce864 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Tue, 23 Oct 2012 19:02:17 +0530 Subject: Bluetooth: Remove unnecessary include export.h For files only using THIS_MODULE and/or EXPORT_SYMBOL, map them onto including export.h -- or if the file isn't even using those, then just delete the include. Signed-off-by: Syam Sidhardhan Signed-off-by: Gustavo Padovan --- net/bluetooth/bnep/netdev.c | 1 - net/bluetooth/hci_event.c | 1 - 2 files changed, 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 98f86f91d47c..e58c8b32589c 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -25,7 +25,6 @@ SOFTWARE IS DISCLAIMED. */ -#include #include #include diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index aa79ed278959..c4e10e656c68 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -24,7 +24,6 @@ /* Bluetooth HCI event handling. */ -#include #include #include -- cgit v1.2.3 From 612dfce9fbd2e564bcd656d4b7f7fa7d72966c47 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Mon, 29 Oct 2012 22:37:36 +0530 Subject: Bluetooth: mgmt: Use __constant when dealing with constants __constant_cpu_to_le*() is the right go here. Signed-off-by: Syam Sidhardhan Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1a304e2d5a74..a1a62baaaafb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); + hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS); hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev)); @@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE); hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); @@ -832,7 +832,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, if (hdev) hdr->index = cpu_to_le16(hdev->id); else - hdr->index = cpu_to_le16(MGMT_INDEX_NONE); + hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE); hdr->len = cpu_to_le16(data_len); if (data) @@ -3622,9 +3622,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; if (cfm_name) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); + ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); if (!ssp) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); + ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); if (eir_len > 0) memcpy(ev->eir, eir, eir_len); -- cgit v1.2.3 From ba6fc31727c07e11a7b700a9c17e91ab4bed2f4c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:26 +0200 Subject: Bluetooth: trivial: Fix braces style and remove empty line Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d1728af8e84f..c2fbaf9c6ac8 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6252,9 +6252,9 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) conn = l2cap_conn_add(hcon, status); if (conn) l2cap_conn_ready(conn); - } else + } else { l2cap_conn_del(hcon, bt_to_errno(status)); - + } } int l2cap_disconn_ind(struct hci_conn *hcon) -- cgit v1.2.3 From 336178a33491685039d154d1f0a8bd696f51699b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:27 +0200 Subject: Bluetooth: Save hs_hchan instead of hs_hcon in loglink complete When logical link creation is completed we need to save hs_hchan which represents logical link instead of hs_hcon representing physical link. hs_hcon shall be saved when receiving physical link complete event. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c2fbaf9c6ac8..4ef85d24797d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4375,7 +4375,7 @@ static void l2cap_logical_finish_create(struct l2cap_chan *chan, { struct l2cap_conf_rsp rsp; - chan->hs_hcon = hchan->conn; + chan->hs_hchan = hchan; chan->hs_hcon->l2cap_data = chan->conn; l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0); -- cgit v1.2.3 From 439f34acead5bb1679f194c2023f05b90dcb8b4c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:28 +0200 Subject: Bluetooth: Return correct L2CAP response type Return L2CAP_CREATE_CHAN_RSP for Create Channel Request and L2CAP_CONN_RSP for Create Connection Request. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4ef85d24797d..d51741fb5a9c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3478,12 +3478,21 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) struct l2cap_conn_rsp rsp; struct l2cap_conn *conn = chan->conn; u8 buf[128]; + u8 rsp_code; rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (chan->hs_hcon) + rsp_code = L2CAP_CREATE_CHAN_RSP; + else + rsp_code = L2CAP_CONN_RSP; + + BT_DBG("chan %p rsp_code %u", chan, rsp_code); + + l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp); if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; -- cgit v1.2.3 From 770bfefa2cbe8f5911860fef1a68ea873a9bbdbe Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:29 +0200 Subject: Bluetooth: Derive remote and local amp id from chan struct l2cap_chan already keeps information about *_amp_id. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d51741fb5a9c..782e49c97e7e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4573,9 +4573,11 @@ static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result) l2cap_ertm_send(chan); } -void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id, - u8 remote_amp_id) +void l2cap_physical_cfm(struct l2cap_chan *chan, int result) { + u8 local_amp_id = chan->local_amp_id; + u8 remote_amp_id = chan->ctrl_id; + BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d", chan, result, local_amp_id, remote_amp_id); -- cgit v1.2.3 From 5ce66b59d787478f57a6f3368ff23d75a06e76e2 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:30 +0200 Subject: Bluetooth: AMP: Add Logical Link Create function After physical link is created logical link needs to be created. The process starts after L2CAP channel is created and L2CAP Configuration Response with result PENDING is received. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 1 + net/bluetooth/amp.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 9 +++++++++ net/bluetooth/l2cap_core.c | 19 +++++++++++++----- 4 files changed, 73 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 2e7c79ea0463..70d5c153de15 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -46,5 +46,6 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); +void amp_create_logical_link(struct l2cap_chan *chan); #endif /* __AMP_H */ diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 231d7ef53ecb..fbb63605a27e 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -372,3 +372,52 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); } + +void amp_create_logical_link(struct l2cap_chan *chan) +{ + struct hci_cp_create_accept_logical_link cp; + struct hci_conn *hcon; + struct hci_dev *hdev; + + BT_DBG("chan %p", chan); + + if (!chan->hs_hcon) + return; + + hdev = hci_dev_hold(chan->hs_hcon->hdev); + if (!hdev) + return; + + BT_DBG("chan %p ctrl_id %d dst %pMR", chan, chan->ctrl_id, + chan->conn->dst); + + hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst); + if (!hcon) + goto done; + + cp.phy_handle = hcon->handle; + + cp.tx_flow_spec.id = chan->local_id; + cp.tx_flow_spec.stype = chan->local_stype; + cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu); + cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime); + cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat); + cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to); + + cp.rx_flow_spec.id = chan->remote_id; + cp.rx_flow_spec.stype = chan->remote_stype; + cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu); + cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime); + cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat); + cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to); + + if (hcon->out) + hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), + &cp); + else + hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), + &cp); + +done: + hci_dev_put(hdev); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c4e10e656c68..14cad155af3c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1835,6 +1835,11 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) amp_write_remote_assoc(hdev, cp->phy_handle); } +static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status 0x%2.2x", hdev->name, status); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2669,6 +2674,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_accept_phylink(hdev, ev->status); break; + case HCI_OP_CREATE_LOGICAL_LINK: + hci_cs_create_logical_link(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 782e49c97e7e..ecc5020c9242 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -38,6 +38,7 @@ #include #include #include +#include bool disable_ertm; @@ -1013,6 +1014,12 @@ static bool __amp_capable(struct l2cap_chan *chan) return false; } +static bool l2cap_check_efs(struct l2cap_chan *chan) +{ + /* Check EFS parameters */ + return true; +} + void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -3957,13 +3964,15 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, goto done; } - /* check compatibility */ - - if (!chan->ctrl_id) + if (!chan->ctrl_id) { l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, 0); - else - chan->ident = cmd->ident; + } else { + if (l2cap_check_efs(chan)) { + amp_create_logical_link(chan); + chan->ident = cmd->ident; + } + } } goto done; -- cgit v1.2.3 From 606e2a10a6d23e900dad0b098a09438a5f7e0495 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:31 +0200 Subject: Bluetooth: AMP: Process Disc Logical Link Add processing for HCI Disconnection Logical Link Complete Event. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 1 + net/bluetooth/amp.c | 7 +++++++ net/bluetooth/hci_event.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 70d5c153de15..405fb9c987ef 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -47,5 +47,6 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); void amp_create_logical_link(struct l2cap_chan *chan); +void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason); #endif /* __AMP_H */ diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index fbb63605a27e..0f3fef34eabc 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -421,3 +421,10 @@ void amp_create_logical_link(struct l2cap_chan *chan) done: hci_dev_put(hdev); } + +void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason) +{ + BT_DBG("hchan %p", hchan); + + hci_chan_del(hchan); +} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 14cad155af3c..07dce614f81a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3741,6 +3741,30 @@ static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static void hci_disconn_loglink_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_disconn_logical_link_complete *ev = (void *) skb->data; + struct hci_chan *hchan; + + BT_DBG("%s log handle 0x%4.4x status 0x%2.2x", hdev->name, + le16_to_cpu(ev->handle), ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + hchan = hci_chan_lookup_handle(hdev, le16_to_cpu(ev->handle)); + if (!hchan) + goto unlock; + + amp_destroy_logical_link(hchan, ev->reason); + +unlock: + hci_dev_unlock(hdev); +} + static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; @@ -4076,6 +4100,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_loglink_complete_evt(hdev, skb); break; + case HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE: + hci_disconn_loglink_complete_evt(hdev, skb); + break; + case HCI_EV_NUM_COMP_BLOCKS: hci_num_comp_blocks_evt(hdev, skb); break; -- cgit v1.2.3 From 9eef6b3a9e38d5f8ad315b2a7db153392e6a77d6 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:32 +0200 Subject: Bluetooth: AMP: Process Disc Physical Link Complete evt Add processing for HCI Disconnection Physical Link Complete Event. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 07dce614f81a..bca71a8b1272 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3765,6 +3765,28 @@ unlock: hci_dev_unlock(hdev); } +static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_disconn_phy_link_complete *ev = (void *) skb->data; + struct hci_conn *hcon; + + BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (hcon) { + hcon->state = BT_CLOSED; + hci_conn_del(hcon); + } + + hci_dev_unlock(hdev); +} + static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; @@ -4104,6 +4126,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_disconn_loglink_complete_evt(hdev, skb); break; + case HCI_EV_DISCONN_PHY_LINK_COMPLETE: + hci_disconn_phylink_complete_evt(hdev, skb); + break; + case HCI_EV_NUM_COMP_BLOCKS: hci_num_comp_blocks_evt(hdev, skb); break; -- cgit v1.2.3 From e58917b990ef0cc3903aa962236a0dae4f1f81a0 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:33 +0200 Subject: Bluetooth: AMP: Remove hci_conn receiving error command status When receiving HCI Event: Command Status for Create Physical Link with Error code remove AMP hcon. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index bca71a8b1272..0572f051c693 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1809,14 +1809,23 @@ static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, status); - if (status) - return; - cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK); if (!cp) return; - amp_write_remote_assoc(hdev, cp->phy_handle); + hci_dev_lock(hdev); + + if (status) { + struct hci_conn *hcon; + + hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle); + if (hcon) + hci_conn_del(hcon); + } else { + amp_write_remote_assoc(hdev, cp->phy_handle); + } + + hci_dev_unlock(hdev); } static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) -- cgit v1.2.3 From 419e08c1121ab346bf5f66fe0a21950529355cee Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:34 +0200 Subject: Bluetooth: Disconnect logical link when deleting chan Disconnect logical link for high speed channel hs_hchan associated with L2CAP channel chan. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 1 + net/bluetooth/amp.c | 14 ++++++++++++++ net/bluetooth/l2cap_core.c | 7 +++++++ 3 files changed, 22 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 405fb9c987ef..f1c0017f69e7 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -47,6 +47,7 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); void amp_create_logical_link(struct l2cap_chan *chan); +void amp_disconnect_logical_link(struct hci_chan *hchan); void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason); #endif /* __AMP_H */ diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 0f3fef34eabc..917e034b9aa5 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -422,6 +422,20 @@ done: hci_dev_put(hdev); } +void amp_disconnect_logical_link(struct hci_chan *hchan) +{ + struct hci_conn *hcon = hchan->conn; + struct hci_cp_disconn_logical_link cp; + + if (hcon->state != BT_CONNECTED) { + BT_DBG("hchan %p not connected", hchan); + return; + } + + cp.log_handle = cpu_to_le16(hchan->handle); + hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp); +} + void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason) { BT_DBG("hchan %p", hchan); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ecc5020c9242..bb2cd9eaa8a1 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -578,6 +578,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) mgr->bredr_chan = NULL; } + if (chan->hs_hchan) { + struct hci_chan *hs_hchan = chan->hs_hchan; + + BT_DBG("chan %p disconnect hs_hchan %p", chan, hs_hchan); + amp_disconnect_logical_link(hs_hchan); + } + chan->ops->teardown(chan, err); if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) -- cgit v1.2.3 From f351bc72676e2666543511558215fe3c95d4336f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:35 +0200 Subject: Bluetooth: AMP: Check for hs_hcon instead of ctrl_id When deciding whether to send EFS configuration response with success, check rather for existence of High Speed physical link hs_hcon then ctrl_id. There might be cases when there is ctrl_id but high speed link is not established yet. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bb2cd9eaa8a1..bdffc4c207b5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3921,7 +3921,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, /* check compatibility */ /* Send rsp for BR/EDR channel */ - if (!chan->ctrl_id) + if (!chan->hs_hcon) l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); else chan->ident = cmd->ident; @@ -3971,7 +3971,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, goto done; } - if (!chan->ctrl_id) { + if (!chan->hs_hcon) { l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, 0); } else { -- cgit v1.2.3 From cf70ff220a918b25d383510f913de52308d04bb2 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 31 Oct 2012 15:46:36 +0200 Subject: Bluetooth: AMP: Use l2cap_physical_cfm in phylink complete evt When receiving HCI Phylink Complete event run amp_physical_cfm which initialize BR/EDR L2CAP channel associated with High Speed link and run l2cap_physical_cfm which shall send L2CAP Create Chan Request. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/amp.h | 1 + include/net/bluetooth/l2cap.h | 1 + net/bluetooth/amp.c | 24 ++++++++++++++++++++++++ net/bluetooth/hci_event.c | 15 ++------------- 4 files changed, 28 insertions(+), 13 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index f1c0017f69e7..7ea3db77ba89 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -46,6 +46,7 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); +void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon); void amp_create_logical_link(struct l2cap_chan *chan); void amp_disconnect_logical_link(struct hci_chan *hchan); void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 24c61ef933b5..18149c8b4d1d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -812,5 +812,6 @@ void l2cap_send_conn_req(struct l2cap_chan *chan); void l2cap_move_start(struct l2cap_chan *chan); void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, u8 status); +void l2cap_physical_cfm(struct l2cap_chan *chan, int result); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 917e034b9aa5..650bb8df04fd 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -373,6 +373,30 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); } +void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) +{ + struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev); + struct amp_mgr *mgr = hs_hcon->amp_mgr; + struct l2cap_chan *bredr_chan; + + BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr); + + if (!bredr_hdev || !mgr || !mgr->bredr_chan) + return; + + bredr_chan = mgr->bredr_chan; + + set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags); + bredr_chan->ctrl_id = hs_hcon->remote_id; + bredr_chan->hs_hcon = hs_hcon; + bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu; + bredr_chan->fcs = L2CAP_FCS_NONE; + + l2cap_physical_cfm(bredr_chan, 0); + + hci_dev_put(bredr_hdev); +} + void amp_create_logical_link(struct l2cap_chan *chan) { struct hci_cp_create_accept_logical_link cp; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0572f051c693..c08ac7c03711 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3696,20 +3696,9 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev, hci_conn_hold_device(hcon); hci_conn_add_sysfs(hcon); - hci_dev_unlock(hdev); - - if (hcon->out) { - struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev); - - if (!bredr_hdev) - return; + amp_physical_cfm(bredr_hcon, hcon); - /* Placeholder - create chan req - l2cap_chan_create_cfm(bredr_hcon, hcon->remote_id); - */ - - hci_dev_put(bredr_hdev); - } + hci_dev_unlock(hdev); } static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3 From 6e1df6a60372b6ea00c480c1cd8c8c8134357d89 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 1 Nov 2012 15:37:02 +0200 Subject: Bluetooth: Process Create Chan Request Add processing L2CAP Create Chan Request. When channel is created save associated high speed link hs_hcon. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 63 +++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 20 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bdffc4c207b5..2f0e165eef4d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4237,7 +4237,9 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, u16 cmd_len, void *data) { struct l2cap_create_chan_req *req = data; + struct l2cap_create_chan_rsp rsp; struct l2cap_chan *chan; + struct hci_dev *hdev; u16 psm, scid; if (cmd_len != sizeof(*req)) @@ -4251,36 +4253,57 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id); - if (req->amp_id) { - struct hci_dev *hdev; - - /* Validate AMP controller id */ - hdev = hci_dev_get(req->amp_id); - if (!hdev || hdev->dev_type != HCI_AMP || - !test_bit(HCI_UP, &hdev->flags)) { - struct l2cap_create_chan_rsp rsp; + /* For controller id 0 make BR/EDR connection */ + if (req->amp_id == HCI_BREDR_ID) { + l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, + req->amp_id); + return 0; + } - rsp.dcid = 0; - rsp.scid = cpu_to_le16(scid); - rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP); - rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); + /* Validate AMP controller id */ + hdev = hci_dev_get(req->amp_id); + if (!hdev) + goto error; - l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, - sizeof(rsp), &rsp); + if (hdev->dev_type != HCI_AMP || !test_bit(HCI_UP, &hdev->flags)) { + hci_dev_put(hdev); + goto error; + } - if (hdev) - hci_dev_put(hdev); + chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, + req->amp_id); + if (chan) { + struct amp_mgr *mgr = conn->hcon->amp_mgr; + struct hci_conn *hs_hcon; - return 0; + hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst); + if (!hs_hcon) { + hci_dev_put(hdev); + return -EFAULT; } - hci_dev_put(hdev); + BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon); + + chan->local_amp_id = req->amp_id; + mgr->bredr_chan = chan; + chan->hs_hcon = hs_hcon; + conn->mtu = hdev->block_mtu; } - chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, - req->amp_id); + hci_dev_put(hdev); return 0; + +error: + rsp.dcid = 0; + rsp.scid = cpu_to_le16(scid); + rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP); + rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, + sizeof(rsp), &rsp); + + return -EFAULT; } static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id) -- cgit v1.2.3 From fffadc08ebf1f4c61bb8f9be0f1d8c3c053d815f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 1 Nov 2012 15:37:03 +0200 Subject: Bluetooth: Rename ctrl_id to remote_amp_id Since we have started to use local_amp_id for local AMP Controller Id it makes sense to rename ctrl_id to remote_amp_id since it represents remote AMP controller Id. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/a2mp.c | 4 ++-- net/bluetooth/amp.c | 5 ++--- net/bluetooth/l2cap_core.c | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 18149c8b4d1d..d65db459c843 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -481,6 +481,7 @@ struct l2cap_chan { unsigned long conn_state; unsigned long flags; + __u8 remote_amp_id; __u8 local_amp_id; __u8 move_id; __u8 move_state; @@ -518,8 +519,6 @@ struct l2cap_chan { __u32 remote_acc_lat; __u32 remote_flush_to; - __u8 ctrl_id; - struct delayed_work chan_timer; struct delayed_work retrans_timer; struct delayed_work monitor_timer; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index d5136cfb57e2..2f67d5ecc907 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -423,7 +423,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); - mgr->bredr_chan->ctrl_id = rsp->id; + mgr->bredr_chan->remote_amp_id = rsp->id; amp_create_phylink(hdev, mgr, hcon); @@ -939,7 +939,7 @@ void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) goto clean; req->local_id = hdev->id; - req->remote_id = bredr_chan->ctrl_id; + req->remote_id = bredr_chan->remote_amp_id; memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 650bb8df04fd..4b2fea6c1c2a 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -387,7 +387,7 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) bredr_chan = mgr->bredr_chan; set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags); - bredr_chan->ctrl_id = hs_hcon->remote_id; + bredr_chan->remote_amp_id = hs_hcon->remote_id; bredr_chan->hs_hcon = hs_hcon; bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu; bredr_chan->fcs = L2CAP_FCS_NONE; @@ -412,8 +412,7 @@ void amp_create_logical_link(struct l2cap_chan *chan) if (!hdev) return; - BT_DBG("chan %p ctrl_id %d dst %pMR", chan, chan->ctrl_id, - chan->conn->dst); + BT_DBG("chan %p dst %pMR", chan, chan->conn->dst); hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst); if (!hcon) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2f0e165eef4d..a1faaab41839 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4615,7 +4615,7 @@ static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result) void l2cap_physical_cfm(struct l2cap_chan *chan, int result) { u8 local_amp_id = chan->local_amp_id; - u8 remote_amp_id = chan->ctrl_id; + u8 remote_amp_id = chan->remote_amp_id; BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d", chan, result, local_amp_id, remote_amp_id); -- cgit v1.2.3 From 476e44cb19f1fbf2d5883dddcc0ce31b33b45915 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 19 Oct 2012 20:10:46 +0300 Subject: Bluetooth: Fix having bogus entries in mgmt_read_index_list reply The mgmt_read_index_list uses one loop to calculate the max needed size of its response with the help of an upper-bound of the controller count. The second loop is more strict as it checks for HCI_SETUP (which might have gotten set after the first loop) and could result in some indexes being skipped. Because of this the function needs to readjust the event length and index count after filling in the response array. Signed-off-by: Johan Hedberg Cc: stable@vger.kernel.org Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index aa2ea0a8142c..2cfabe27d3e0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -326,7 +326,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, struct hci_dev *d; size_t rp_len; u16 count; - int i, err; + int err; BT_DBG("sock %p", sk); @@ -347,9 +347,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, return -ENOMEM; } - rp->num_controllers = cpu_to_le16(count); - - i = 0; + count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_bit(HCI_SETUP, &d->dev_flags)) continue; @@ -357,10 +355,13 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, if (!mgmt_valid_hdev(d)) continue; - rp->index[i++] = cpu_to_le16(d->id); + rp->index[count++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } + rp->num_controllers = cpu_to_le16(count); + rp_len = sizeof(*rp) + (2 * count); + read_unlock(&hci_dev_list_lock); err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp, -- cgit v1.2.3 From 896ea28ea824d49671fc7e9315d9c5be491a644f Mon Sep 17 00:00:00 2001 From: Paulo Sérgio Date: Thu, 25 Oct 2012 16:55:51 -0300 Subject: Bluetooth: Fix error status when pairing fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pairing fails due to wrong confirm value, the management layer doesn't report a proper error status. It sends MGMT_STATUS_CONNECT_FAILED instead of MGMT_STATUS_AUTH_FAILED. Most of management functions that receive a status as a parameter expects for it to be encoded as a HCI status. But when a SMP pairing fails, the SMP layer sends the SMP reason as the error status to the management layer. This commit maps all SMP reasons to HCI_ERROR_AUTH_FAILURE, which will be converted to MGMT_STATUS_AUTH_FAILED in the management layer. Reported-by: Claudio Takahasi Reviewed-by: João Paulo Rechi Vita Signed-off-by: Paulo Sérgio Signed-off-by: Gustavo Padovan --- net/bluetooth/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2ac8d50861e0..a5923378bdf0 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -267,7 +267,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, - hcon->dst_type, reason); + hcon->dst_type, HCI_ERROR_AUTH_FAILURE); cancel_delayed_work_sync(&conn->security_timer); -- cgit v1.2.3 From fbe96d6ff9f4e361e1b2ec0a30140e17af7e3854 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 30 Oct 2012 01:35:40 -0700 Subject: Bluetooth: Notify about device registration before power on It is important that the monitor interface gets notified about a new device before its power on procedure has been started. For some reason that is no longer working as expected and the power on procedure runs first. It is safe to just notify about device registration and trigger the power on procedure afterwards. Signed-off-by: Marcel Holtmann Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8a0ce706aebd..a0a2f97b9c62 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1754,11 +1754,11 @@ int hci_register_dev(struct hci_dev *hdev) if (hdev->dev_type != HCI_AMP) set_bit(HCI_AUTO_OFF, &hdev->dev_flags); - schedule_work(&hdev->power_on); - hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); + schedule_work(&hdev->power_on); + return id; err_wqueue: -- cgit v1.2.3 From 482049f75750d73358e65236b933417b69f9cc25 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 8 Nov 2012 10:25:26 +0100 Subject: Bluetooth: Fix memory leak when removing a UUID When removing a UUID from the list in the remove_uuid() function we must also kfree the entry in addition to removing it from the list. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2cfabe27d3e0..91de4239da66 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1367,6 +1367,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, continue; list_del(&match->list); + kfree(match); found++; } -- cgit v1.2.3 From bbaf444a89dd7dd7effd8ed2f4e4ec64da3cc1da Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 8 Nov 2012 01:22:59 +0100 Subject: Bluetooth: Use proper invalid value for tx_power The core specification defines 127 as the "not available" value (well, "reserved" for BR/EDR and "not available" for LE - but essentially the same). Therefore, instead of testing for 0 (which is in fact a valid value) we should be using this invalid value to test if the tx_power is available. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 3 +++ net/bluetooth/hci_core.c | 2 ++ net/bluetooth/hci_event.c | 2 ++ net/bluetooth/mgmt.c | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6c414f4302fe..344fea0a7244 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -319,6 +319,9 @@ enum { #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00 #define HCI_FLOW_CTL_MODE_BLOCK_BASED 0x01 +/* The core spec defines 127 as the "not available" value */ +#define HCI_TX_POWER_INVALID 127 + /* Extended Inquiry Response field types */ #define EIR_FLAGS 0x01 /* flags */ #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 515d0c394f35..9713a2917ddc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1606,6 +1606,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); hdev->io_capability = 0x03; /* No Input No Output */ + hdev->inq_tx_power = HCI_TX_POWER_INVALID; + hdev->adv_tx_power = HCI_TX_POWER_INVALID; hdev->sniff_max_interval = 800; hdev->sniff_min_interval = 80; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c08ac7c03711..09c65712e8cc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -202,6 +202,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) BIT(HCI_PERIODIC_INQ)); hdev->discovery.state = DISCOVERY_STOPPED; + hdev->inq_tx_power = HCI_TX_POWER_INVALID; + hdev->adv_tx_power = HCI_TX_POWER_INVALID; } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a1a62baaaafb..dedbb1d8b2d2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -484,7 +484,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr += (name_len + 2); } - if (hdev->inq_tx_power) { + if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) { ptr[0] = 2; ptr[1] = EIR_TX_POWER; ptr[2] = (u8) hdev->inq_tx_power; -- cgit v1.2.3 From 3f0f524bafcd2025c12e215f13207c7be0a13bf9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 8 Nov 2012 01:23:00 +0100 Subject: Bluetooth: Add support for setting LE advertising data This patch adds support for setting basing LE advertising data. The three elements supported for now are the advertising flags, the TX power and the friendly name. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 15 +++++++ include/net/bluetooth/hci_core.h | 4 ++ net/bluetooth/hci_core.c | 94 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 11 ++++- 4 files changed, 123 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 344fea0a7244..7306078e547c 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -338,6 +338,13 @@ enum { #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ +/* Low Energy Advertising Flags */ +#define LE_AD_LIMITED 0x01 /* Limited Discoverable */ +#define LE_AD_GENERAL 0x02 /* General Discoverable */ +#define LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ +#define LE_AD_SIM_LE_BREDR_CTRL 0x08 /* Simultaneous LE & BR/EDR Controller */ +#define LE_AD_SIM_LE_BREDR_HOST 0x10 /* Simultaneous LE & BR/EDR Host */ + /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 @@ -942,6 +949,14 @@ struct hci_rp_le_read_adv_tx_power { __s8 tx_power; } __packed; +#define HCI_MAX_AD_LENGTH 31 + +#define HCI_OP_LE_SET_ADV_DATA 0x2008 +struct hci_cp_le_set_adv_data { + __u8 length; + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; + #define HCI_OP_LE_SET_SCAN_PARAM 0x200b struct hci_cp_le_set_scan_param { __u8 type; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ce6dbeb6dfb6..ef5b85dac3f7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -279,6 +279,8 @@ struct hci_dev { struct le_scan_params le_scan_params; __s8 adv_tx_power; + __u8 adv_data[HCI_MAX_AD_LENGTH]; + __u8 adv_data_len; int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); @@ -734,6 +736,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_update_ad(struct hci_dev *hdev); + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9713a2917ddc..e3a49db9cfcb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -594,6 +594,99 @@ done: return err; } +static u8 create_ad(struct hci_dev *hdev, u8 *ptr) +{ + u8 ad_len = 0, flags = 0; + size_t name_len; + + if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) + flags |= LE_AD_GENERAL; + + if (!lmp_bredr_capable(hdev)) + flags |= LE_AD_NO_BREDR; + + if (lmp_le_br_capable(hdev)) + flags |= LE_AD_SIM_LE_BREDR_CTRL; + + if (lmp_host_le_br_capable(hdev)) + flags |= LE_AD_SIM_LE_BREDR_HOST; + + if (flags) { + BT_DBG("adv flags 0x%02x", flags); + + ptr[0] = 2; + ptr[1] = EIR_FLAGS; + ptr[2] = flags; + + ad_len += 3; + ptr += 3; + } + + if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) { + ptr[0] = 2; + ptr[1] = EIR_TX_POWER; + ptr[2] = (u8) hdev->adv_tx_power; + + ad_len += 3; + ptr += 3; + } + + name_len = strlen(hdev->dev_name); + if (name_len > 0) { + size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2; + + if (name_len > max_len) { + name_len = max_len; + ptr[1] = EIR_NAME_SHORT; + } else + ptr[1] = EIR_NAME_COMPLETE; + + ptr[0] = name_len + 1; + + memcpy(ptr + 2, hdev->dev_name, name_len); + + ad_len += (name_len + 2); + ptr += (name_len + 2); + } + + return ad_len; +} + +int hci_update_ad(struct hci_dev *hdev) +{ + struct hci_cp_le_set_adv_data cp; + u8 len; + int err; + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = -EINVAL; + goto unlock; + } + + memset(&cp, 0, sizeof(cp)); + + len = create_ad(hdev, cp.data); + + if (hdev->adv_data_len == len && + memcmp(cp.data, hdev->adv_data, len) == 0) { + err = 0; + goto unlock; + } + + memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); + hdev->adv_data_len = len; + + cp.length = len; + err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); + +unlock: + hci_dev_unlock(hdev); + + return err; +} + /* ---- HCI ioctl helpers ---- */ int hci_dev_open(__u16 dev) @@ -651,6 +744,7 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); + hci_update_ad(hdev); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 09c65712e8cc..7caea1af557b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -204,6 +204,9 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) hdev->discovery.state = DISCOVERY_STOPPED; hdev->inq_tx_power = HCI_TX_POWER_INVALID; hdev->adv_tx_power = HCI_TX_POWER_INVALID; + + memset(hdev->adv_data, 0, sizeof(hdev->adv_data)); + hdev->adv_data_len = 0; } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -226,6 +229,9 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); + if (!status && !test_bit(HCI_INIT, &hdev->flags)) + hci_update_ad(hdev); + hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); } @@ -1091,8 +1097,11 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) + if (!rp->status) { hdev->adv_tx_power = rp->tx_power; + if (!test_bit(HCI_INIT, &hdev->flags)) + hci_update_ad(hdev); + } hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status); } -- cgit v1.2.3 From c1d5dc4ac15be45c7061e207f06ad8dfba0c2170 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 8 Nov 2012 01:23:01 +0100 Subject: Bluetooth: Fix updating advertising state flags and data This patch adds a callback for the HCI_LE_Set_Advertise_Enable command. The callback is responsible for updating the HCI_LE_PERIPHERAL flag updating as well as updating the advertising data flags field to indicate undirected connectable advertising. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_event.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7306078e547c..4bbabd85f5e5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -957,6 +957,8 @@ struct hci_cp_le_set_adv_data { __u8 data[HCI_MAX_AD_LENGTH]; } __packed; +#define HCI_OP_LE_SET_ADV_ENABLE 0x200a + #define HCI_OP_LE_SET_SCAN_PARAM 0x200b struct hci_cp_le_set_scan_param { __u8 type; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7caea1af557b..9f5c5f244502 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1190,6 +1190,33 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 *sent, status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE); + if (!sent) + return; + + hci_dev_lock(hdev); + + if (!status) { + if (*sent) + set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); + else + clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); + } + + hci_dev_unlock(hdev); + + if (!test_bit(HCI_INIT, &hdev->flags)) + hci_update_ad(hdev); + + hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status); +} + static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2585,6 +2612,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_set_scan_param(hdev, skb); break; + case HCI_OP_LE_SET_ADV_ENABLE: + hci_cc_le_set_adv_enable(hdev, skb); + break; + case HCI_OP_LE_SET_SCAN_ENABLE: hci_cc_le_set_scan_enable(hdev, skb); break; -- cgit v1.2.3 From 96eff46e9f5632efa0d2941f7e028701a5f1a0f6 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 15 Nov 2012 18:14:53 +0200 Subject: Bluetooth: Use __l2cap_no_conn_pending helper Use helper instead of test_bit. This is the only place left using test CONF_CONNECT_PEND flag. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a1faaab41839..4479894e0a5a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6380,7 +6380,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { + if (!__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); continue; } -- cgit v1.2.3 From 522db70286fcce420e5fc1be107912fbd3806323 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 15 Nov 2012 18:14:55 +0200 Subject: Bluetooth: Remove unneeded local_amp_id initialization local_amp_id is already set in l2cap_connect() which is called several lines above. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4479894e0a5a..e4f52a7f08c6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4284,7 +4284,6 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon); - chan->local_amp_id = req->amp_id; mgr->bredr_chan = chan; chan->hs_hcon = hs_hcon; conn->mtu = hdev->block_mtu; -- cgit v1.2.3 From a514b17fab51c1433db920d76cf8ddda959e5da0 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 14 Nov 2012 17:39:30 +0200 Subject: Bluetooth: Refactor locking in amp_physical_cfm Remove locking from l2cap_physical_cfm and lock chan inside amp_physical_cfm. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/amp.c | 6 +++++- net/bluetooth/l2cap_core.c | 7 ++----- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d65db459c843..f57fab04e7c5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -811,6 +811,6 @@ void l2cap_send_conn_req(struct l2cap_chan *chan); void l2cap_move_start(struct l2cap_chan *chan); void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, u8 status); -void l2cap_physical_cfm(struct l2cap_chan *chan, int result); +void __l2cap_physical_cfm(struct l2cap_chan *chan, int result); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 4b2fea6c1c2a..eaf473ffeef9 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -386,13 +386,17 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) bredr_chan = mgr->bredr_chan; + l2cap_chan_lock(bredr_chan); + set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags); bredr_chan->remote_amp_id = hs_hcon->remote_id; bredr_chan->hs_hcon = hs_hcon; bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu; bredr_chan->fcs = L2CAP_FCS_NONE; - l2cap_physical_cfm(bredr_chan, 0); + __l2cap_physical_cfm(bredr_chan, 0); + + l2cap_chan_unlock(bredr_chan); hci_dev_put(bredr_hdev); } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e4f52a7f08c6..22c4ef926b0d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4611,7 +4611,8 @@ static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result) l2cap_ertm_send(chan); } -void l2cap_physical_cfm(struct l2cap_chan *chan, int result) +/* Invoke with locked chan */ +void __l2cap_physical_cfm(struct l2cap_chan *chan, int result) { u8 local_amp_id = chan->local_amp_id; u8 remote_amp_id = chan->remote_amp_id; @@ -4619,8 +4620,6 @@ void l2cap_physical_cfm(struct l2cap_chan *chan, int result) BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d", chan, result, local_amp_id, remote_amp_id); - l2cap_chan_lock(chan); - if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) { l2cap_chan_unlock(chan); return; @@ -4644,8 +4643,6 @@ void l2cap_physical_cfm(struct l2cap_chan *chan, int result) break; } } - - l2cap_chan_unlock(chan); } static inline int l2cap_move_channel_req(struct l2cap_conn *conn, -- cgit v1.2.3 From 12d6cc60f2d02d5754ca662b414f8f96200b14a6 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 14 Nov 2012 17:39:31 +0200 Subject: Bluetooth: Disable FCS only for new HS channels Set chan->fcs to L2CAP_FCS_NONE only for new L2CAP channels (not moved). Other side can still request to use FCS. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/amp.c | 1 - net/bluetooth/l2cap_core.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index eaf473ffeef9..0258b2645edb 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -392,7 +392,6 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) bredr_chan->remote_amp_id = hs_hcon->remote_id; bredr_chan->hs_hcon = hs_hcon; bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu; - bredr_chan->fcs = L2CAP_FCS_NONE; __l2cap_physical_cfm(bredr_chan, 0); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 22c4ef926b0d..fc92fe341115 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4517,6 +4517,8 @@ void l2cap_move_start(struct l2cap_chan *chan) static void l2cap_do_create(struct l2cap_chan *chan, int result, u8 local_amp_id, u8 remote_amp_id) { + chan->fcs = L2CAP_FCS_NONE; + if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { struct l2cap_conn_rsp rsp; char buf[128]; -- cgit v1.2.3 From 62cd50e262182685c291bc86076e74ef6f7331b1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 14 Nov 2012 17:39:32 +0200 Subject: Bluetooth: trivial: Use __constant for constants Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fc92fe341115..3ed93938370a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4528,12 +4528,12 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result, /* Incoming channel on AMP */ if (result == L2CAP_CR_SUCCESS) { /* Send successful response */ - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); } else { /* Send negative response */ - rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); + rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM); + rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); } l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP, -- cgit v1.2.3 From 62748ca16f2f79a62d90fb45963233fb10f726c4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 20 Nov 2012 17:16:19 +0200 Subject: Bluetooth: Fix sending L2CAP Create Chan Req When receiving Physical Link Completed event we need to create L2CAP channel with L2CAP Create Chan Request. Current code was sending this command only if connection was pending (which is probably needed in channel move case). If channel is not moved but created Create Chan should be sent for outgoing channel which is checked with BT_CONNECT flag. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3ed93938370a..7114bdff5958 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4517,15 +4517,31 @@ void l2cap_move_start(struct l2cap_chan *chan) static void l2cap_do_create(struct l2cap_chan *chan, int result, u8 local_amp_id, u8 remote_amp_id) { + BT_DBG("chan %p state %s %u -> %u", chan, state_to_string(chan->state), + local_amp_id, remote_amp_id); + chan->fcs = L2CAP_FCS_NONE; - if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { + /* Outgoing channel on AMP */ + if (chan->state == BT_CONNECT) { + if (result == L2CAP_CR_SUCCESS) { + chan->local_amp_id = local_amp_id; + l2cap_send_create_chan_req(chan, remote_amp_id); + } else { + /* Revert to BR/EDR connect */ + l2cap_send_conn_req(chan); + } + + return; + } + + /* Incoming channel on AMP */ + if (__l2cap_no_conn_pending(chan)) { struct l2cap_conn_rsp rsp; char buf[128]; rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); - /* Incoming channel on AMP */ if (result == L2CAP_CR_SUCCESS) { /* Send successful response */ rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); @@ -4547,15 +4563,6 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result, l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } - } else { - /* Outgoing channel on AMP */ - if (result == L2CAP_CR_SUCCESS) { - chan->local_amp_id = local_amp_id; - l2cap_send_create_chan_req(chan, remote_amp_id); - } else { - /* Revert to BR/EDR connect */ - l2cap_send_conn_req(chan); - } } } -- cgit v1.2.3 From 37295996ce181a2a4e74308070abf06bd822f7a1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 20 Nov 2012 17:16:20 +0200 Subject: Bluetooth: Set local_amp_id after getting Phylink Completed evt local_amp_id is used in l2cap_physical_cfm and shall be set up before calling it. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/amp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 0258b2645edb..1b0d92c0643a 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -390,6 +390,7 @@ void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags); bredr_chan->remote_amp_id = hs_hcon->remote_id; + bredr_chan->local_amp_id = hs_hcon->hdev->id; bredr_chan->hs_hcon = hs_hcon; bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu; -- cgit v1.2.3 From 1bb166e66cc46323d3757ce1027cc1c767498d50 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 20 Nov 2012 17:16:21 +0200 Subject: Bluetooth: Improve error message printing Instead of printing: "[ 7763.082007] Bluetooth: 2" print something like: "[ 7763.082007] Bluetooth: Trailing bytes: 2 in sframe" Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7114bdff5958..f44c5429b4f0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6091,7 +6091,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) control->super); if (len != 0) { - BT_ERR("%d", len); + BT_ERR("Trailing bytes: %d in sframe", len); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } -- cgit v1.2.3 From fd45bf4c923308b19d8baa3c227d26a0e7d79fa7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 20 Nov 2012 17:16:22 +0200 Subject: Bluetooth: AMP: Set no FCS for incoming L2CAP chan When receiving L2CAP Create Channel Request set the channel as L2CAP_FCS_NONE. Then in "L2CAP Config req" following field will be set: "FCS Option 0x00 (No FCS)". So by default High Speed channels have no FCS. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f44c5429b4f0..b52f66d22437 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -4286,6 +4286,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, mgr->bredr_chan = chan; chan->hs_hcon = hs_hcon; + chan->fcs = L2CAP_FCS_NONE; conn->mtu = hdev->block_mtu; } -- cgit v1.2.3 From 2b2fec4d08a0aabe20d2e749cb7978f04217af65 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Tue, 20 Nov 2012 11:38:54 +0100 Subject: Bluetooth: Remove OOB data if device was discovered in band OOB authentication mechanism should be used only if pairing process has been activated by previous OOB information exchange (Core Spec 4.0 , vol. 1, Part A, 5.1.4.3). Stored OOB data for specific device should be removed if that device was discovered in band later on. Signed-off-by: Szymon Janc Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e3a49db9cfcb..81f4bac098c9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -434,6 +434,8 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, BT_DBG("cache %p, %pMR", cache, &data->bdaddr); + hci_remove_remote_oob_data(hdev, &data->bdaddr); + if (ssp) *ssp = data->ssp_mode; -- cgit v1.2.3 From dc2a0e20fbc85a71c63aa4330b496fda33f6bf80 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Tue, 20 Nov 2012 23:25:54 -0200 Subject: Bluetooth: Add missing lock nesting notation This patch fixes the following report, it happens when accepting rfcomm connections: [ 228.165378] ============================================= [ 228.165378] [ INFO: possible recursive locking detected ] [ 228.165378] 3.7.0-rc1-00536-gc1d5dc4 #120 Tainted: G W [ 228.165378] --------------------------------------------- [ 228.165378] bluetoothd/1341 is trying to acquire lock: [ 228.165378] (sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM){+.+...}, at: [] bt_accept_dequeue+0xa0/0x180 [bluetooth] [ 228.165378] [ 228.165378] but task is already holding lock: [ 228.165378] (sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM){+.+...}, at: [] rfcomm_sock_accept+0x58/0x2d0 [rfcomm] [ 228.165378] [ 228.165378] other info that might help us debug this: [ 228.165378] Possible unsafe locking scenario: [ 228.165378] [ 228.165378] CPU0 [ 228.165378] ---- [ 228.165378] lock(sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM); [ 228.165378] lock(sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM); [ 228.165378] [ 228.165378] *** DEADLOCK *** [ 228.165378] [ 228.165378] May be due to missing lock nesting notation Cc: stable@vger.kernel.org Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/sock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 4ddef57d03a7..ce3f6658f4b2 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -467,7 +467,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f long timeo; int err = 0; - lock_sock(sk); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; @@ -504,7 +504,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f release_sock(sk); timeo = schedule_timeout(timeo); - lock_sock(sk); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); -- cgit v1.2.3 From b9b5ef188e5a2222cfc16ef62a4703080750b451 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 21 Nov 2012 00:50:21 -0200 Subject: Bluetooth: cancel power_on work when unregistering the device We need to cancel the hci_power_on work in order to avoid it run when we try to free the hdev. [ 1434.201149] ------------[ cut here ]------------ [ 1434.204998] WARNING: at lib/debugobjects.c:261 debug_print_object+0x8e/0xb0() [ 1434.208324] ODEBUG: free active (active state 0) object type: work_struct hint: hci _power_on+0x0/0x90 [ 1434.210386] Pid: 8564, comm: trinity-child25 Tainted: G W 3.7.0-rc5-next- 20121112-sasha-00018-g2f4ce0e #127 [ 1434.210760] Call Trace: [ 1434.210760] [] ? debug_print_object+0x8e/0xb0 [ 1434.210760] [] warn_slowpath_common+0x87/0xb0 [ 1434.210760] [] warn_slowpath_fmt+0x41/0x50 [ 1434.210760] [] debug_print_object+0x8e/0xb0 [ 1434.210760] [] ? hci_dev_open+0x310/0x310 [ 1434.210760] [] ? _raw_spin_unlock_irqrestore+0x55/0xa0 [ 1434.210760] [] __debug_check_no_obj_freed+0xa5/0x230 [ 1434.210760] [] ? bt_host_release+0x10/0x20 [ 1434.210760] [] debug_check_no_obj_freed+0x15/0x20 [ 1434.210760] [] kfree+0x227/0x330 [ 1434.210760] [] bt_host_release+0x10/0x20 [ 1434.210760] [] device_release+0x65/0xc0 [ 1434.210760] [] kobject_cleanup+0x145/0x190 [ 1434.210760] [] kobject_release+0xd/0x10 [ 1434.210760] [] kobject_put+0x4c/0x60 [ 1434.210760] [] put_device+0x12/0x20 [ 1434.210760] [] hci_free_dev+0x24/0x30 [ 1434.210760] [] vhci_release+0x31/0x60 [ 1434.210760] [] __fput+0x122/0x250 [ 1434.210760] [] ? rcu_user_exit+0x9d/0xd0 [ 1434.210760] [] ____fput+0x9/0x10 [ 1434.210760] [] task_work_run+0xb2/0xf0 [ 1434.210760] [] do_notify_resume+0x77/0xa0 [ 1434.210760] [] int_signal+0x12/0x17 [ 1434.210760] ---[ end trace a6d57fefbc8a8cc7 ]--- Cc: stable@vger.kernel.org Reported-by: Sasha Levin Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 81f4bac098c9..69eb644f0d48 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1854,6 +1854,8 @@ void hci_unregister_dev(struct hci_dev *hdev) for (i = 0; i < NUM_REASSEMBLY; i++) kfree_skb(hdev->reassembly[i]); + cancel_work_sync(&hdev->power_on); + if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); -- cgit v1.2.3 From b96e9c671b05f95126753a22145d4509d45ca197 Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Wed, 21 Nov 2012 10:51:11 +0100 Subject: Bluetooth: Add BT_DEFER_SETUP option to sco socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option will set the BT_SK_DEFER_SETUP bit in socket flags. Signed-off-by: Frédéric Dalleau Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 450cdcd88e5c..c6678f2bffc9 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -666,12 +666,31 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char { struct sock *sk = sock->sk; int err = 0; + u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { + + 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; + } + + if (opt) + set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + else + clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + break; + default: err = -ENOPROTOOPT; break; @@ -753,6 +772,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char lock_sock(sk); switch (optname) { + + case BT_DEFER_SETUP: + if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { + err = -EINVAL; + break; + } + + if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags), + (u32 __user *) optval)) + err = -EFAULT; + + break; + default: err = -ENOPROTOOPT; break; -- cgit v1.2.3 From 20714bfef84d3e690c9c6f8e9cd46543b5ae1eed Mon Sep 17 00:00:00 2001 From: Frédéric Dalleau Date: Wed, 21 Nov 2012 10:51:12 +0100 Subject: Bluetooth: Implement deferred sco socket setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to authenticate and configure an incoming SCO connection, the BT_DEFER_SETUP option was added. This option is intended to defer reply to Connect Request on SCO sockets. When a connection is requested, the listening socket is unblocked but the effective connection setup happens only on first recv. Any send between accept and recv fails with -ENOTCONN. Signed-off-by: Frédéric Dalleau Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 9 ++++--- net/bluetooth/hci_event.c | 52 ++++++++++++++++++++++++++++++++++++---- net/bluetooth/sco.c | 35 ++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 10 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ef5b85dac3f7..76891a914e75 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -376,7 +376,7 @@ extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); -extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); +extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status); extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); @@ -577,6 +577,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); +void hci_conn_accept(struct hci_conn *conn, int mask); struct hci_chan *hci_chan_create(struct hci_conn *conn); void hci_chan_del(struct hci_chan *chan); @@ -779,8 +780,10 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR) /* ----- HCI protocols ----- */ +#define HCI_PROTO_DEFER 0x01 + static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, - __u8 type) + __u8 type, __u8 *flags) { switch (type) { case ACL_LINK: @@ -788,7 +791,7 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, case SCO_LINK: case ESCO_LINK: - return sco_connect_ind(hdev, bdaddr); + return sco_connect_ind(hdev, bdaddr, flags); default: BT_ERR("unknown link type %d", type); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9f5c5f244502..3843f1897c86 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2047,15 +2047,53 @@ unlock: hci_conn_check_pending(hdev); } +void hci_conn_accept(struct hci_conn *conn, int mask) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p", conn); + + conn->state = BT_CONFIG; + + if (!lmp_esco_capable(hdev)) { + struct hci_cp_accept_conn_req cp; + + bacpy(&cp.bdaddr, &conn->dst); + + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) + cp.role = 0x00; /* Become master */ + else + cp.role = 0x01; /* Remain slave */ + + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); + } else /* lmp_esco_capable(hdev)) */ { + struct hci_cp_accept_sync_conn_req cp; + + bacpy(&cp.bdaddr, &conn->dst); + cp.pkt_type = cpu_to_le16(conn->pkt_type); + + cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40); + cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40); + cp.max_latency = __constant_cpu_to_le16(0xffff); + cp.content_format = cpu_to_le16(hdev->voice_setting); + cp.retrans_effort = 0xff; + + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, + sizeof(cp), &cp); + } +} + static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; + __u8 flags = 0; BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr, ev->link_type); - mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); + mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type, + &flags); if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) { @@ -2081,12 +2119,13 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) } memcpy(conn->dev_class, ev->dev_class, 3); - conn->state = BT_CONNECT; hci_dev_unlock(hdev); - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { + if (ev->link_type == ACL_LINK || + (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) { struct hci_cp_accept_conn_req cp; + conn->state = BT_CONNECT; bacpy(&cp.bdaddr, &ev->bdaddr); @@ -2097,8 +2136,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); - } else { + } else if (!(flags & HCI_PROTO_DEFER)) { struct hci_cp_accept_sync_conn_req cp; + conn->state = BT_CONNECT; bacpy(&cp.bdaddr, &ev->bdaddr); cp.pkt_type = cpu_to_le16(conn->pkt_type); @@ -2111,6 +2151,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(cp), &cp); + } else { + conn->state = BT_CONNECT2; + hci_proto_connect_cfm(conn, 0); + hci_conn_put(conn); } } else { /* Connection rejected */ diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c6678f2bffc9..eea17cdcaf7f 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -397,6 +397,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent) if (parent) { sk->sk_type = parent->sk_type; + bt_sk(sk)->flags = bt_sk(parent)->flags; security_sk_clone(parent, sk); } } @@ -662,6 +663,28 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, return err; } +static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t len, int flags) +{ + struct sock *sk = sock->sk; + struct sco_pinfo *pi = sco_pi(sk); + + lock_sock(sk); + + if (sk->sk_state == BT_CONNECT2 && + test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { + hci_conn_accept(pi->conn->hcon, 0); + sk->sk_state = BT_CONFIG; + + release_sock(sk); + return 0; + } + + release_sock(sk); + + return bt_sock_recvmsg(iocb, sock, msg, len, flags); +} + static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) { struct sock *sk = sock->sk; @@ -906,7 +929,10 @@ static void sco_conn_ready(struct sco_conn *conn) hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); - sk->sk_state = BT_CONNECTED; + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags)) + sk->sk_state = BT_CONNECT2; + else + sk->sk_state = BT_CONNECTED; /* Wake up parent */ parent->sk_data_ready(parent, 1); @@ -919,7 +945,7 @@ done: } /* ----- SCO interface with lower layer (HCI) ----- */ -int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) +int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) { struct sock *sk; struct hlist_node *node; @@ -936,6 +962,9 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm |= HCI_LM_ACCEPT; + + if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) + *flags |= HCI_PROTO_DEFER; break; } } @@ -1024,7 +1053,7 @@ static const struct proto_ops sco_sock_ops = { .accept = sco_sock_accept, .getname = sco_sock_getname, .sendmsg = sco_sock_sendmsg, - .recvmsg = bt_sock_recvmsg, + .recvmsg = sco_sock_recvmsg, .poll = bt_sock_poll, .ioctl = bt_sock_ioctl, .mmap = sock_no_mmap, -- cgit v1.2.3 From ffa88e02bc67a1496fae762ad899e8f49136e7a1 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Fri, 23 Nov 2012 16:50:51 -0200 Subject: Bluetooth: Move double negation to macros Some comparisons needs to double negation(!!) in order to make the value of the field boolean. Add it to the macro makes the code more readable. Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 6 +++--- net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/mgmt.c | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 76891a914e75..2f2b743f5b10 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -767,7 +767,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) #define lmp_pause_enc_capable(dev) ((dev)->features[5] & LMP_PAUSE_ENC) #define lmp_ext_inq_capable(dev) ((dev)->features[6] & LMP_EXT_INQ) -#define lmp_le_br_capable(dev) ((dev)->features[6] & LMP_SIMUL_LE_BR) +#define lmp_le_br_capable(dev) !!((dev)->features[6] & LMP_SIMUL_LE_BR) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) #define lmp_lsto_capable(dev) ((dev)->features[7] & LMP_LSTO) @@ -776,8 +776,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn); /* ----- Extended LMP capabilities ----- */ #define lmp_host_ssp_capable(dev) ((dev)->host_features[0] & LMP_HOST_SSP) -#define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) -#define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR) +#define lmp_host_le_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE) +#define lmp_host_le_br_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE_BREDR) /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3843f1897c86..705078a0cc39 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -794,10 +794,10 @@ static void hci_set_le_support(struct hci_dev *hdev) if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { cp.le = 1; - cp.simul = !!lmp_le_br_capable(hdev); + cp.simul = lmp_le_br_capable(hdev); } - if (cp.le != !!lmp_host_le_capable(hdev)) + if (cp.le != lmp_host_le_capable(hdev)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dedbb1d8b2d2..5d0ef759ef49 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1225,7 +1225,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } val = !!cp->val; - enabled = !!lmp_host_le_capable(hdev); + enabled = lmp_host_le_capable(hdev); if (!hdev_is_powered(hdev) || val == enabled) { bool changed = false; @@ -1261,7 +1261,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (val) { hci_cp.le = val; - hci_cp.simul = !!lmp_le_br_capable(hdev); + hci_cp.simul = lmp_le_br_capable(hdev); } err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), @@ -2924,13 +2924,13 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) struct hci_cp_write_le_host_supported cp; cp.le = 1; - cp.simul = !!lmp_le_br_capable(hdev); + cp.simul = lmp_le_br_capable(hdev); /* Check first if we already have the right * host state (host features set) */ - if (cp.le != !!lmp_host_le_capable(hdev) || - cp.simul != !!lmp_host_le_br_capable(hdev)) + if (cp.le != lmp_host_le_capable(hdev) || + cp.simul != lmp_host_le_br_capable(hdev)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); -- cgit v1.2.3 From 5e4e3972b8da23e270fa37670caec4d32f2197f7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 28 Nov 2012 17:59:39 +0200 Subject: Bluetooth: Refactor l2cap_send_disconn_req l2cap_send_disconn_req takes 3 parameters of which conn might be derived from chan. Make this conversion inside l2cap_send_disconn_req. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 56 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b52f66d22437..f7ee037c7934 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -53,8 +53,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data); static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); -static void l2cap_send_disconn_req(struct l2cap_conn *conn, - struct l2cap_chan *chan, int err); +static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event); @@ -632,7 +631,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { __set_chan_timer(chan, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, chan, reason); + l2cap_send_disconn_req(chan, reason); } else l2cap_chan_del(chan, reason); break; @@ -1180,10 +1179,10 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) } } -static void l2cap_send_disconn_req(struct l2cap_conn *conn, - struct l2cap_chan *chan, int err) +static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err) { struct sock *sk = chan->sk; + struct l2cap_conn *conn = chan->conn; struct l2cap_disconn_req req; if (!conn) @@ -1960,7 +1959,7 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) if (chan->max_tx != 0 && bt_cb(skb)->control.retries > chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); l2cap_seq_list_clear(&chan->retrans_list); break; } @@ -2666,7 +2665,7 @@ static void l2cap_tx_state_wait_f(struct l2cap_chan *chan, __set_monitor_timer(chan); chan->retry_count++; } else { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); + l2cap_send_disconn_req(chan, ECONNABORTED); } break; default: @@ -3877,7 +3876,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, /* Complete config. */ len = l2cap_parse_conf_req(chan, rsp); if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto unlock; } @@ -3899,7 +3898,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, err = l2cap_ertm_init(chan); if (err < 0) - l2cap_send_disconn_req(chan->conn, chan, -err); + l2cap_send_disconn_req(chan, -err); else l2cap_chan_ready(chan); @@ -3967,7 +3966,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, len = l2cap_parse_conf_rsp(chan, rsp->data, len, buf, &result); if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -3988,7 +3987,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, char req[64]; if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -3997,7 +3996,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, len = l2cap_parse_conf_rsp(chan, rsp->data, len, req, &result); if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -4013,7 +4012,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_chan_set_err(chan, ECONNRESET); __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); - l2cap_send_disconn_req(conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto done; } @@ -4030,7 +4029,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, err = l2cap_ertm_init(chan); if (err < 0) - l2cap_send_disconn_req(chan->conn, chan, -err); + l2cap_send_disconn_req(chan, -err); else l2cap_chan_ready(chan); } @@ -4392,7 +4391,7 @@ static void l2cap_logical_fail(struct l2cap_chan *chan) /* Logical link setup failed */ if (chan->state != BT_CONNECTED) { /* Create channel failure, disconnect */ - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -4435,7 +4434,7 @@ static void l2cap_logical_finish_create(struct l2cap_chan *chan, err = l2cap_ertm_init(chan); if (err < 0) - l2cap_send_disconn_req(chan->conn, chan, -err); + l2cap_send_disconn_req(chan, -err); else l2cap_chan_ready(chan); } @@ -5400,7 +5399,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan, if (control->reqseq == chan->next_tx_seq) { BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5414,7 +5413,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan, if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5458,7 +5457,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan, if (control->reqseq == chan->next_tx_seq) { BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5467,7 +5466,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan, if (chan->max_tx && skb && bt_cb(skb)->control.retries >= chan->max_tx) { BT_DBG("Retry limit exceeded (%d)", chan->max_tx); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); return; } @@ -5651,8 +5650,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, break; case L2CAP_TXSEQ_INVALID: default: - l2cap_send_disconn_req(chan->conn, chan, - ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); break; } break; @@ -5785,8 +5783,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, break; case L2CAP_TXSEQ_INVALID: default: - l2cap_send_disconn_req(chan->conn, chan, - ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); break; } break; @@ -5981,7 +5978,7 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d", control->reqseq, chan->next_tx_seq, chan->expected_ack_seq); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); } return err; @@ -6050,7 +6047,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) len -= L2CAP_FCS_SIZE; if (len > chan->mps) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto drop; } @@ -6075,8 +6072,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) } if (err) - l2cap_send_disconn_req(chan->conn, chan, - ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); } else { const u8 rx_func_to_event[4] = { L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ, @@ -6093,7 +6089,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) if (len != 0) { BT_ERR("Trailing bytes: %d in sframe", len); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); goto drop; } @@ -6104,7 +6100,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) event = rx_func_to_event[control->super]; if (l2cap_rx(chan, control, skb, event)) - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + l2cap_send_disconn_req(chan, ECONNRESET); } return 0; -- cgit v1.2.3 From ced5c338d7b696021058c23fb6a286def2171df5 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 28 Nov 2012 17:59:42 +0200 Subject: Bluetooth: AMP: Mark controller radio powered down after HCIDEVDOWN After getting HCIDEVDOWN controller did not mark itself as 0x00 which means: "The Controller radio is available but is currently physically powered down". The result was even if the hdev was down we return in controller list value 0x01 "status 0x01 (Bluetooth only)". Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 69eb644f0d48..ec7d3a7adf74 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -861,6 +861,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Clear flags */ hdev->flags = 0; + /* Controller radio is available but is currently powered down */ + hdev->amp_status = 0; + memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); -- cgit v1.2.3 From 5d05416e0907b0dd30b62b002bed3b85e6efbd61 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:05 +0200 Subject: Bluetooth: AMP: Check that AMP is present and active Before starting quering remote AMP controllers make sure that there is local active AMP controller. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/hci_core.h | 16 ++++++++++++++++ net/bluetooth/l2cap_core.c | 1 + 2 files changed, 17 insertions(+) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2f2b743f5b10..014a2eaa5389 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -779,6 +779,22 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE) #define lmp_host_le_br_capable(dev) !!((dev)->host_features[0] & LMP_HOST_LE_BREDR) +/* returns true if at least one AMP active */ +static inline bool hci_amp_capable(void) +{ + struct hci_dev *hdev; + bool ret = false; + + read_lock(&hci_dev_list_lock); + list_for_each_entry(hdev, &hci_dev_list, list) + if (hdev->amp_type == HCI_AMP && + test_bit(HCI_UP, &hdev->flags)) + ret = true; + read_unlock(&hci_dev_list_lock); + + return ret; +} + /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f7ee037c7934..d8cffdbf0d37 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1013,6 +1013,7 @@ static bool __amp_capable(struct l2cap_chan *chan) struct l2cap_conn *conn = chan->conn; if (enable_hs && + hci_amp_capable() && chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && conn->fixed_chan_mask & L2CAP_FC_A2MP) return true; -- cgit v1.2.3 From 60918918a9f3455859a4be0d4e381003cbdb843f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:06 +0200 Subject: Bluetooth: Fix missing L2CAP EWS Conf parameter If L2CAP_FEAT_FCS is not supported we sould miss EWS option configuration because of break. Make code more readable by combining FCS configuration in the single block. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d8cffdbf0d37..d22d183ddd45 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3106,18 +3106,17 @@ done: if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { - chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); - } - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, chan->tx_win); + + if (chan->conn->feat_mask & L2CAP_FEAT_FCS) + if (chan->fcs == L2CAP_FCS_NONE || + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + chan->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, + chan->fcs); + } break; case L2CAP_MODE_STREAMING: @@ -3139,14 +3138,13 @@ done: if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) l2cap_add_opt_efs(&ptr, chan); - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { - chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); - } + if (chan->conn->feat_mask & L2CAP_FEAT_FCS) + if (chan->fcs == L2CAP_FCS_NONE || + test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + chan->fcs = L2CAP_FCS_NONE; + l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, + chan->fcs); + } break; } -- cgit v1.2.3 From cbabee788ff881b87bbaf258caf080b49a61fa43 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:07 +0200 Subject: Bluetooth: Process receiving FCS_NONE in L2CAP Conf Rsp Process L2CAP Config rsp Pending with FCS Option 0x00 (No FCS) which is sent by Motorola Windows 7 Bluetooth stack. The trace is shown below (all other options are skipped). ... < ACL data: handle 1 flags 0x00 dlen 48 L2CAP(s): Config req: dcid 0x0043 flags 0x00 clen 36 ... FCS Option 0x00 (No FCS) > ACL data: handle 1 flags 0x02 dlen 48 L2CAP(s): Config req: dcid 0x0041 flags 0x00 clen 36 ... FCS Option 0x01 (CRC16 Check) < ACL data: handle 1 flags 0x00 dlen 47 L2CAP(s): Config rsp: scid 0x0043 flags 0x00 result 4 clen 33 Pending ... > ACL data: handle 1 flags 0x02 dlen 50 L2CAP(s): Config rsp: scid 0x0041 flags 0x00 result 4 clen 36 Pending ... FCS Option 0x00 (No FCS) < ACL data: handle 1 flags 0x00 dlen 14 L2CAP(s): Config rsp: scid 0x0043 flags 0x00 result 0 clen 0 Success > ACL data: handle 1 flags 0x02 dlen 14 L2CAP(s): Config rsp: scid 0x0041 flags 0x00 result 0 clen 0 Success ... Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- net/bluetooth/l2cap_core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d22d183ddd45..8c432771d6f9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3431,6 +3431,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); break; + + case L2CAP_CONF_FCS: + if (*result == L2CAP_CONF_PENDING) + if (val == L2CAP_FCS_NONE) + set_bit(CONF_NO_FCS_RECV, + &chan->conf_state); + break; } } -- cgit v1.2.3 From f2592d3ee3c5817981f343b90bfb9c5612f38d23 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 29 Nov 2012 17:46:08 +0200 Subject: Bluetooth: trivial: Change NO_FCS_RECV to RECV_NO_FCS Make code more readable by changing CONF_NO_FCS_RECV which is read as "No L2CAP FCS option received" to CONF_RECV_NO_FCS which means "Received L2CAP option NO_FCS". This flag really means that we have received L2CAP FRAME CHECK SEQUENCE (FCS) OPTION with value "No FCS". Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f57fab04e7c5..7588ef44ebaf 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -611,7 +611,7 @@ enum { CONF_MTU_DONE, CONF_MODE_DONE, CONF_CONNECT_PEND, - CONF_NO_FCS_RECV, + CONF_RECV_NO_FCS, CONF_STATE2_DEVICE, CONF_EWS_RECV, CONF_LOC_CONF_PEND, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8c432771d6f9..2c78208d793e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3112,7 +3112,7 @@ done: if (chan->conn->feat_mask & L2CAP_FEAT_FCS) if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); @@ -3140,7 +3140,7 @@ done: if (chan->conn->feat_mask & L2CAP_FEAT_FCS) if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { + test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) { chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); @@ -3196,7 +3196,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) - set_bit(CONF_NO_FCS_RECV, &chan->conf_state); + set_bit(CONF_RECV_NO_FCS, &chan->conf_state); break; case L2CAP_CONF_EFS: @@ -3435,7 +3435,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, case L2CAP_CONF_FCS: if (*result == L2CAP_CONF_PENDING) if (val == L2CAP_FCS_NONE) - set_bit(CONF_NO_FCS_RECV, + set_bit(CONF_RECV_NO_FCS, &chan->conf_state); break; } @@ -3807,7 +3807,7 @@ static inline void set_default_fcs(struct l2cap_chan *chan) */ if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) chan->fcs = L2CAP_FCS_NONE; - else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) + else if (!test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) chan->fcs = L2CAP_FCS_CRC16; } -- cgit v1.2.3 From 0b27a4b97cb1874503c78453c0903df53c0c86b2 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Mon, 3 Dec 2012 15:36:51 -0200 Subject: Revert "Bluetooth: Fix possible deadlock in SCO code" This reverts commit 269c4845d5b3627b95b1934107251bacbe99bb68. The commit was causing dead locks and NULL dereferences in the sco code: [28084.104013] BUG: soft lockup - CPU#0 stuck for 22s! [kworker/u:0H:7] [28084.104021] Modules linked in: btusb bluetooth ] _raw_spin_lock+0xd/0x10 [28084.104021] [] sco_conn_del+0x58/0x1b0 [bluetooth] [28084.104021] [] sco_connect_cfm+0xb9/0x2b0 [bluetooth] [28084.104021] [] hci_sync_conn_complete_evt.isra.94+0x1c9/0x260 [bluetooth] [28084.104021] [] hci_event_packet+0x74d/0x2b40 [bluetooth] [28084.104021] [] ? __kfree_skb+0x3d/0x90 [28084.104021] [] ? kfree_skb+0x36/0x90 [28084.104021] [] ? hci_send_to_monitor+0x10e/0x190 [bluetooth] [28084.104021] [] ? hci_send_to_monitor+0x10e/0x190 [bluetooth] Cc: stable@vger.kernel.org Reported-by: Chan-yeol Park Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index eea17cdcaf7f..531a93d613d4 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -131,15 +131,6 @@ static int sco_conn_del(struct hci_conn *hcon, int err) sco_sock_clear_timer(sk); sco_chan_del(sk, err); bh_unlock_sock(sk); - - sco_conn_lock(conn); - conn->sk = NULL; - sco_pi(sk)->conn = NULL; - sco_conn_unlock(conn); - - if (conn->hcon) - hci_conn_put(conn->hcon); - sco_sock_kill(sk); } @@ -885,6 +876,16 @@ static void sco_chan_del(struct sock *sk, int err) BT_DBG("sk %p, conn %p, err %d", sk, conn, err); + if (conn) { + sco_conn_lock(conn); + conn->sk = NULL; + sco_pi(sk)->conn = NULL; + sco_conn_unlock(conn); + + if (conn->hcon) + hci_conn_put(conn->hcon); + } + sk->sk_state = BT_CLOSED; sk->sk_err = err; sk->sk_state_change(sk); -- cgit v1.2.3 From 4529eefad087f97b33c0f31984d924b1f15d7bae Mon Sep 17 00:00:00 2001 From: "Lamarque V. Souza" Date: Thu, 6 Dec 2012 12:39:55 -0200 Subject: HID: hidp: fallback to input session properly if hid is blacklisted This patch against kernel 3.7.0-rc8 fixes a kernel oops when turning on the bluetooth mouse with id 0458:0058 [1]. The mouse in question supports both input and hid sessions, however it is blacklisted in drivers/hid/hid-core.c so the input session is one that should be used. Long ago (around kernel 3.0.0) some changes in the bluetooth subsystem made the kernel do not fallback to input session when hid session is not supported or blacklisted. This patch restore that behaviour by making the kernel try the input session if hid_add_device returns ENODEV. The patch exports hid_ignore() from hid-core.c so that it can be used in the bluetooth subsystem. [1] https://bugzilla.kernel.org/show_bug.cgi?id=39882 Signed-off-by: Lamarque V. Souza Acked-by: Gustavo Padovan Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 11 ++++++++--- include/linux/hid.h | 1 + net/bluetooth/hidp/core.c | 9 ++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a7550bb30836..2629d5240d63 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2145,8 +2145,13 @@ static const struct hid_device_id hid_mouse_ignore_list[] = { { } }; -static bool hid_ignore(struct hid_device *hdev) +bool hid_ignore(struct hid_device *hdev) { + if (hdev->quirks & HID_QUIRK_NO_IGNORE) + return false; + if (hdev->quirks & HID_QUIRK_IGNORE) + return true; + switch (hdev->vendor) { case USB_VENDOR_ID_CODEMERCS: /* ignore all Code Mercenaries IOWarrior devices */ @@ -2201,6 +2206,7 @@ static bool hid_ignore(struct hid_device *hdev) return !!hid_match_id(hdev, hid_ignore_list); } +EXPORT_SYMBOL_GPL(hid_ignore); int hid_add_device(struct hid_device *hdev) { @@ -2212,8 +2218,7 @@ int hid_add_device(struct hid_device *hdev) /* we need to kill them here, otherwise they will stay allocated to * wait for coming driver */ - if (!(hdev->quirks & HID_QUIRK_NO_IGNORE) - && (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE))) + if (hid_ignore(hdev)) return -ENODEV; /* diff --git a/include/linux/hid.h b/include/linux/hid.h index 7e1f37db7582..abce7eb4f258 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -721,6 +721,7 @@ struct hid_ll_driver { extern int hid_debug; +extern bool hid_ignore(struct hid_device *); extern int hid_add_device(struct hid_device *); extern void hid_destroy_device(struct hid_device *); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index ccd985da6518..1f4a32bb2971 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -941,6 +941,13 @@ static int hidp_setup_hid(struct hidp_session *session, hid->hid_get_raw_report = hidp_get_raw_report; hid->hid_output_raw_report = hidp_output_raw_report; + /* True if device is blacklisted in drivers/hid/hid-core.c */ + if (hid_ignore(hid)) { + hid_destroy_device(session->hid); + session->hid = NULL; + return -ENODEV; + } + return 0; fault: @@ -1013,7 +1020,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (req->rd_size > 0) { err = hidp_setup_hid(session, req); - if (err) + if (err && err != -ENODEV) goto purge; } -- cgit v1.2.3 From dbccd791a3fbbdac12c33834b73beff3984988e9 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Tue, 11 Dec 2012 08:51:19 +0100 Subject: Bluetooth: Fix sending HCI commands after reset After sending reset command wait for its command complete event before sending next command. Some chips sends CC event for command received before reset if reset was send before chip replied with CC. This is also required by specification that host shall not send additional HCI commands before receiving CC for reset. < HCI Command: Reset (0x03|0x0003) plen 0 [hci0] 18.404612 > HCI Event: Command Complete (0x0e) plen 4 [hci0] 18.405850 Write Extended Inquiry Response (0x03|0x0052) ncmd 1 Status: Success (0x00) < HCI Command: Read Local Supported Features (0x04|0x0003) plen 0 [hci0] 18.406079 > HCI Event: Command Complete (0x0e) plen 4 [hci0] 18.407864 Reset (0x03|0x0003) ncmd 1 Status: Success (0x00) < HCI Command: Read Local Supported Features (0x04|0x0003) plen 0 [hci0] 18.408062 > HCI Event: Command Complete (0x0e) plen 12 [hci0] 18.408835 Signed-off-by: Szymon Janc Cc: stable@vger.kernel.org Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 705078a0cc39..81b44481d0d9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2688,7 +2688,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); - if (ev->ncmd) { + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) queue_work(hdev->workqueue, &hdev->cmd_work); -- cgit v1.2.3 From 0a9ab9bdb3e891762553f667066190c1d22ad62b Mon Sep 17 00:00:00 2001 From: Anderson Lizardo Date: Sun, 6 Jan 2013 18:28:53 -0400 Subject: Bluetooth: Fix incorrect strncpy() in hidp_setup_hid() The length parameter should be sizeof(req->name) - 1 because there is no guarantee that string provided by userspace will contain the trailing '\0'. Can be easily reproduced by manually setting req->name to 128 non-zero bytes prior to ioctl(HIDPCONNADD) and checking the device name setup on input subsystem: $ cat /sys/devices/pnp0/00\:04/tty/ttyS0/hci0/hci0\:1/input8/name AAAAAA[...]AAAAAAAAf0:af:f0:af:f0:af ("f0:af:f0:af:f0:af" is the device bluetooth address, taken from "phys" field in struct hid_device due to overflow.) Cc: stable@vger.kernel.org Signed-off-by: Anderson Lizardo Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hidp/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index b2bcbe2dc328..a7352ff3fd1e 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -931,7 +931,7 @@ static int hidp_setup_hid(struct hidp_session *session, hid->version = req->version; hid->country = req->country; - strncpy(hid->name, req->name, 128); + strncpy(hid->name, req->name, sizeof(req->name) - 1); snprintf(hid->phys, sizeof(hid->phys), "%pMR", &bt_sk(session->ctrl_sock->sk)->src); -- cgit v1.2.3 From 7b064edae38d62d8587a8c574f93b53ce75ae749 Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Thu, 10 Jan 2013 10:28:35 +0530 Subject: Bluetooth: Fix authentication if acl data comes before remote feature evt If remote device sends l2cap info request before read_remote_ext_feature completes then mgmt_connected will be sent in hci_acldata_packet() and remote name request wont be sent and eventually authentication wont happen Hcidump log of the issue < HCI Command: Create Connection (0x01|0x0005) plen 13 bdaddr BC:85:1F:74:7F:29 ptype 0xcc18 rswitch 0x01 clkoffset 0x4bf7 (valid) Packet type: DM1 DM3 DM5 DH1 DH3 DH5 > HCI Event: Command Status (0x0f) plen 4 Create Connection (0x01|0x0005) status 0x00 ncmd 1 > HCI Event: Connect Complete (0x03) plen 11 status 0x00 handle 12 bdaddr BC:85:1F:74:7F:29 type ACL encrypt 0x00 < HCI Command: Read Remote Supported Features (0x01|0x001b) plen 2 handle 12 > HCI Event: Command Status (0x0f) plen 4 Read Remote Supported Features (0x01|0x001b) status 0x00 ncmd 1 > HCI Event: Read Remote Supported Features (0x0b) plen 11 status 0x00 handle 12 Features: 0xbf 0xfe 0xcf 0xfe 0xdb 0xff 0x7b 0x87 > HCI Event: Max Slots Change (0x1b) plen 3 handle 12 slots 5 < HCI Command: Read Remote Extended Features (0x01|0x001c) plen 3 handle 12 page 1 > HCI Event: Command Status (0x0f) plen 4 Read Remote Extended Features (0x01|0x001c) status 0x00 ncmd 1 > ACL data: handle 12 flags 0x02 dlen 10 L2CAP(s): Info req: type 2 < ACL data: handle 12 flags 0x00 dlen 16 L2CAP(s): Info rsp: type 2 result 0 Extended feature mask 0x00b8 Enhanced Retransmission mode Streaming mode FCS Option Fixed Channels > HCI Event: Read Remote Extended Features (0x23) plen 13 status 0x00 handle 12 page 1 max 1 Features: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 > ACL data: handle 12 flags 0x02 dlen 10 L2CAP(s): Info req: type 3 < ACL data: handle 12 flags 0x00 dlen 20 L2CAP(s): Info rsp: type 3 result 0 Fixed channel list 0x00000002 L2CAP Signalling Channel > HCI Event: Number of Completed Packets (0x13) plen 5 handle 12 packets 2 This patch moves sending mgmt_connected from hci_acldata_packet() to l2cap_connect_req() since this code is to handle the scenario remote device sends l2cap connect req too fast Signed-off-by: Jaganath Kanakkassery Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 8 -------- net/bluetooth/l2cap_core.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 596660d37c5e..0f78e34220c9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2810,14 +2810,6 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF); - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); - hci_dev_unlock(hdev); - /* Send to upper protocol */ l2cap_recv_acldata(conn, skb, flags); return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2c78208d793e..22e658322845 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3727,6 +3727,17 @@ sendresp: static int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { + struct hci_dev *hdev = conn->hcon->hdev; + struct hci_conn *hcon = conn->hcon; + + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->dev_flags) && + !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags)) + mgmt_device_connected(hdev, &hcon->dst, hcon->type, + hcon->dst_type, 0, NULL, 0, + hcon->dev_class); + hci_dev_unlock(hdev); + l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); return 0; } -- cgit v1.2.3 From b7e98b5100aad9290d7f06fcb9d1e80f7f62f05f Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Thu, 3 Jan 2013 19:59:28 -0200 Subject: Bluetooth: Check if the hci connection exists in SCO shutdown Checking only for sco_conn seems to not be enough and lead to NULL dereferences in the code, check for hcon instead. <1>[11340.226404] BUG: unable to handle kernel NULL pointer dereference at 0000000 8 <4>[11340.226619] EIP is at __sco_sock_close+0xe8/0x1a0 <4>[11340.226629] EAX: f063a740 EBX: 00000000 ECX: f58f4544 EDX: 00000000 <4>[11340.226640] ESI: dec83e00 EDI: 5f9a081f EBP: e0fdff38 ESP: e0fdff1c <0>[11340.226674] Stack: <4>[11340.226682] c184db87 c1251028 dec83e00 e0fdff38 c1754aef dec83e00 00000000 e0fdff5c <4>[11340.226718] c184f587 e0fdff64 e0fdff68 5f9a081f e0fdff5c c1751852 d7813800 62262f10 <4>[11340.226752] e0fdff70 c1753c00 00000000 00000001 0000000d e0fdffac c175425c 00000041 <0>[11340.226793] Call Trace: <4>[11340.226813] [] ? sco_sock_clear_timer+0x27/0x60 <4>[11340.226831] [] ? local_bh_enable+0x68/0xd0 <4>[11340.226846] [] ? lock_sock_nested+0x4f/0x60 <4>[11340.226862] [] sco_sock_shutdown+0x67/0xb0 <4>[11340.226879] [] ? sockfd_lookup_light+0x22/0x80 <4>[11340.226897] [] sys_shutdown+0x30/0x60 <4>[11340.226912] [] sys_socketcall+0x1dc/0x2a0 <4>[11340.226929] [] ? trace_hardirqs_on_thunk+0xc/0x10 <4>[11340.226944] [] syscall_call+0x7/0xb <4>[11340.226960] [] ? restore_cur+0x5e/0xd7 <0>[11340.226969] Code: ff 4b 08 0f 94 c0 84 c0 74 20 80 7b 19 01 74 2f b8 0a 00 00 Reported-by: Chuansheng Liu Signed-off-by: Gustavo Padovan --- net/bluetooth/sco.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 531a93d613d4..57f250c20e39 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -352,7 +352,7 @@ static void __sco_sock_close(struct sock *sk) case BT_CONNECTED: case BT_CONFIG: - if (sco_pi(sk)->conn) { + if (sco_pi(sk)->conn->hcon) { sk->sk_state = BT_DISCONN; sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT); hci_conn_put(sco_pi(sk)->conn->hcon); -- cgit v1.2.3