From: Andrei Emeltchenko <[email protected]>
Basic A2MP implementation.
Changes:
* PATCHv1: Rebasing against latest tree and minor fixes.
* RFCv7: Taking comments from review in linux-bluetooth.
* RFCv6: Remove some unneded check for sk since they are in different path
(A2MP decoded as a data channel wrt L2CAP signalling channel). Style fixes.
* RFCv5: Fix memory leaks, sparse warnings and taking comments from upstream.
* RFCv4: redesign code to use l2cap channel lock instead of socket lock
and general modifications. Basic implementation of HCI callback interface.
* RFCv3: redesign code to use l2cap functions instead of kernel sockets
L2CAP functions modified to work with channels without sk.
* RFCv2: rebased against "workqueue" patches.
* RFCv1: added refcnt to amp_mgr, fixed sleeping in atomic
Initially code was based of reference implementations below:
References: Code Aurora (git://codeaurora.org/kernel/msm.git) and Atheros
(search for: [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3)
implementations.
Andrei Emeltchenko (17):
Bluetooth: A2MP: Create A2MP channel
Bluetooth: A2MP: AMP Manager basic functions
Bluetooth: A2MP: Build and Send msg helpers
Bluetooth: A2MP: Add chan callbacks
Bluetooth: A2MP: Definitions for A2MP commands
Bluetooth: A2MP: Define A2MP status codes
Bluetooth: A2MP: Process A2MP messages
Bluetooth: A2MP: Process A2MP Command Reject
Bluetooth: A2MP: Process A2MP Discover Request
Bluetooth: A2MP: Process A2MP Change Notify
Bluetooth: A2MP: Process A2MP Get Info Request
Bluetooth: A2MP: Process A2MP Get AMP Assoc Request
Bluetooth: A2MP: Process A2MP Create Physlink Request
Bluetooth: A2MP: Process A2MP Disc Physlink Request
Bluetooth: A2MP: Process A2MP Command Responses
Bluetooth: A2MP: Handling fixed channels
Bluetooth: A2MP: Manage incoming connections
include/net/bluetooth/a2mp.h | 127 +++++++++
include/net/bluetooth/hci_core.h | 14 +
include/net/bluetooth/l2cap.h | 6 +
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 558 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 15 +
net/bluetooth/l2cap_core.c | 43 ++-
7 files changed, 757 insertions(+), 9 deletions(-)
create mode 100644 include/net/bluetooth/a2mp.h
create mode 100644 net/bluetooth/a2mp.c
--
1.7.9.5
Hi Mat,
On Tue, May 29, 2012 at 09:23:34AM -0700, Mat Martineau wrote:
> >>>Since A2MP is a fixed channel, it is not opened and closed like
> >>>typical L2CAP channels. Consider this scenario:
> >>>
> >>>1. Remote device decides to connect
> >>>2. ACL is created
> >>>3. Remote device sends A2MP discover
> >>>4. A2MP channel is set up, AMP manager is created, hci_conn_hold for
> >>>A2MP channel
> >>>5. Remote device connects L2CAP channel over BR/EDR. hci_conn_hold
> >>>for the data channel
> >>>6. A2MP communication to set up physical link
> >>>7. L2CAP channel move to AMP controller
> >>>8. Data sent over AMP.
> >>>9. L2CAP channel disconnected, hci_conn_put for the data channel
> >>>10. Device stays in range. hci_conn reference count is not 0, so
> >>>ACL is not closed.
> >>
> >>My concern here if A2MP does not reference hci_conn can hci_conn be
> >>removed while we still have A2MP chan in some situation?
>
> hci_conn_hold and hci_conn_put are not reference counts on the
> hci_conn structure in the typical way. They are reference counts
> for the ACL. When you do the last hci_conn_put, the ACL is
> disconnected after a timeout.
>
> With or without hci_conn_hold for the A2MP channel, the ACL can
> disappear at any time. The AMP manager must deal with that without
> crashing like it does in your trace.
OK, I found the bug, solution would be to define teardown for a2mp channel
to make sure all callbacks are cleared.
-static void a2mp_chan_no_teardown_cb(struct l2cap_chan *chan, int err)
+static void a2mp_chan_teardown_cb(struct l2cap_chan *chan, int err)
{
- BT_ERR("teardown for chan %p not implemented", chan);
+ struct hci_dev *hdev;
+
+ BT_DBG("chan %p", chan);
+
+ read_lock(&hci_dev_list_lock);
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ /* Iterate through AMP controllers */
+ if (hdev->amp_type == HCI_AMP)
+ hci_cb_clear(hdev);
+ }
+ read_unlock(&hci_dev_list_lock);
}
> >I just tested code with PTS and got one crash:
> >
> ><------8<--------------------------------------------------------------------
> >| [ 275.100760] [5] hci_chan_del: hci0 conn f3ff0800 chan e818e540
> >| [ 275.109610] [19] l2cap_send_rr_or_rnr: chan e806b800, poll 0
> >| [ 275.127922] [19] l2cap_send_sframe: chan e806b800, control f5ef3ed8
> >| [ 275.133532] [19] l2cap_send_sframe: reqseq 2, final 0, poll 0, super 0
> >| [ 275.173033] [5] hci_conn_del: hci0 conn f3ff0800 handle 11
> >| [ 275.181131] [5] hci_chan_list_flush: conn f3ff0800
> >| [ 275.202645] [5] amp_mgr_put: mgr e8260600
> ><------8<--------------------------------------------------------------------
> >
> >Here we deleted the last L2CAP channel
>
> Ok. The AMP manager should not attempt to send anything after this.
>
> >
> ><------8<------------------------------------------------------------------
> >| [ 275.295020] BUG: unable to handle kernel NULL pointer dereference at
> >| (null)
> >| [ 275.298791] IP: [<f8362e6c>] l2cap_do_send+0x1c/0xa0 [bluetooth]
> >| [ 275.298791] *pde = 00000000
> >| [ 275.298791] Oops: 0000 [#1] SMP
> ><------8<------------------------------------------------------------------
> >
> >Then we got the crash probably hci_conn deleted faster then we get reply
> >from AMP controller
>
> This looks like a crash when the AMP manager tries to send
> something. It should not be trying to send anything at this point.
>
> Once the AMP manager gets a state change callback with BT_CLOSED or
> a a close callback, amp_mgr->l2cap_conn and amp_mgr->a2mp_chan are
> invalid - even if the AMP manager has not been freed.
Sometimes we do not get this state change for a2mp channel.
Best regards
Andrei Emeltchenko
On Mon, 28 May 2012, Andrei Emeltchenko wrote:
> Hi Mat,
>
> On Mon, May 28, 2012 at 10:24:56AM +0300, Andrei Emeltchenko wrote:
>>>>> The risk is not memory leaks - if the other device closes the ACL,
>>>>> then the A2MP channel will be cleaned up. But if you had two BlueZ
>>>>> devices with A2MP connected to each other, the ACL would never close
>>>>> as long as the devices remained powered and in range! Nothing would
>>>>> trigger the flush as long as the hci_conn is held.
>>>>
>>>> Does it indicate that we have problem with rather freeing amp_mgr?
>>>
>>> It looks like the amp_mgr will be freed if the channel is closed.
>>> There's nothing that will close the channel except for:
>>>
>>> * Remote device disconnects the ACL
>>> * Remote device goes out of range
>>> * Local BR/EDR controller is powered down or reset
>>>
>>>>> I think the checks for hci_conn_put are necessary, even if they are
>>>>> ugly.
>>>>
>>>> I think that if I have hci_conn_hold when creating channel and
>>>> hci_conn_put when closing channel that does not differ from the case when
>>>> I do not have hci_conn_put/hold.
>>>
>>> If there was something that closed the A2MP channel, there would be
>>> no difference. But nothing closes the A2MP channel.
>>
>> I actually was thinking about timers which could do clean up.
>>
>>>> So you are probably referring to the case when hci_conn needs to be closed
>>>> but cannot because it has hci_conn_hold in a2mp channel?
>>>
>>> Yes.
>>>
>>>> This also means that we still have L2CAP channel (a2mp) hanging.
>>>> This seems right to me if I did not miss something.
>>>
>>> Since A2MP is a fixed channel, it is not opened and closed like
>>> typical L2CAP channels. Consider this scenario:
>>>
>>> 1. Remote device decides to connect
>>> 2. ACL is created
>>> 3. Remote device sends A2MP discover
>>> 4. A2MP channel is set up, AMP manager is created, hci_conn_hold for
>>> A2MP channel
>>> 5. Remote device connects L2CAP channel over BR/EDR. hci_conn_hold
>>> for the data channel
>>> 6. A2MP communication to set up physical link
>>> 7. L2CAP channel move to AMP controller
>>> 8. Data sent over AMP.
>>> 9. L2CAP channel disconnected, hci_conn_put for the data channel
>>> 10. Device stays in range. hci_conn reference count is not 0, so
>>> ACL is not closed.
>>
>> My concern here if A2MP does not reference hci_conn can hci_conn be
>> removed while we still have A2MP chan in some situation?
hci_conn_hold and hci_conn_put are not reference counts on the
hci_conn structure in the typical way. They are reference counts for
the ACL. When you do the last hci_conn_put, the ACL is disconnected
after a timeout.
With or without hci_conn_hold for the A2MP channel, the ACL can
disappear at any time. The AMP manager must deal with that without
crashing like it does in your trace.
> I just tested code with PTS and got one crash:
>
> <------8<--------------------------------------------------------------------
> | [ 275.100760] [5] hci_chan_del: hci0 conn f3ff0800 chan e818e540
> | [ 275.109610] [19] l2cap_send_rr_or_rnr: chan e806b800, poll 0
> | [ 275.127922] [19] l2cap_send_sframe: chan e806b800, control f5ef3ed8
> | [ 275.133532] [19] l2cap_send_sframe: reqseq 2, final 0, poll 0, super 0
> | [ 275.173033] [5] hci_conn_del: hci0 conn f3ff0800 handle 11
> | [ 275.181131] [5] hci_chan_list_flush: conn f3ff0800
> | [ 275.202645] [5] amp_mgr_put: mgr e8260600
> <------8<--------------------------------------------------------------------
>
> Here we deleted the last L2CAP channel
Ok. The AMP manager should not attempt to send anything after this.
>
> <------8<------------------------------------------------------------------
> | [ 275.295020] BUG: unable to handle kernel NULL pointer dereference at
> | (null)
> | [ 275.298791] IP: [<f8362e6c>] l2cap_do_send+0x1c/0xa0 [bluetooth]
> | [ 275.298791] *pde = 00000000
> | [ 275.298791] Oops: 0000 [#1] SMP
> <------8<------------------------------------------------------------------
>
> Then we got the crash probably hci_conn deleted faster then we get reply
> from AMP controller
This looks like a crash when the AMP manager tries to send something.
It should not be trying to send anything at this point.
Once the AMP manager gets a state change callback with BT_CLOSED or a
a close callback, amp_mgr->l2cap_conn and amp_mgr->a2mp_chan are
invalid - even if the AMP manager has not been freed.
>
> <------8<---------------------------------------------------------------------
> | [ 275.298791] EIP: [<f8362e6c>] l2cap_do_send+0x1c/0xa0 [bluetooth]
> | SS:ESP 0068:f5ef3e58
> | [ 275.298791] CR2: 0000000000000000
> | [ 276.553113] Bluetooth: hci1 command tx timeout
> | [ 276.553706] ---[ end trace b08f23ddb2b49a2e ]---
> | [ 276.577494] [30] hci_queue_cb: Queue cmd ed3d5540 opt e8260600
> | [ 276.723885] [30] l2cap_segment_sdu: chan e806b800, msg f5919e94, len 22
> | [ 276.790990] BUG: unable to handle kernel NULL pointer dereference at
> | 00000010
> | [ 276.794865] IP: [<f83694dd>] l2cap_chan_send+0x28d/0xb40 [bluetooth]
> <------8<---------------------------------------------------------------------
Regards,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Andrei,
* Andrei Emeltchenko <[email protected]> [2012-05-29 13:59:17 +0300]:
> From: Andrei Emeltchenko <[email protected]>
>
> Handle incoming A2MP connection by creating AMP manager and
> processing A2MP messages.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/a2mp.h | 4 ++++
> net/bluetooth/a2mp.c | 16 ++++++++++++++++
> net/bluetooth/l2cap_core.c | 19 +++++++++++++++----
> 3 files changed, 35 insertions(+), 4 deletions(-)
A 17 patches were applied to bluetooth-next. Thanks.
Gustavo
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Create Physlink Request.
Handles requests with invalid controler id as shown below:
...
> ACL data: handle 11 flags 0x02 dlen 50
A2MP: Create Physical Link req: local id 1 remote id 85
Assoc data:
<skipped>
< ACL data: handle 11 flags 0x00 dlen 15
A2MP: Create Physical Link rsp: local id 85 remote id 1 status 1
Invalid Controller ID
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 63c5394..870ef009 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -243,6 +243,43 @@ clean:
return 0;
}
+static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (void *) skb->data;
+
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+
+ hdev = hci_dev_get(req->remote_id);
+ if (!hdev || hdev->amp_type != HCI_AMP) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO process physlink create */
+
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+send_rsp:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -289,6 +326,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
break;
case A2MP_CREATEPHYSLINK_REQ:
+ err = a2mp_createphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for future A2MP Command Reject handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d07a8a5..0aac317 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,22 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Processing A2MP messages */
+static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cmd_rej *rej = (void *) skb->data;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*rej))
+ return -EINVAL;
+
+ BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason));
+
+ skb_pull(skb, sizeof(*rej));
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -89,6 +105,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
switch (hdr->code) {
case A2MP_COMMAND_REJ:
+ a2mp_command_rej(mgr, skb, hdr);
+ break;
+
case A2MP_DISCOVER_REQ:
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Implement basic processing for AMP Manager Protocol (A2MP).
Example below shows processing unrecognized command.
...
> ACL data: handle 11 flags 0x02 dlen 12
A2MP: code 0x00 ident 3 len 0
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Command Reject: reason (0) - Command not recognized
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 4ba67dc..d07a8a5 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,70 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Handle A2MP signalling */
+static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+ struct a2mp_cmd *hdr = (void *) skb->data;
+ struct amp_mgr *mgr = chan->data;
+ int err = 0;
+
+ amp_mgr_get(mgr);
+
+ while (skb->len >= sizeof(*hdr)) {
+ struct a2mp_cmd *hdr = (void *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+
+ BT_DBG("code 0x%02x id %d len %d", hdr->code, hdr->ident, len);
+
+ skb_pull(skb, sizeof(*hdr));
+
+ if (len > skb->len || !hdr->ident) {
+ err = -EINVAL;
+ break;
+ }
+
+ mgr->ident = hdr->ident;
+
+ switch (hdr->code) {
+ case A2MP_COMMAND_REJ:
+ case A2MP_DISCOVER_REQ:
+ case A2MP_CHANGE_NOTIFY:
+ case A2MP_GETINFO_REQ:
+ case A2MP_GETAMPASSOC_REQ:
+ case A2MP_CREATEPHYSLINK_REQ:
+ case A2MP_DISCONNPHYSLINK_REQ:
+ case A2MP_CHANGE_RSP:
+ case A2MP_DISCOVER_RSP:
+ case A2MP_GETINFO_RSP:
+ case A2MP_GETAMPASSOC_RSP:
+ case A2MP_CREATEPHYSLINK_RSP:
+ case A2MP_DISCONNPHYSLINK_RSP:
+ default:
+ BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (err) {
+ struct a2mp_cmd_rej rej;
+ rej.reason = __constant_cpu_to_le16(0);
+
+ BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
+
+ a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
+ &rej);
+ }
+
+ /* Always free skb and return success error code to prevent
+ from sending L2CAP Disconnect over A2MP channel */
+ kfree_skb(skb);
+
+ amp_mgr_put(mgr);
+
+ return 0;
+}
+
static void a2mp_chan_close_cb(struct l2cap_chan *chan)
{
l2cap_chan_destroy(chan);
@@ -114,6 +178,7 @@ static void a2mp_chan_no_ready(struct l2cap_chan *chan)
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .recv = a2mp_chan_recv_cb,
.close = a2mp_chan_close_cb,
.state_change = a2mp_chan_state_change_cb,
.alloc_skb = a2mp_chan_alloc_skb_cb,
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define A2MP command IDs and packet structures.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 73 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 654df60..7cbeb91 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -31,6 +31,79 @@ struct a2mp_cmd {
__u8 data[0];
} __packed;
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ 0x01
+struct a2mp_cmd_rej {
+ __le16 reason;
+ __u8 data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ 0x02
+struct a2mp_discov_req {
+ __le16 mtu;
+ __le16 ext_feat;
+} __packed;
+
+struct a2mp_cl {
+ __u8 id;
+ __u8 type;
+ __u8 status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP 0x03
+struct a2mp_discov_rsp {
+ __le16 mtu;
+ __le16 ext_feat;
+ struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+
+#define A2MP_GETINFO_REQ 0x06
+struct a2mp_info_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETINFO_RSP 0x07
+struct a2mp_info_rsp {
+ __u8 id;
+ __u8 status;
+ __le32 total_bw;
+ __le32 max_bw;
+ __le32 min_latency;
+ __le16 pal_cap;
+ __le16 assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ 0x08
+struct a2mp_amp_assoc_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP 0x09
+struct a2mp_amp_assoc_rsp {
+ __u8 id;
+ __u8 status;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ 0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP 0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 status;
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Add L2CAP chan ops callbacks.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 53f49a0..4ba67dc 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,8 +63,65 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static void a2mp_chan_close_cb(struct l2cap_chan *chan)
+{
+ l2cap_chan_destroy(chan);
+}
+
+static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state)
+{
+ struct amp_mgr *mgr = chan->data;
+
+ if (!mgr)
+ return;
+
+ chan = mgr->a2mp_chan;
+
+ BT_DBG("chan %p state %s", chan, state_to_string(state));
+
+ chan->state = state;
+
+ switch (state) {
+ case BT_CLOSED:
+ if (mgr)
+ amp_mgr_put(mgr);
+ break;
+ }
+}
+
+static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb)
+{
+ return bt_skb_alloc(len, GFP_KERNEL);
+}
+
+static struct l2cap_chan *a2mp_chan_no_new_conn_cb(struct l2cap_chan *chan)
+{
+ BT_ERR("new_connection for chan %p not implemented", chan);
+
+ return NULL;
+}
+
+static void a2mp_chan_no_teardown_cb(struct l2cap_chan *chan, int err)
+{
+ BT_ERR("teardown for chan %p not implemented", chan);
+}
+
+static void a2mp_chan_no_ready(struct l2cap_chan *chan)
+{
+ BT_ERR("ready for chan %p not implemented", chan);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .close = a2mp_chan_close_cb,
+ .state_change = a2mp_chan_state_change_cb,
+ .alloc_skb = a2mp_chan_alloc_skb_cb,
+
+ /* Not implemented for A2MP */
+ .new_connection = a2mp_chan_no_new_conn_cb,
+ .teardown = a2mp_chan_no_teardown_cb,
+ .ready = a2mp_chan_no_ready,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define AMP Manager and some basic functions.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 30 +++++++++++++++++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/a2mp.c | 54 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 4 +++
4 files changed, 89 insertions(+)
create mode 100644 include/net/bluetooth/a2mp.h
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
new file mode 100644
index 0000000..ff47540
--- /dev/null
+++ b/include/net/bluetooth/a2mp.h
@@ -0,0 +1,30 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 __A2MP_H
+#define __A2MP_H
+
+struct amp_mgr {
+ struct l2cap_conn *l2cap_conn;
+ struct l2cap_chan *a2mp_chan;
+ struct kref kref;
+ __u8 ident;
+ __u8 handle;
+ unsigned long flags;
+};
+
+void amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+
+#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d584a47..6e64b76 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -332,6 +332,7 @@ struct hci_conn {
void *l2cap_data;
void *sco_data;
void *smp_conn;
+ struct amp_mgr *amp_mgr;
struct hci_conn *link;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index de455a2..3c241c2 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -15,6 +15,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/a2mp.h>
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
@@ -67,3 +68,56 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
return chan;
}
+
+/* AMP Manager functions */
+void amp_mgr_get(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ kref_get(&mgr->kref);
+}
+
+static void amp_mgr_destroy(struct kref *kref)
+{
+ struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref);
+
+ BT_DBG("mgr %p", mgr);
+
+ kfree(mgr);
+}
+
+int amp_mgr_put(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ return kref_put(&mgr->kref, &_mgr_destroy);
+}
+
+static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
+{
+ struct amp_mgr *mgr;
+ struct l2cap_chan *chan;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return NULL;
+
+ BT_DBG("conn %p mgr %p", conn, mgr);
+
+ mgr->l2cap_conn = conn;
+
+ chan = a2mp_chan_open(conn);
+ if (!chan) {
+ kfree(mgr);
+ return NULL;
+ }
+
+ mgr->a2mp_chan = chan;
+ chan->data = mgr;
+
+ conn->hcon->amp_mgr = mgr;
+
+ kref_init(&mgr->kref);
+
+ return mgr;
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 126876d..1458667b 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -28,6 +28,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/a2mp.h>
static void hci_le_connect(struct hci_conn *conn)
{
@@ -411,6 +412,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_chan_list_flush(conn);
+ if (conn->amp_mgr)
+ amp_mgr_put(conn->amp_mgr);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Adds helper functions to count HCI devs and process A2MP Discover
Request, code makes sure that first controller in the list is
BREDR one. Trace is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 16
A2MP: Discover req: mtu/mps 670 mask: 0x0000
< ACL data: handle 11 flags 0x00 dlen 22
A2MP: Discover rsp: mtu/mps 670 mask: 0x0000
Controller list:
id 0 type 0 (BR-EDR) status 0x01 (Bluetooth only)
id 1 type 1 (802.11 AMP) status 0x01 (Bluetooth only)
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 2 +
include/net/bluetooth/hci.h | 3 ++
include/net/bluetooth/hci_core.h | 13 ++++++
net/bluetooth/a2mp.c | 85 ++++++++++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 391acd7..96f9cc2 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#define A2MP_FEAT_EXT 0x8000
+
struct amp_mgr {
struct l2cap_conn *l2cap_conn;
struct l2cap_chan *a2mp_chan;
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index de09a26..66af2c6 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -59,6 +59,9 @@
#define HCI_BREDR 0x00
#define HCI_AMP 0x01
+/* First BR/EDR Controller shall have ID = 0 */
+#define HCI_BREDR_ID 0
+
/* HCI device quirks */
enum {
HCI_QUIRK_RESET_ON_CLOSE,
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6e64b76..20fd573 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -641,6 +641,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
dev_set_drvdata(&hdev->dev, data);
}
+/* hci_dev_list shall be locked */
+static inline uint8_t __hci_num_ctrl(void)
+{
+ uint8_t count = 0;
+ struct list_head *p;
+
+ list_for_each(p, &hci_dev_list) {
+ count++;
+ }
+
+ return count;
+}
+
struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 0aac317..51927b6 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
+{
+ cl->id = 0;
+ cl->type = 0;
+ cl->status = 1;
+}
+
+/* hci_dev_list shall be locked */
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+{
+ int i = 0;
+ struct hci_dev *hdev;
+
+ __a2mp_cl_bredr(cl);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ /* Iterate through AMP controllers */
+ if (hdev->id == HCI_BREDR_ID)
+ continue;
+
+ /* Starting from second entry */
+ if (++i >= num_ctrl)
+ return;
+
+ cl[i].id = hdev->id;
+ cl[i].type = hdev->amp_type;
+ cl[i].status = hdev->amp_status;
+ }
+}
+
/* Processing A2MP messages */
static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
@@ -79,6 +109,58 @@ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_discov_req *req = (void *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+ struct a2mp_discov_rsp *rsp;
+ u16 ext_feat;
+ u8 num_ctrl;
+
+ if (len < sizeof(*req))
+ return -EINVAL;
+
+ skb_pull(skb, sizeof(*req));
+
+ ext_feat = le16_to_cpu(req->ext_feat);
+
+ BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->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));
+ }
+
+ read_lock(&hci_dev_list_lock);
+
+ num_ctrl = __hci_num_ctrl();
+ len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
+ rsp = kmalloc(len, GFP_ATOMIC);
+ if (!rsp) {
+ read_unlock(&hci_dev_list_lock);
+ return -ENOMEM;
+ }
+
+ rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ rsp->ext_feat = 0;
+
+ __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+
+ read_unlock(&hci_dev_list_lock);
+
+ a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
+
+ kfree(rsp);
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -109,6 +191,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
break;
case A2MP_DISCOVER_REQ:
+ err = a2mp_discover_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP fixed channel do not have sk
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/a2mp.c | 3 +--
net/bluetooth/l2cap_core.c | 17 +++++++++++++++++
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index aaba222..a00b43e 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -581,6 +581,7 @@ struct l2cap_conn {
#define L2CAP_CHAN_RAW 1
#define L2CAP_CHAN_CONN_LESS 2
#define L2CAP_CHAN_CONN_ORIENTED 3
+#define L2CAP_CHAN_CONN_FIX_A2MP 4
/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 1076ce3..2019fe3 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -485,8 +485,7 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
hci_conn_hold(conn->hcon);
- chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
- chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->ops = &a2mp_chan_ops;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index eb0ccef..621da9d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -465,6 +465,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
+ case L2CAP_CHAN_CONN_FIX_A2MP:
+ chan->scid = L2CAP_CID_A2MP;
+ chan->dcid = L2CAP_CID_A2MP;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ break;
+
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
@@ -1000,6 +1007,11 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
__clear_ack_timer(chan);
}
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ __l2cap_state_change(chan, BT_DISCONN);
+ return;
+ }
+
req.dcid = cpu_to_le16(chan->dcid);
req.scid = cpu_to_le16(chan->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
@@ -1194,6 +1206,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_lock(chan);
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
l2cap_chan_ready(chan);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Disconnect Physlink Request.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 870ef009..0d3e0d9 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -280,6 +280,39 @@ send_rsp:
return 0;
}
+static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (void *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+ hdev = hci_dev_get(req->local_id);
+ if (!hdev) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO Disconnect Phys Link here */
+
+ hci_dev_put(hdev);
+
+send_rsp:
+ a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -330,6 +363,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
break;
case A2MP_DISCONNPHYSLINK_REQ:
+ err = a2mp_discphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
case A2MP_GETINFO_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Handle incoming A2MP connection by creating AMP manager and
processing A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 4 ++++
net/bluetooth/a2mp.c | 16 ++++++++++++++++
net/bluetooth/l2cap_core.c | 19 +++++++++++++++----
3 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 96f9cc2..6a76e0a 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#include <net/bluetooth/l2cap.h>
+
#define A2MP_FEAT_EXT 0x8000
struct amp_mgr {
@@ -118,5 +120,7 @@ struct a2mp_physlink_rsp {
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb);
#endif /* __A2MP_H */
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 2019fe3..859a055 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -571,3 +571,19 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
return mgr;
}
+
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct amp_mgr *mgr;
+
+ mgr = amp_mgr_create(conn);
+ if (!mgr) {
+ BT_ERR("Could not create AMP manager");
+ return NULL;
+ }
+
+ BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
+
+ return mgr->a2mp_chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 621da9d..953ac53 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -37,6 +37,7 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
+#include <net/bluetooth/a2mp.h>
bool disable_ertm;
@@ -5131,10 +5132,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
- BT_DBG("unknown cid 0x%4.4x", cid);
- /* Drop packet and return */
- kfree_skb(skb);
- return 0;
+ if (cid == L2CAP_CID_A2MP) {
+ chan = a2mp_channel_create(conn, skb);
+ if (!chan) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ l2cap_chan_lock(chan);
+ } else {
+ BT_DBG("unknown cid 0x%4.4x", cid);
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
+ }
}
BT_DBG("chan %p, len %d", chan, skb->len);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Helper function to build and send A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 7 +++++++
net/bluetooth/a2mp.c | 46 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index ff47540..654df60 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -24,6 +24,13 @@ struct amp_mgr {
unsigned long flags;
};
+struct a2mp_cmd {
+ __u8 code;
+ __u8 ident;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 3c241c2..53f49a0 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -17,6 +17,52 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>
+/* A2MP build & send command helper functions */
+static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
+{
+ struct a2mp_cmd *cmd;
+ int plen;
+
+ plen = sizeof(*cmd) + len;
+ cmd = kzalloc(plen, GFP_KERNEL);
+ if (!cmd)
+ return NULL;
+
+ cmd->code = code;
+ cmd->ident = ident;
+ cmd->len = cpu_to_le16(len);
+
+ memcpy(cmd->data, data, len);
+
+ return cmd;
+}
+
+static 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;
+ u16 total_len = len + sizeof(*cmd);
+ struct kvec iv;
+ struct msghdr msg;
+
+ cmd = __a2mp_build(code, ident, len, data);
+ if (!cmd)
+ return;
+
+ iv.iov_base = cmd;
+ iv.iov_len = total_len;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_iov = (struct iovec *) &iv;
+ msg.msg_iovlen = 1;
+
+ l2cap_chan_send(chan, &msg, total_len, 0);
+
+ kfree(cmd);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
};
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Example trace when receiving AMP Assoc Request with wrong AMP id.
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get AMP Assoc req: id 238
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Get AMP Assoc rsp: id 238 status (1) Invalid Controller ID
assoc data:
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 38a0b38..63c5394 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -211,6 +211,38 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_amp_assoc_req *req = (void *) skb->data;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("id %d", req->id);
+
+ hdev = hci_dev_get(req->id);
+ if (!hdev || hdev->amp_type == HCI_BREDR) {
+ struct a2mp_amp_assoc_rsp rsp;
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+ goto clean;
+ }
+
+ /* Placeholder for HCI Read AMP Assoc */
+
+clean:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -253,6 +285,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
break;
case A2MP_GETAMPASSOC_REQ:
+ err = a2mp_getampassoc_req(mgr, skb, hdr);
+ break;
+
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP Get Info Request.
Example of trace log for invalid controller id is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get Info req: id 238
< ACL data: handle 11 flags 0x00 dlen 30
A2MP: Get Info rsp: id 238 status (1) Invalid Controller ID
...
Note that If the Status field is set to Invalid Controller ID all subsequent
fields in the AMP Get Info Response shall be ignored by the receiver.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index b610f13..38a0b38 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -177,6 +177,40 @@ static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+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))
+ return -EINVAL;
+
+ 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)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -215,6 +249,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
break;
case A2MP_GETINFO_REQ:
+ err = a2mp_getinfo_req(mgr, skb, hdr);
+ break;
+
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Change Notify handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 51927b6..b610f13 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -161,6 +161,22 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cl *cl = (void *) skb->data;
+
+ while (skb->len >= sizeof(*cl)) {
+ BT_DBG("Controller id %d type %d status %d", cl->id, cl->type,
+ cl->status);
+ cl = (struct a2mp_cl *) skb_pull(skb, sizeof(*cl));
+ }
+
+ /* TODO send A2MP_CHANGE_RSP */
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -195,6 +211,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
break;
case A2MP_CHANGE_NOTIFY:
+ err = a2mp_change_notify(mgr, skb, hdr);
+ break;
+
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP responses, print cmd code and ident for now.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 0d3e0d9..1076ce3 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -313,6 +313,15 @@ send_rsp:
return 0;
}
+static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ BT_DBG("ident %d code %d", hdr->ident, hdr->code);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
{
@@ -372,6 +381,9 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
case A2MP_GETAMPASSOC_RSP:
case A2MP_CREATEPHYSLINK_RSP:
case A2MP_DISCONNPHYSLINK_RSP:
+ err = a2mp_cmd_rsp(mgr, skb, hdr);
+ break;
+
default:
BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
err = -EINVAL;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP status codes copied from Bluez patch sent by Peter Krystad
<[email protected]>.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 7cbeb91..391acd7 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -104,6 +104,16 @@ struct a2mp_physlink_rsp {
__u8 status;
} __packed;
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Create and initialize fixed A2MP channel
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 6 ++++
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 69 +++++++++++++++++++++++++++++++++++++++++
net/bluetooth/l2cap_core.c | 6 ++--
4 files changed, 80 insertions(+), 4 deletions(-)
create mode 100644 net/bluetooth/a2mp.c
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index c5726c2..aaba222 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -52,6 +52,8 @@
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
+#define L2CAP_A2MP_DEFAULT_MTU 670
+
/* L2CAP socket address */
struct sockaddr_l2 {
sa_family_t l2_family;
@@ -236,6 +238,7 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_A2MP 0x0003
#define L2CAP_CID_LE_DATA 0x0004
#define L2CAP_CID_LE_SIGNALING 0x0005
#define L2CAP_CID_SMP 0x0006
@@ -758,5 +761,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
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_del(struct l2cap_chan *chan, int err);
#endif /* __L2CAP_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 2dc5a57..fa6d94a 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
- hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o
+ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
+ a2mp.o
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
new file mode 100644
index 0000000..de455a2
--- /dev/null
+++ b/net/bluetooth/a2mp.c
@@ -0,0 +1,69 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+static struct l2cap_ops a2mp_chan_ops = {
+ .name = "L2CAP A2MP channel",
+};
+
+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan;
+ int err;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ BT_DBG("chan %p", chan);
+
+ hci_conn_hold(conn->hcon);
+
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+
+ chan->ops = &a2mp_chan_ops;
+
+ l2cap_chan_set_defaults(chan);
+ chan->remote_max_tx = chan->max_tx;
+ chan->remote_tx_win = chan->tx_win;
+
+ chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ skb_queue_head_init(&chan->tx_q);
+
+ chan->mode = L2CAP_MODE_ERTM;
+
+ err = l2cap_ertm_init(chan);
+ if (err < 0) {
+ l2cap_chan_del(chan, 0);
+ return NULL;
+ }
+
+ chan->conf_state = 0;
+
+ l2cap_chan_add(conn, chan);
+
+ chan->remote_mps = chan->omtu;
+ chan->mps = chan->omtu;
+
+ chan->state = BT_CONNECTED;
+
+ return chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4f65a32..eb0ccef 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -484,14 +484,14 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
list_add(&chan->list, &conn->chan_l);
}
-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)
{
mutex_lock(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
mutex_unlock(&conn->chan_lock);
}
-static void l2cap_chan_del(struct l2cap_chan *chan, int err)
+void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct l2cap_conn *conn = chan->conn;
@@ -2690,7 +2690,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
l2cap_chan_put(chan);
}
-static inline int l2cap_ertm_init(struct l2cap_chan *chan)
+int l2cap_ertm_init(struct l2cap_chan *chan)
{
int err;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Basic A2MP implementation.
Changes:
* PATCHv4: Rebased against recent tree, changed type conversion to (void *),
added dummy "no_function"s in a2mp chan ops.
* PATCHv3: Skipped unlink callback since Gustavo seems to have better
approach, added NULL pointer checks reported by Mat.
* PATCHv2: Rebased against recent changes, changed amp_mgr list to pointer
in hci_conn, reworked handling fixed channels and create A2MP channel patches,
adding unlink and ready callbacks to get rid of socket dependecies in L2CAP and
other minor fixes.
* PATCHv1: Rebasing against latest tree and minor fixes.
* RFCv7: Taking comments from review in linux-bluetooth.
* RFCv6: Remove some unneded check for sk since they are in different path
(A2MP decoded as a data channel wrt L2CAP signalling channel). Style fixes.
* RFCv5: Fix memory leaks, sparse warnings and taking comments from upstream.
* RFCv4: redesign code to use l2cap channel lock instead of socket lock
and general modifications. Basic implementation of HCI callback interface.
* RFCv3: redesign code to use l2cap functions instead of kernel sockets
L2CAP functions modified to work with channels without sk.
* RFCv2: rebased against "workqueue" patches.
* RFCv1: added refcnt to amp_mgr, fixed sleeping in atomic
Initially code was based of reference implementations below:
References: Code Aurora (git://codeaurora.org/kernel/msm.git) and Atheros
(search for: [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3)
implementations.
Andrei Emeltchenko (17):
Bluetooth: A2MP: Create A2MP channel
Bluetooth: A2MP: AMP Manager basic functions
Bluetooth: A2MP: Build and Send msg helpers
Bluetooth: A2MP: Add chan callbacks
Bluetooth: A2MP: Definitions for A2MP commands
Bluetooth: A2MP: Define A2MP status codes
Bluetooth: A2MP: Process A2MP messages
Bluetooth: A2MP: Process A2MP Command Reject
Bluetooth: A2MP: Process A2MP Discover Request
Bluetooth: A2MP: Process A2MP Change Notify
Bluetooth: A2MP: Process A2MP Get Info Request
Bluetooth: A2MP: Process A2MP Get AMP Assoc Request
Bluetooth: A2MP: Process A2MP Create Physlink Request
Bluetooth: A2MP: Process A2MP Disc Physlink Request
Bluetooth: A2MP: Process A2MP Command Responses
Bluetooth: A2MP: Handling fixed channels
Bluetooth: A2MP: Manage incoming connections
include/net/bluetooth/a2mp.h | 126 ++++++++
include/net/bluetooth/hci.h | 3 +
include/net/bluetooth/hci_core.h | 14 +
include/net/bluetooth/l2cap.h | 7 +
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 589 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 4 +
net/bluetooth/l2cap_core.c | 42 ++-
8 files changed, 780 insertions(+), 8 deletions(-)
create mode 100644 include/net/bluetooth/a2mp.h
create mode 100644 net/bluetooth/a2mp.c
--
1.7.9.5
Hi Mat,
On Mon, May 28, 2012 at 10:24:56AM +0300, Andrei Emeltchenko wrote:
> > >>The risk is not memory leaks - if the other device closes the ACL,
> > >>then the A2MP channel will be cleaned up. But if you had two BlueZ
> > >>devices with A2MP connected to each other, the ACL would never close
> > >>as long as the devices remained powered and in range! Nothing would
> > >>trigger the flush as long as the hci_conn is held.
> > >
> > >Does it indicate that we have problem with rather freeing amp_mgr?
> >
> > It looks like the amp_mgr will be freed if the channel is closed.
> > There's nothing that will close the channel except for:
> >
> > * Remote device disconnects the ACL
> > * Remote device goes out of range
> > * Local BR/EDR controller is powered down or reset
> >
> > >>I think the checks for hci_conn_put are necessary, even if they are
> > >>ugly.
> > >
> > >I think that if I have hci_conn_hold when creating channel and
> > >hci_conn_put when closing channel that does not differ from the case when
> > >I do not have hci_conn_put/hold.
> >
> > If there was something that closed the A2MP channel, there would be
> > no difference. But nothing closes the A2MP channel.
>
> I actually was thinking about timers which could do clean up.
>
> > >So you are probably referring to the case when hci_conn needs to be closed
> > >but cannot because it has hci_conn_hold in a2mp channel?
> >
> > Yes.
> >
> > >This also means that we still have L2CAP channel (a2mp) hanging.
> > >This seems right to me if I did not miss something.
> >
> > Since A2MP is a fixed channel, it is not opened and closed like
> > typical L2CAP channels. Consider this scenario:
> >
> > 1. Remote device decides to connect
> > 2. ACL is created
> > 3. Remote device sends A2MP discover
> > 4. A2MP channel is set up, AMP manager is created, hci_conn_hold for
> > A2MP channel
> > 5. Remote device connects L2CAP channel over BR/EDR. hci_conn_hold
> > for the data channel
> > 6. A2MP communication to set up physical link
> > 7. L2CAP channel move to AMP controller
> > 8. Data sent over AMP.
> > 9. L2CAP channel disconnected, hci_conn_put for the data channel
> > 10. Device stays in range. hci_conn reference count is not 0, so
> > ACL is not closed.
>
> My concern here if A2MP does not reference hci_conn can hci_conn be
> removed while we still have A2MP chan in some situation?
I just tested code with PTS and got one crash:
<------8<--------------------------------------------------------------------
| [ 275.100760] [5] hci_chan_del: hci0 conn f3ff0800 chan e818e540
| [ 275.109610] [19] l2cap_send_rr_or_rnr: chan e806b800, poll 0
| [ 275.127922] [19] l2cap_send_sframe: chan e806b800, control f5ef3ed8
| [ 275.133532] [19] l2cap_send_sframe: reqseq 2, final 0, poll 0, super 0
| [ 275.173033] [5] hci_conn_del: hci0 conn f3ff0800 handle 11
| [ 275.181131] [5] hci_chan_list_flush: conn f3ff0800
| [ 275.202645] [5] amp_mgr_put: mgr e8260600
<------8<--------------------------------------------------------------------
Here we deleted the last L2CAP channel
<------8<------------------------------------------------------------------
| [ 275.295020] BUG: unable to handle kernel NULL pointer dereference at
| (null)
| [ 275.298791] IP: [<f8362e6c>] l2cap_do_send+0x1c/0xa0 [bluetooth]
| [ 275.298791] *pde = 00000000
| [ 275.298791] Oops: 0000 [#1] SMP
<------8<------------------------------------------------------------------
Then we got the crash probably hci_conn deleted faster then we get reply
from AMP controller
<------8<---------------------------------------------------------------------
| [ 275.298791] EIP: [<f8362e6c>] l2cap_do_send+0x1c/0xa0 [bluetooth]
| SS:ESP 0068:f5ef3e58
| [ 275.298791] CR2: 0000000000000000
| [ 276.553113] Bluetooth: hci1 command tx timeout
| [ 276.553706] ---[ end trace b08f23ddb2b49a2e ]---
| [ 276.577494] [30] hci_queue_cb: Queue cmd ed3d5540 opt e8260600
| [ 276.723885] [30] l2cap_segment_sdu: chan e806b800, msg f5919e94, len 22
| [ 276.790990] BUG: unable to handle kernel NULL pointer dereference at
| 00000010
| [ 276.794865] IP: [<f83694dd>] l2cap_chan_send+0x28d/0xb40 [bluetooth]
<------8<---------------------------------------------------------------------
Best regards
Andrei Emeltchenko
Hi Mat,
On Fri, May 25, 2012 at 10:18:20AM -0700, Mat Martineau wrote:
> >On Thu, May 24, 2012 at 08:59:57AM -0700, Mat Martineau wrote:
> >>On Thu, 24 May 2012, Andrei Emeltchenko wrote:
> >>>On Wed, May 23, 2012 at 08:44:22AM -0700, Mat Martineau wrote:
> >>>>>>>+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> >>>>>>>+{
> >>>>>>>+ struct l2cap_chan *chan;
> >>>>>>>+
> >>>>>>>+ chan = l2cap_chan_create();
> >>>>>>>+ if (!chan)
> >>>>>>>+ return NULL;
> >>>>>>>+
> >>>>>>>+ BT_DBG("chan %p", chan);
> >>>>>>>+
> >>>>>>>+ hci_conn_hold(conn->hcon);
> >>>>>>
> >>>>>>Holding the hcon will keep the ACL open after all of the other L2CAP
> >>>>>>channels have closed (unless I missed some code later in the patch
> >>>>>>series). The A2MP fixed channel should not keep the ACL open. If
> >>>>>>the connection is not held here, then there shouldn't be a put in
> >>>>>>l2cap_chan_del for the A2MP channel either.
> >>>>>
> >>>>>l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
> >>>>>the same for fixed and normal channels.
> >>>>
> >>>>And when does l2cap_chan_del get called for a fixed channel? The
> >>>>fixed channel must not do an hci_conn_hold so the ACL is allowed to
> >>>>close when all dynamic L2CAP channels have closed.
> >>>
> >>>The current approach is to have amp_mgr for hci_conn. It will be freed
> >>>when in hci_conn_del together with other l2cap channels in
> >>>hci_chan_list_flush and then we make amp_mgr_put() and destroy mgr.
> >>>
> >>>The idea is to make a2mp chan similar to other chans and other chans do
> >>>hci_conn_hold and hci_conn_put. Otherwise I would need to add extra checks
> >>>before hci_conn_put which is ugly.
> >>>
> >>>I've tested this with PTS and no memory leaks found so far.
> >>
> >>The risk is not memory leaks - if the other device closes the ACL,
> >>then the A2MP channel will be cleaned up. But if you had two BlueZ
> >>devices with A2MP connected to each other, the ACL would never close
> >>as long as the devices remained powered and in range! Nothing would
> >>trigger the flush as long as the hci_conn is held.
> >
> >Does it indicate that we have problem with rather freeing amp_mgr?
>
> It looks like the amp_mgr will be freed if the channel is closed.
> There's nothing that will close the channel except for:
>
> * Remote device disconnects the ACL
> * Remote device goes out of range
> * Local BR/EDR controller is powered down or reset
>
> >>I think the checks for hci_conn_put are necessary, even if they are
> >>ugly.
> >
> >I think that if I have hci_conn_hold when creating channel and
> >hci_conn_put when closing channel that does not differ from the case when
> >I do not have hci_conn_put/hold.
>
> If there was something that closed the A2MP channel, there would be
> no difference. But nothing closes the A2MP channel.
I actually was thinking about timers which could do clean up.
> >So you are probably referring to the case when hci_conn needs to be closed
> >but cannot because it has hci_conn_hold in a2mp channel?
>
> Yes.
>
> >This also means that we still have L2CAP channel (a2mp) hanging.
> >This seems right to me if I did not miss something.
>
> Since A2MP is a fixed channel, it is not opened and closed like
> typical L2CAP channels. Consider this scenario:
>
> 1. Remote device decides to connect
> 2. ACL is created
> 3. Remote device sends A2MP discover
> 4. A2MP channel is set up, AMP manager is created, hci_conn_hold for
> A2MP channel
> 5. Remote device connects L2CAP channel over BR/EDR. hci_conn_hold
> for the data channel
> 6. A2MP communication to set up physical link
> 7. L2CAP channel move to AMP controller
> 8. Data sent over AMP.
> 9. L2CAP channel disconnected, hci_conn_put for the data channel
> 10. Device stays in range. hci_conn reference count is not 0, so
> ACL is not closed.
My concern here if A2MP does not reference hci_conn can hci_conn be
removed while we still have A2MP chan in some situation?
I will modify code that A2MP does not reference hci_conn, or create
special patch with this change.
Best regards
Andrei Emeltchenko
Andrei -
On Fri, 25 May 2012, Andrei Emeltchenko wrote:
> Hi Mat,
>
> On Thu, May 24, 2012 at 08:59:57AM -0700, Mat Martineau wrote:
>> On Thu, 24 May 2012, Andrei Emeltchenko wrote:
>>> On Wed, May 23, 2012 at 08:44:22AM -0700, Mat Martineau wrote:
>>>>>>> +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
>>>>>>> +{
>>>>>>> + struct l2cap_chan *chan;
>>>>>>> +
>>>>>>> + chan = l2cap_chan_create();
>>>>>>> + if (!chan)
>>>>>>> + return NULL;
>>>>>>> +
>>>>>>> + BT_DBG("chan %p", chan);
>>>>>>> +
>>>>>>> + hci_conn_hold(conn->hcon);
>>>>>>
>>>>>> Holding the hcon will keep the ACL open after all of the other L2CAP
>>>>>> channels have closed (unless I missed some code later in the patch
>>>>>> series). The A2MP fixed channel should not keep the ACL open. If
>>>>>> the connection is not held here, then there shouldn't be a put in
>>>>>> l2cap_chan_del for the A2MP channel either.
>>>>>
>>>>> l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
>>>>> the same for fixed and normal channels.
>>>>
>>>> And when does l2cap_chan_del get called for a fixed channel? The
>>>> fixed channel must not do an hci_conn_hold so the ACL is allowed to
>>>> close when all dynamic L2CAP channels have closed.
>>>
>>> The current approach is to have amp_mgr for hci_conn. It will be freed
>>> when in hci_conn_del together with other l2cap channels in
>>> hci_chan_list_flush and then we make amp_mgr_put() and destroy mgr.
>>>
>>> The idea is to make a2mp chan similar to other chans and other chans do
>>> hci_conn_hold and hci_conn_put. Otherwise I would need to add extra checks
>>> before hci_conn_put which is ugly.
>>>
>>> I've tested this with PTS and no memory leaks found so far.
>>
>> The risk is not memory leaks - if the other device closes the ACL,
>> then the A2MP channel will be cleaned up. But if you had two BlueZ
>> devices with A2MP connected to each other, the ACL would never close
>> as long as the devices remained powered and in range! Nothing would
>> trigger the flush as long as the hci_conn is held.
>
> Does it indicate that we have problem with rather freeing amp_mgr?
It looks like the amp_mgr will be freed if the channel is closed.
There's nothing that will close the channel except for:
* Remote device disconnects the ACL
* Remote device goes out of range
* Local BR/EDR controller is powered down or reset
>> I think the checks for hci_conn_put are necessary, even if they are
>> ugly.
>
> I think that if I have hci_conn_hold when creating channel and
> hci_conn_put when closing channel that does not differ from the case when
> I do not have hci_conn_put/hold.
If there was something that closed the A2MP channel, there would be no
difference. But nothing closes the A2MP channel.
> So you are probably referring to the case when hci_conn needs to be closed
> but cannot because it has hci_conn_hold in a2mp channel?
Yes.
> This also means that we still have L2CAP channel (a2mp) hanging.
> This seems right to me if I did not miss something.
Since A2MP is a fixed channel, it is not opened and closed like
typical L2CAP channels. Consider this scenario:
1. Remote device decides to connect
2. ACL is created
3. Remote device sends A2MP discover
4. A2MP channel is set up, AMP manager is created, hci_conn_hold for
A2MP channel
5. Remote device connects L2CAP channel over BR/EDR. hci_conn_hold
for the data channel
6. A2MP communication to set up physical link
7. L2CAP channel move to AMP controller
8. Data sent over AMP.
9. L2CAP channel disconnected, hci_conn_put for the data channel
10. Device stays in range. hci_conn reference count is not 0, so ACL
is not closed.
If you reach step 10 and both sides are BlueZ, the ACL stays active
and BT is using extra power.
If you did not do the hci_conn_hold in step 4, the ACL would be
disconnected 2 seconds after step 9, allowing the BR/EDR baseband to
go in to a low power state.
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Gustavo,
On Fri, May 25, 2012 at 09:19:34AM -0300, Gustavo Padovan wrote:
> Hi Andrei,
>
> > +static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
> > + struct a2mp_cmd *hdr)
> > +{
> > + struct a2mp_discov_req *req = (struct a2mp_discov_req *) skb->data;
>
> Just use (void *) as cast here.
This actually does not matter but I can change it. This is pity that it
was not mentioned in the 20 previous reviews ;(
> > + rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
>
> __constant_cpu_to_le16() here.
OK.
Best regards
Andrei Emeltchenko
Hi Gustavo,
On Fri, May 25, 2012 at 09:20:37AM -0300, Gustavo Padovan wrote:
> > static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> > @@ -110,6 +148,8 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> > chan->remote_mps = chan->omtu;
> > chan->mps = chan->omtu;
> >
> > + chan->state = BT_CONNECTED;
> > +
>
> This change seems to be unrelated to this patch.
OK, I move it to the first patch.
Best regards
Andrei Emeltchenko
Hi Andrei,
* Andrei Emeltchenko <[email protected]> [2012-05-24 14:38:21 +0300]:
> From: Andrei Emeltchenko <[email protected]>
>
> Placeholder for A2MP Change Notify handler.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/a2mp.c | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index ac095ac..d60be75 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -161,6 +161,22 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
> return 0;
> }
>
> +static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
> + struct a2mp_cmd *hdr)
> +{
> + struct a2mp_cl *cl = (struct a2mp_cl *) skb->data;
Use void *. Same for the next patches in this series.
Gustavo
Hi Andrei,
* Andrei Emeltchenko <[email protected]> [2012-05-24 14:38:15 +0300]:
> From: Andrei Emeltchenko <[email protected]>
>
> Add state change, close and skb allocation callbacks.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> net/bluetooth/a2mp.c | 40 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 40 insertions(+)
>
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index e7e5f5f..eb98541 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -63,8 +63,46 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
> kfree(cmd);
> }
>
> +static void a2mp_chan_state_change_cb(void *data, int state)
> +{
> + struct amp_mgr *mgr = data;
> + struct l2cap_chan *chan;
> +
> + if (!mgr)
> + return;
> +
> + chan = mgr->a2mp_chan;
> +
> + BT_DBG("chan %p state %s", chan, state_to_string(state));
> +
> + chan->state = state;
> +
> + switch (state) {
> + case BT_CLOSED:
> + if (mgr)
> + amp_mgr_put(mgr);
> + break;
> + }
> +}
> +
> +static void a2mp_chan_close_cb(void *data)
> +{
> + struct amp_mgr *mgr = data;
> +
> + l2cap_chan_destroy(mgr->a2mp_chan);
> +}
> +
> +static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
> + unsigned long len, int nb)
> +{
> + return bt_skb_alloc(len, GFP_KERNEL);
> +}
> +
> static struct l2cap_ops a2mp_chan_ops = {
> .name = "L2CAP A2MP channel",
> + .state_change = a2mp_chan_state_change_cb,
> + .close = a2mp_chan_close_cb,
> + .alloc_skb = a2mp_chan_alloc_skb_cb,
> };
>
> static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> @@ -110,6 +148,8 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> chan->remote_mps = chan->omtu;
> chan->mps = chan->omtu;
>
> + chan->state = BT_CONNECTED;
> +
This change seems to be unrelated to this patch.
Gustavo
Hi Andrei,
* Andrei Emeltchenko <[email protected]> [2012-05-24 14:38:20 +0300]:
> From: Andrei Emeltchenko <[email protected]>
>
> Adds helper functions to count HCI devs and process A2MP Discover
> Request, code makes sure that first controller in the list is
> BREDR one. Trace is shown below:
>
> ...
> > ACL data: handle 11 flags 0x02 dlen 16
> A2MP: Discover req: mtu/mps 670 mask: 0x0000
> < ACL data: handle 11 flags 0x00 dlen 22
> A2MP: Discover rsp: mtu/mps 670 mask: 0x0000
> Controller list:
> id 0 type 0 (BR-EDR) status 0x01 (Bluetooth only)
> id 1 type 1 (802.11 AMP) status 0x01 (Bluetooth only)
> ...
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/a2mp.h | 2 +
> include/net/bluetooth/hci.h | 3 ++
> include/net/bluetooth/hci_core.h | 13 ++++++
> net/bluetooth/a2mp.c | 85 ++++++++++++++++++++++++++++++++++++++
> 4 files changed, 103 insertions(+)
>
> diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
> index 391acd7..96f9cc2 100644
> --- a/include/net/bluetooth/a2mp.h
> +++ b/include/net/bluetooth/a2mp.h
> @@ -15,6 +15,8 @@
> #ifndef __A2MP_H
> #define __A2MP_H
>
> +#define A2MP_FEAT_EXT 0x8000
> +
> struct amp_mgr {
> struct l2cap_conn *l2cap_conn;
> struct l2cap_chan *a2mp_chan;
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index edb6639..158f403 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -58,6 +58,9 @@
> #define HCI_BREDR 0x00
> #define HCI_AMP 0x01
>
> +/* First BR/EDR Controller shall have ID = 0 */
> +#define HCI_BREDR_ID 0
> +
> /* HCI device quirks */
> enum {
> HCI_QUIRK_RESET_ON_CLOSE,
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 6e64b76..20fd573 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -641,6 +641,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
> dev_set_drvdata(&hdev->dev, data);
> }
>
> +/* hci_dev_list shall be locked */
> +static inline uint8_t __hci_num_ctrl(void)
> +{
> + uint8_t count = 0;
> + struct list_head *p;
> +
> + list_for_each(p, &hci_dev_list) {
> + count++;
> + }
> +
> + return count;
> +}
> +
> struct hci_dev *hci_dev_get(int index);
> struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
>
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index 4137f9b..ac095ac 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -63,6 +63,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
> kfree(cmd);
> }
>
> +static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
> +{
> + cl->id = 0;
> + cl->type = 0;
> + cl->status = 1;
> +}
> +
> +/* hci_dev_list shall be locked */
> +static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
> +{
> + int i = 0;
> + struct hci_dev *hdev;
> +
> + __a2mp_cl_bredr(cl);
> +
> + list_for_each_entry(hdev, &hci_dev_list, list) {
> + /* Iterate through AMP controllers */
> + if (hdev->id == HCI_BREDR_ID)
> + continue;
> +
> + /* Starting from second entry */
> + if (++i >= num_ctrl)
> + return;
> +
> + cl[i].id = hdev->id;
> + cl[i].type = hdev->amp_type;
> + cl[i].status = hdev->amp_status;
> + }
> +}
> +
> /* Processing A2MP messages */
> static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
> struct a2mp_cmd *hdr)
> @@ -79,6 +109,58 @@ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
> return 0;
> }
>
> +static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
> + struct a2mp_cmd *hdr)
> +{
> + struct a2mp_discov_req *req = (struct a2mp_discov_req *) skb->data;
Just use (void *) as cast here.
> + u16 len = le16_to_cpu(hdr->len);
> + struct a2mp_discov_rsp *rsp;
> + u16 ext_feat;
> + u8 num_ctrl;
> +
> + if (len < sizeof(*req))
> + return -EINVAL;
> +
> + skb_pull(skb, sizeof(*req));
> +
> + ext_feat = le16_to_cpu(req->ext_feat);
> +
> + BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->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));
> + }
> +
> + read_lock(&hci_dev_list_lock);
> +
> + num_ctrl = __hci_num_ctrl();
> + len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
> + rsp = kmalloc(len, GFP_ATOMIC);
> + if (!rsp) {
> + read_unlock(&hci_dev_list_lock);
> + return -ENOMEM;
> + }
> +
> + rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
__constant_cpu_to_le16() here.
> + rsp->ext_feat = 0;
> +
> + __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
> +
> + read_unlock(&hci_dev_list_lock);
> +
> + a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
> +
> + kfree(rsp);
> + return 0;
> +}
> +
> /* Handle A2MP signalling */
> static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
> {
> @@ -109,6 +191,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
> break;
>
> case A2MP_DISCOVER_REQ:
> + err = a2mp_discover_req(mgr, skb, hdr);
> + break;
> +
> case A2MP_CHANGE_NOTIFY:
> case A2MP_GETINFO_REQ:
> case A2MP_GETAMPASSOC_REQ:
> --
> 1.7.9.5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
Gustavo
Hi Mat,
On Thu, May 24, 2012 at 08:59:57AM -0700, Mat Martineau wrote:
> On Thu, 24 May 2012, Andrei Emeltchenko wrote:
> >On Wed, May 23, 2012 at 08:44:22AM -0700, Mat Martineau wrote:
> >>>>>+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> >>>>>+{
> >>>>>+ struct l2cap_chan *chan;
> >>>>>+
> >>>>>+ chan = l2cap_chan_create();
> >>>>>+ if (!chan)
> >>>>>+ return NULL;
> >>>>>+
> >>>>>+ BT_DBG("chan %p", chan);
> >>>>>+
> >>>>>+ hci_conn_hold(conn->hcon);
> >>>>
> >>>>Holding the hcon will keep the ACL open after all of the other L2CAP
> >>>>channels have closed (unless I missed some code later in the patch
> >>>>series). The A2MP fixed channel should not keep the ACL open. If
> >>>>the connection is not held here, then there shouldn't be a put in
> >>>>l2cap_chan_del for the A2MP channel either.
> >>>
> >>>l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
> >>>the same for fixed and normal channels.
> >>
> >>And when does l2cap_chan_del get called for a fixed channel? The
> >>fixed channel must not do an hci_conn_hold so the ACL is allowed to
> >>close when all dynamic L2CAP channels have closed.
> >
> >The current approach is to have amp_mgr for hci_conn. It will be freed
> >when in hci_conn_del together with other l2cap channels in
> >hci_chan_list_flush and then we make amp_mgr_put() and destroy mgr.
> >
> >The idea is to make a2mp chan similar to other chans and other chans do
> >hci_conn_hold and hci_conn_put. Otherwise I would need to add extra checks
> >before hci_conn_put which is ugly.
> >
> >I've tested this with PTS and no memory leaks found so far.
>
> The risk is not memory leaks - if the other device closes the ACL,
> then the A2MP channel will be cleaned up. But if you had two BlueZ
> devices with A2MP connected to each other, the ACL would never close
> as long as the devices remained powered and in range! Nothing would
> trigger the flush as long as the hci_conn is held.
Does it indicate that we have problem with rather freeing amp_mgr?
> I think the checks for hci_conn_put are necessary, even if they are
> ugly.
I think that if I have hci_conn_hold when creating channel and
hci_conn_put when closing channel that does not differ from the case when
I do not have hci_conn_put/hold.
So you are probably referring to the case when hci_conn needs to be closed
but cannot because it has hci_conn_hold in a2mp channel? This also means
that we still have L2CAP channel (a2mp) hanging. This seems right to me if
I did not miss something.
Best regards
Andrei Emeltchenko
Hi Andrei -
On Thu, 24 May 2012, Andrei Emeltchenko wrote:
> On Wed, May 23, 2012 at 08:44:22AM -0700, Mat Martineau wrote:
>>>>> +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
>>>>> +{
>>>>> + struct l2cap_chan *chan;
>>>>> +
>>>>> + chan = l2cap_chan_create();
>>>>> + if (!chan)
>>>>> + return NULL;
>>>>> +
>>>>> + BT_DBG("chan %p", chan);
>>>>> +
>>>>> + hci_conn_hold(conn->hcon);
>>>>
>>>> Holding the hcon will keep the ACL open after all of the other L2CAP
>>>> channels have closed (unless I missed some code later in the patch
>>>> series). The A2MP fixed channel should not keep the ACL open. If
>>>> the connection is not held here, then there shouldn't be a put in
>>>> l2cap_chan_del for the A2MP channel either.
>>>
>>> l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
>>> the same for fixed and normal channels.
>>
>> And when does l2cap_chan_del get called for a fixed channel? The
>> fixed channel must not do an hci_conn_hold so the ACL is allowed to
>> close when all dynamic L2CAP channels have closed.
>
> The current approach is to have amp_mgr for hci_conn. It will be freed
> when in hci_conn_del together with other l2cap channels in
> hci_chan_list_flush and then we make amp_mgr_put() and destroy mgr.
>
> The idea is to make a2mp chan similar to other chans and other chans do
> hci_conn_hold and hci_conn_put. Otherwise I would need to add extra checks
> before hci_conn_put which is ugly.
>
> I've tested this with PTS and no memory leaks found so far.
The risk is not memory leaks - if the other device closes the ACL,
then the A2MP channel will be cleaned up. But if you had two BlueZ
devices with A2MP connected to each other, the ACL would never close
as long as the devices remained powered and in range! Nothing would
trigger the flush as long as the hci_conn is held.
I think the checks for hci_conn_put are necessary, even if they are
ugly.
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Mat,
On Wed, May 23, 2012 at 04:30:39PM -0700, Mat Martineau wrote:
> >+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
> >+ struct sk_buff *skb)
> >+{
> >+ struct amp_mgr *mgr;
> >+
> >+ mgr = amp_mgr_create(conn);
> >+
> >+ BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
> >+
> >+ return mgr->a2mp_chan;
>
> mgr can be NULL.
...
> > chan = l2cap_get_chan_by_scid(conn, cid);
> > if (!chan) {
> >- BT_DBG("unknown cid 0x%4.4x", cid);
> >- /* Drop packet and return */
> >- kfree_skb(skb);
> >- return 0;
> >+ if (cid == L2CAP_CID_A2MP) {
> >+ chan = a2mp_channel_create(conn, skb);
>
> If amp_mgr_create returned NULL, then you would have to handle NULL
> from a2mp_channel_create too.
I've taken all comment in my v3 patch series.
Best regards
Andrei Emeltchenko
From: Andrei Emeltchenko <[email protected]>
Add state change, close and skb allocation callbacks.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index e7e5f5f..eb98541 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,8 +63,46 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static void a2mp_chan_state_change_cb(void *data, int state)
+{
+ struct amp_mgr *mgr = data;
+ struct l2cap_chan *chan;
+
+ if (!mgr)
+ return;
+
+ chan = mgr->a2mp_chan;
+
+ BT_DBG("chan %p state %s", chan, state_to_string(state));
+
+ chan->state = state;
+
+ switch (state) {
+ case BT_CLOSED:
+ if (mgr)
+ amp_mgr_put(mgr);
+ break;
+ }
+}
+
+static void a2mp_chan_close_cb(void *data)
+{
+ struct amp_mgr *mgr = data;
+
+ l2cap_chan_destroy(mgr->a2mp_chan);
+}
+
+static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb)
+{
+ return bt_skb_alloc(len, GFP_KERNEL);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .state_change = a2mp_chan_state_change_cb,
+ .close = a2mp_chan_close_cb,
+ .alloc_skb = a2mp_chan_alloc_skb_cb,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
@@ -110,6 +148,8 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
chan->remote_mps = chan->omtu;
chan->mps = chan->omtu;
+ chan->state = BT_CONNECTED;
+
return chan;
}
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define A2MP command IDs and packet structures.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 73 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 654df60..7cbeb91 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -31,6 +31,79 @@ struct a2mp_cmd {
__u8 data[0];
} __packed;
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ 0x01
+struct a2mp_cmd_rej {
+ __le16 reason;
+ __u8 data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ 0x02
+struct a2mp_discov_req {
+ __le16 mtu;
+ __le16 ext_feat;
+} __packed;
+
+struct a2mp_cl {
+ __u8 id;
+ __u8 type;
+ __u8 status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP 0x03
+struct a2mp_discov_rsp {
+ __le16 mtu;
+ __le16 ext_feat;
+ struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+
+#define A2MP_GETINFO_REQ 0x06
+struct a2mp_info_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETINFO_RSP 0x07
+struct a2mp_info_rsp {
+ __u8 id;
+ __u8 status;
+ __le32 total_bw;
+ __le32 max_bw;
+ __le32 min_latency;
+ __le16 pal_cap;
+ __le16 assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ 0x08
+struct a2mp_amp_assoc_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP 0x09
+struct a2mp_amp_assoc_rsp {
+ __u8 id;
+ __u8 status;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ 0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP 0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 status;
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Change Notify handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ac095ac..d60be75 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -161,6 +161,22 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cl *cl = (struct a2mp_cl *) skb->data;
+
+ while (skb->len >= sizeof(*cl)) {
+ BT_DBG("Controller id %d type %d status %d", cl->id, cl->type,
+ cl->status);
+ cl = (struct a2mp_cl *) skb_pull(skb, sizeof(*cl));
+ }
+
+ /* TODO send A2MP_CHANGE_RSP */
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -195,6 +211,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_CHANGE_NOTIFY:
+ err = a2mp_change_notify(mgr, skb, hdr);
+ break;
+
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP status codes copied from Bluez patch sent by Peter Krystad
<[email protected]>.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 7cbeb91..391acd7 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -104,6 +104,16 @@ struct a2mp_physlink_rsp {
__u8 status;
} __packed;
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Handle incoming A2MP connection by creating AMP manager and
processing A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 4 ++++
net/bluetooth/a2mp.c | 16 ++++++++++++++++
net/bluetooth/l2cap_core.c | 19 +++++++++++++++----
3 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 96f9cc2..6a76e0a 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#include <net/bluetooth/l2cap.h>
+
#define A2MP_FEAT_EXT 0x8000
struct amp_mgr {
@@ -118,5 +120,7 @@ struct a2mp_physlink_rsp {
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb);
#endif /* __A2MP_H */
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 894e53c..2f0172b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -552,3 +552,19 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
return mgr;
}
+
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct amp_mgr *mgr;
+
+ mgr = amp_mgr_create(conn);
+ if (!mgr) {
+ BT_ERR("Could not create AMP manager");
+ return NULL;
+ }
+
+ BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
+
+ return mgr->a2mp_chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 3cbdd1d..2cfbc0c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -37,6 +37,7 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
+#include <net/bluetooth/a2mp.h>
bool disable_ertm;
@@ -5175,10 +5176,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
- BT_DBG("unknown cid 0x%4.4x", cid);
- /* Drop packet and return */
- kfree_skb(skb);
- return 0;
+ if (cid == L2CAP_CID_A2MP) {
+ chan = a2mp_channel_create(conn, skb);
+ if (!chan) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ l2cap_chan_lock(chan);
+ } else {
+ BT_DBG("unknown cid 0x%4.4x", cid);
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
+ }
}
BT_DBG("chan %p, len %d", chan, skb->len);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Implement basic processing for AMP Manager Protocol (A2MP).
Example below shows processing unrecognized command.
...
> ACL data: handle 11 flags 0x02 dlen 12
A2MP: code 0x00 ident 3 len 0
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Command Reject: reason (0) - Command not recognized
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index eb98541..92b7671 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,70 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Handle A2MP signalling */
+static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
+{
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ struct amp_mgr *mgr = data;
+ int err = 0;
+
+ amp_mgr_get(mgr);
+
+ while (skb->len >= sizeof(*hdr)) {
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+
+ BT_DBG("code 0x%02x id %d len %d", hdr->code, hdr->ident, len);
+
+ skb_pull(skb, sizeof(*hdr));
+
+ if (len > skb->len || !hdr->ident) {
+ err = -EINVAL;
+ break;
+ }
+
+ mgr->ident = hdr->ident;
+
+ switch (hdr->code) {
+ case A2MP_COMMAND_REJ:
+ case A2MP_DISCOVER_REQ:
+ case A2MP_CHANGE_NOTIFY:
+ case A2MP_GETINFO_REQ:
+ case A2MP_GETAMPASSOC_REQ:
+ case A2MP_CREATEPHYSLINK_REQ:
+ case A2MP_DISCONNPHYSLINK_REQ:
+ case A2MP_CHANGE_RSP:
+ case A2MP_DISCOVER_RSP:
+ case A2MP_GETINFO_RSP:
+ case A2MP_GETAMPASSOC_RSP:
+ case A2MP_CREATEPHYSLINK_RSP:
+ case A2MP_DISCONNPHYSLINK_RSP:
+ default:
+ BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (err) {
+ struct a2mp_cmd_rej rej;
+ rej.reason = cpu_to_le16(0);
+
+ BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
+
+ a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
+ &rej);
+ }
+
+ /* Always free skb and return success error code to prevent
+ from sending L2CAP Disconnect over A2MP channel */
+ kfree_skb(skb);
+
+ amp_mgr_put(mgr);
+
+ return 0;
+}
+
static void a2mp_chan_state_change_cb(void *data, int state)
{
struct amp_mgr *mgr = data;
@@ -100,6 +164,7 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .recv = a2mp_chan_recv_cb,
.state_change = a2mp_chan_state_change_cb,
.close = a2mp_chan_close_cb,
.alloc_skb = a2mp_chan_alloc_skb_cb,
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Basic A2MP implementation.
Changes:
* PATCHv3: Skipped unlink callback since Gustavo seems to have better
approach, added NULL pointer checks reported by Mat.
* PATCHv2: Rebased against recent changes, changed amp_mgr list to pointer
in hci_conn, reworked handling fixed channels and create A2MP channel patches,
adding unlink and ready callbacks to get rid of socket dependecies in L2CAP and
other minor fixes.
* PATCHv1: Rebasing against latest tree and minor fixes.
* RFCv7: Taking comments from review in linux-bluetooth.
* RFCv6: Remove some unneded check for sk since they are in different path
(A2MP decoded as a data channel wrt L2CAP signalling channel). Style fixes.
* RFCv5: Fix memory leaks, sparse warnings and taking comments from upstream.
* RFCv4: redesign code to use l2cap channel lock instead of socket lock
and general modifications. Basic implementation of HCI callback interface.
* RFCv3: redesign code to use l2cap functions instead of kernel sockets
L2CAP functions modified to work with channels without sk.
* RFCv2: rebased against "workqueue" patches.
* RFCv1: added refcnt to amp_mgr, fixed sleeping in atomic
Initially code was based of reference implementations below:
References: Code Aurora (git://codeaurora.org/kernel/msm.git) and Atheros
(search for: [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3)
implementations.
Andrei Emeltchenko (18):
Bluetooth: Add ready to L2CAP channel ops
Bluetooth: A2MP: Create A2MP channel
Bluetooth: A2MP: AMP Manager basic functions
Bluetooth: A2MP: Build and Send msg helpers
Bluetooth: A2MP: Add chan callbacks
Bluetooth: A2MP: Definitions for A2MP commands
Bluetooth: A2MP: Define A2MP status codes
Bluetooth: A2MP: Process A2MP messages
Bluetooth: A2MP: Process A2MP Command Reject
Bluetooth: A2MP: Process A2MP Discover Request
Bluetooth: A2MP: Process A2MP Change Notify
Bluetooth: A2MP: Process A2MP Get Info Request
Bluetooth: A2MP: Process A2MP Get AMP Assoc Request
Bluetooth: A2MP: Process A2MP Create Physlink Request
Bluetooth: A2MP: Process A2MP Disc Physlink Request
Bluetooth: A2MP: Process A2MP Command Responses
Bluetooth: A2MP: Handling fixed channels
Bluetooth: A2MP: Manage incoming connections
include/net/bluetooth/a2mp.h | 126 +++++++++
include/net/bluetooth/hci.h | 3 +
include/net/bluetooth/hci_core.h | 14 +
include/net/bluetooth/l2cap.h | 8 +
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 570 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 4 +
net/bluetooth/l2cap_core.c | 65 +++--
net/bluetooth/l2cap_sock.c | 22 ++
9 files changed, 789 insertions(+), 26 deletions(-)
create mode 100644 include/net/bluetooth/a2mp.h
create mode 100644 net/bluetooth/a2mp.c
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Addings ops->ready() helps to separate socket and socketless
L2CAP channels.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 23 +++++------------------
net/bluetooth/l2cap_sock.c | 22 ++++++++++++++++++++++
3 files changed, 28 insertions(+), 18 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index a017249..83a67e8 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -530,6 +530,7 @@ struct l2cap_ops {
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
unsigned long len, int nb);
void (*unlink) (struct l2cap_chan *chan, int err);
+ void (*ready) (struct l2cap_chan *chan);
};
struct l2cap_conn {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 8ca6e8f..16ec122 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -407,7 +407,7 @@ struct l2cap_chan *l2cap_chan_create(void)
atomic_set(&chan->refcnt, 1);
- /* This flag is cleared in l2cap_chan_ready() */
+ /* This flag is cleared in l2cap chan ops->ready() */
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
BT_DBG("chan %p", chan);
@@ -955,26 +955,14 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
- struct sock *sk = chan->sk;
- struct sock *parent;
-
- lock_sock(sk);
-
- parent = bt_sk(sk)->parent;
-
- BT_DBG("sk %p, parent %p", sk, parent);
+ __clear_chan_timer(chan);
/* This clears all conf flags, including CONF_NOT_COMPLETE */
chan->conf_state = 0;
- __clear_chan_timer(chan);
-
- __l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
+ chan->state = BT_CONNECTED;
- if (parent)
- parent->sk_data_ready(parent, 0);
-
- release_sock(sk);
+ if (chan->ops->ready)
+ chan->ops->ready(chan);
}
static void l2cap_do_start(struct l2cap_chan *chan)
@@ -1243,7 +1231,6 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
l2cap_chan_ready(chan);
-
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
struct sock *sk = chan->sk;
__clear_chan_timer(chan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 6749d11..3d97b58 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -978,6 +978,27 @@ static void l2cap_sock_unlink_cb(struct l2cap_chan *chan, int err)
release_sock(sk);
}
+static void l2cap_sock_chan_ready_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->sk;
+ struct sock *parent;
+
+ lock_sock(sk);
+
+ parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ sk->sk_state = BT_CONNECTED;
+
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+
+ release_sock(sk);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -986,6 +1007,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.state_change = l2cap_sock_state_change_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
.unlink = l2cap_sock_unlink_cb,
+ .ready = l2cap_sock_chan_ready_cb,
};
static void l2cap_sock_destruct(struct sock *sk)
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP fixed channel do not have sk
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/a2mp.c | 3 +--
net/bluetooth/l2cap_core.c | 17 +++++++++++++++++
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b022329..2fd0afd 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -575,6 +575,7 @@ struct l2cap_conn {
#define L2CAP_CHAN_RAW 1
#define L2CAP_CHAN_CONN_LESS 2
#define L2CAP_CHAN_CONN_ORIENTED 3
+#define L2CAP_CHAN_CONN_FIX_A2MP 4
/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index a454f8b..894e53c 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -466,8 +466,7 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
hci_conn_hold(conn->hcon);
- chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
- chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->ops = &a2mp_chan_ops;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 275b8f7..3cbdd1d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -465,6 +465,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
+ case L2CAP_CHAN_CONN_FIX_A2MP:
+ chan->scid = L2CAP_CID_A2MP;
+ chan->dcid = L2CAP_CID_A2MP;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ break;
+
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
@@ -1025,6 +1032,11 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
__clear_ack_timer(chan);
}
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ __l2cap_state_change(chan, BT_DISCONN);
+ return;
+ }
+
req.dcid = cpu_to_le16(chan->dcid);
req.scid = cpu_to_le16(chan->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
@@ -1228,6 +1240,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_lock(chan);
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
l2cap_chan_ready(chan);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP responses, print cmd code and ident for now.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 16598f9..a454f8b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -313,6 +313,15 @@ send_rsp:
return 0;
}
+static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ BT_DBG("ident %d code %d", hdr->ident, hdr->code);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -372,6 +381,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
case A2MP_GETAMPASSOC_RSP:
case A2MP_CREATEPHYSLINK_RSP:
case A2MP_DISCONNPHYSLINK_RSP:
+ err = a2mp_cmd_rsp(mgr, skb, hdr);
+ break;
+
default:
BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
err = -EINVAL;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Disconnect Physlink Request.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ae4e38b..16598f9 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -280,6 +280,39 @@ send_rsp:
return 0;
}
+static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+ hdev = hci_dev_get(req->local_id);
+ if (!hdev) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO Disconnect Phys Link here */
+
+ hci_dev_put(hdev);
+
+send_rsp:
+ a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -330,6 +363,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_DISCONNPHYSLINK_REQ:
+ err = a2mp_discphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
case A2MP_GETINFO_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define AMP Manager and some basic functions.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 30 +++++++++++++++++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/a2mp.c | 54 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 4 +++
4 files changed, 89 insertions(+)
create mode 100644 include/net/bluetooth/a2mp.h
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
new file mode 100644
index 0000000..ff47540
--- /dev/null
+++ b/include/net/bluetooth/a2mp.h
@@ -0,0 +1,30 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 __A2MP_H
+#define __A2MP_H
+
+struct amp_mgr {
+ struct l2cap_conn *l2cap_conn;
+ struct l2cap_chan *a2mp_chan;
+ struct kref kref;
+ __u8 ident;
+ __u8 handle;
+ unsigned long flags;
+};
+
+void amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+
+#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d584a47..6e64b76 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -332,6 +332,7 @@ struct hci_conn {
void *l2cap_data;
void *sco_data;
void *smp_conn;
+ struct amp_mgr *amp_mgr;
struct hci_conn *link;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d1574f5..ea900fb 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -15,6 +15,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/a2mp.h>
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
@@ -65,3 +66,56 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
return chan;
}
+
+/* AMP Manager functions */
+void amp_mgr_get(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ kref_get(&mgr->kref);
+}
+
+static void amp_mgr_destroy(struct kref *kref)
+{
+ struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref);
+
+ BT_DBG("mgr %p", mgr);
+
+ kfree(mgr);
+}
+
+int amp_mgr_put(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ return kref_put(&mgr->kref, &_mgr_destroy);
+}
+
+static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
+{
+ struct amp_mgr *mgr;
+ struct l2cap_chan *chan;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return NULL;
+
+ BT_DBG("conn %p mgr %p", conn, mgr);
+
+ mgr->l2cap_conn = conn;
+
+ chan = a2mp_chan_open(conn);
+ if (!chan) {
+ kfree(mgr);
+ return NULL;
+ }
+
+ mgr->a2mp_chan = chan;
+ chan->data = mgr;
+
+ conn->hcon->amp_mgr = mgr;
+
+ kref_init(&mgr->kref);
+
+ return mgr;
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 8163ee1..111e549 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -28,6 +28,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/a2mp.h>
static void hci_le_connect(struct hci_conn *conn)
{
@@ -411,6 +412,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_chan_list_flush(conn);
+ if (conn->amp_mgr)
+ amp_mgr_put(conn->amp_mgr);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Adds helper functions to count HCI devs and process A2MP Discover
Request, code makes sure that first controller in the list is
BREDR one. Trace is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 16
A2MP: Discover req: mtu/mps 670 mask: 0x0000
< ACL data: handle 11 flags 0x00 dlen 22
A2MP: Discover rsp: mtu/mps 670 mask: 0x0000
Controller list:
id 0 type 0 (BR-EDR) status 0x01 (Bluetooth only)
id 1 type 1 (802.11 AMP) status 0x01 (Bluetooth only)
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 2 +
include/net/bluetooth/hci.h | 3 ++
include/net/bluetooth/hci_core.h | 13 ++++++
net/bluetooth/a2mp.c | 85 ++++++++++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 391acd7..96f9cc2 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#define A2MP_FEAT_EXT 0x8000
+
struct amp_mgr {
struct l2cap_conn *l2cap_conn;
struct l2cap_chan *a2mp_chan;
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index edb6639..158f403 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -58,6 +58,9 @@
#define HCI_BREDR 0x00
#define HCI_AMP 0x01
+/* First BR/EDR Controller shall have ID = 0 */
+#define HCI_BREDR_ID 0
+
/* HCI device quirks */
enum {
HCI_QUIRK_RESET_ON_CLOSE,
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6e64b76..20fd573 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -641,6 +641,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
dev_set_drvdata(&hdev->dev, data);
}
+/* hci_dev_list shall be locked */
+static inline uint8_t __hci_num_ctrl(void)
+{
+ uint8_t count = 0;
+ struct list_head *p;
+
+ list_for_each(p, &hci_dev_list) {
+ count++;
+ }
+
+ return count;
+}
+
struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 4137f9b..ac095ac 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
+{
+ cl->id = 0;
+ cl->type = 0;
+ cl->status = 1;
+}
+
+/* hci_dev_list shall be locked */
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+{
+ int i = 0;
+ struct hci_dev *hdev;
+
+ __a2mp_cl_bredr(cl);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ /* Iterate through AMP controllers */
+ if (hdev->id == HCI_BREDR_ID)
+ continue;
+
+ /* Starting from second entry */
+ if (++i >= num_ctrl)
+ return;
+
+ cl[i].id = hdev->id;
+ cl[i].type = hdev->amp_type;
+ cl[i].status = hdev->amp_status;
+ }
+}
+
/* Processing A2MP messages */
static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
@@ -79,6 +109,58 @@ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_discov_req *req = (struct a2mp_discov_req *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+ struct a2mp_discov_rsp *rsp;
+ u16 ext_feat;
+ u8 num_ctrl;
+
+ if (len < sizeof(*req))
+ return -EINVAL;
+
+ skb_pull(skb, sizeof(*req));
+
+ ext_feat = le16_to_cpu(req->ext_feat);
+
+ BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->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));
+ }
+
+ read_lock(&hci_dev_list_lock);
+
+ num_ctrl = __hci_num_ctrl();
+ len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
+ rsp = kmalloc(len, GFP_ATOMIC);
+ if (!rsp) {
+ read_unlock(&hci_dev_list_lock);
+ return -ENOMEM;
+ }
+
+ rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ rsp->ext_feat = 0;
+
+ __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+
+ read_unlock(&hci_dev_list_lock);
+
+ a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
+
+ kfree(rsp);
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -109,6 +191,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_DISCOVER_REQ:
+ err = a2mp_discover_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Create Physlink Request.
Handles requests with invalid controler id as shown below:
...
> ACL data: handle 11 flags 0x02 dlen 50
A2MP: Create Physical Link req: local id 1 remote id 85
Assoc data:
<skipped>
< ACL data: handle 11 flags 0x00 dlen 15
A2MP: Create Physical Link rsp: local id 85 remote id 1 status 1
Invalid Controller ID
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index c98a402..ae4e38b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -244,6 +244,42 @@ clean:
return 0;
}
+static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+
+ hdev = hci_dev_get(req->remote_id);
+ if (!hdev || hdev->amp_type != HCI_AMP) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO process physlink create */
+
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+send_rsp:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -290,6 +326,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_CREATEPHYSLINK_REQ:
+ err = a2mp_createphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Example trace when receiving AMP Assoc Request with wrong AMP id.
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get AMP Assoc req: id 238
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Get AMP Assoc rsp: id 238 status (1) Invalid Controller ID
assoc data:
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d636c0c..c98a402 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -211,6 +211,39 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_amp_assoc_req *req =
+ (struct a2mp_amp_assoc_req *) skb->data;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("id %d", req->id);
+
+ hdev = hci_dev_get(req->id);
+ if (!hdev || hdev->amp_type == HCI_BREDR) {
+ struct a2mp_amp_assoc_rsp rsp;
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+ goto clean;
+ }
+
+ /* Placeholder for HCI Read AMP Assoc */
+
+clean:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -253,6 +286,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_GETAMPASSOC_REQ:
+ err = a2mp_getampassoc_req(mgr, skb, hdr);
+ break;
+
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for future A2MP Command Reject handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 92b7671..4137f9b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,22 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Processing A2MP messages */
+static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cmd_rej *rej = (struct a2mp_cmd_rej *) skb->data;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*rej))
+ return -EINVAL;
+
+ BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason));
+
+ skb_pull(skb, sizeof(*rej));
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -89,6 +105,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
switch (hdr->code) {
case A2MP_COMMAND_REJ:
+ a2mp_command_rej(mgr, skb, hdr);
+ break;
+
case A2MP_DISCOVER_REQ:
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP Get Info Request.
Example of trace log for invalid controller id is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get Info req: id 238
< ACL data: handle 11 flags 0x00 dlen 30
A2MP: Get Info rsp: id 238 status (1) Invalid Controller ID
...
Note that If the Status field is set to Invalid Controller ID all subsequent
fields in the AMP Get Info Response shall be ignored by the receiver.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d60be75..d636c0c 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -177,6 +177,40 @@ static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_info_req *req = (struct a2mp_info_req *) skb->data;
+ struct a2mp_info_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ 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)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -215,6 +249,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_GETINFO_REQ:
+ err = a2mp_getinfo_req(mgr, skb, hdr);
+ break;
+
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Create and initialize fixed A2MP channel
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 6 ++++
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 67 +++++++++++++++++++++++++++++++++++++++++
net/bluetooth/l2cap_core.c | 6 ++--
4 files changed, 78 insertions(+), 4 deletions(-)
create mode 100644 net/bluetooth/a2mp.c
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 83a67e8..b022329 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -52,6 +52,8 @@
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
+#define L2CAP_A2MP_DEFAULT_MTU 670
+
/* L2CAP socket address */
struct sockaddr_l2 {
sa_family_t l2_family;
@@ -232,6 +234,7 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_A2MP 0x0003
#define L2CAP_CID_LE_DATA 0x0004
#define L2CAP_CID_LE_SIGNALING 0x0005
#define L2CAP_CID_SMP 0x0006
@@ -752,5 +755,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
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_del(struct l2cap_chan *chan, int err);
#endif /* __L2CAP_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 2dc5a57..fa6d94a 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
- hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o
+ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
+ a2mp.o
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
new file mode 100644
index 0000000..d1574f5
--- /dev/null
+++ b/net/bluetooth/a2mp.c
@@ -0,0 +1,67 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+static struct l2cap_ops a2mp_chan_ops = {
+ .name = "L2CAP A2MP channel",
+};
+
+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan;
+ int err;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ BT_DBG("chan %p", chan);
+
+ hci_conn_hold(conn->hcon);
+
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+
+ chan->ops = &a2mp_chan_ops;
+
+ l2cap_chan_set_defaults(chan);
+ chan->remote_max_tx = chan->max_tx;
+ chan->remote_tx_win = chan->tx_win;
+
+ chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ skb_queue_head_init(&chan->tx_q);
+
+ chan->mode = L2CAP_MODE_ERTM;
+
+ err = l2cap_ertm_init(chan);
+ if (err < 0) {
+ l2cap_chan_del(chan, 0);
+ return NULL;
+ }
+
+ chan->conf_state = 0;
+
+ l2cap_chan_add(conn, chan);
+
+ chan->remote_mps = chan->omtu;
+ chan->mps = chan->omtu;
+
+ return chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 16ec122..275b8f7 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -484,14 +484,14 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
list_add(&chan->list, &conn->chan_l);
}
-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)
{
mutex_lock(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
mutex_unlock(&conn->chan_lock);
}
-static void l2cap_chan_del(struct l2cap_chan *chan, int err)
+void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct l2cap_conn *conn = chan->conn;
@@ -2722,7 +2722,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
l2cap_chan_put(chan);
}
-static inline int l2cap_ertm_init(struct l2cap_chan *chan)
+int l2cap_ertm_init(struct l2cap_chan *chan)
{
int err;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Helper function to build and send A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 7 +++++++
net/bluetooth/a2mp.c | 46 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index ff47540..654df60 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -24,6 +24,13 @@ struct amp_mgr {
unsigned long flags;
};
+struct a2mp_cmd {
+ __u8 code;
+ __u8 ident;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ea900fb..e7e5f5f 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -17,6 +17,52 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>
+/* A2MP build & send command helper functions */
+static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
+{
+ struct a2mp_cmd *cmd;
+ int plen;
+
+ plen = sizeof(*cmd) + len;
+ cmd = kzalloc(plen, GFP_KERNEL);
+ if (!cmd)
+ return NULL;
+
+ cmd->code = code;
+ cmd->ident = ident;
+ cmd->len = cpu_to_le16(len);
+
+ memcpy(cmd->data, data, len);
+
+ return cmd;
+}
+
+static 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;
+ u16 total_len = len + sizeof(*cmd);
+ struct kvec iv;
+ struct msghdr msg;
+
+ cmd = __a2mp_build(code, ident, len, data);
+ if (!cmd)
+ return;
+
+ iv.iov_base = cmd;
+ iv.iov_len = total_len;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_iov = (struct iovec *) &iv;
+ msg.msg_iovlen = 1;
+
+ l2cap_chan_send(chan, &msg, total_len, 0);
+
+ kfree(cmd);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
};
--
1.7.9.5
Hi Mat,
On Wed, May 23, 2012 at 04:16:37PM -0700, Mat Martineau wrote:
> >+static void l2cap_sock_chan_ready_cb(struct l2cap_chan *chan)
> >+{
> >+ struct sock *sk = chan->sk;
> >+ struct sock *parent;
> >+
> >+ lock_sock(sk);
> >+
> >+ parent = bt_sk(sk)->parent;
> >+
> >+ BT_DBG("sk %p, parent %p", sk, parent);
> >+
> >+ __clear_chan_timer(chan);
> >+
> >+ /* This clears all conf flags, including CONF_NOT_COMPLETE */
> >+ chan->conf_state = 0;
> >+
> >+ chan->state = BT_CONNECTED;
>
> Similar to patch 1, would there be some benefit to leaving the chan
> code (timer clearing, state and conf_state modification) in
> l2cap_core? These things need to happen even if chan->ops->ready is
> NULL.
This can be done, then I would need to create new function like
l2cap_chan_ready which do chan code and calls ready callback. I will try
to come out with some solution later today.
Best regards
Andrei Emeltchenko
Hi Mat,
On Wed, May 23, 2012 at 08:44:22AM -0700, Mat Martineau wrote:
> >>>+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> >>>+{
> >>>+ struct l2cap_chan *chan;
> >>>+
> >>>+ chan = l2cap_chan_create();
> >>>+ if (!chan)
> >>>+ return NULL;
> >>>+
> >>>+ BT_DBG("chan %p", chan);
> >>>+
> >>>+ hci_conn_hold(conn->hcon);
> >>
> >>Holding the hcon will keep the ACL open after all of the other L2CAP
> >>channels have closed (unless I missed some code later in the patch
> >>series). The A2MP fixed channel should not keep the ACL open. If
> >>the connection is not held here, then there shouldn't be a put in
> >>l2cap_chan_del for the A2MP channel either.
> >
> >l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
> >the same for fixed and normal channels.
>
> And when does l2cap_chan_del get called for a fixed channel? The
> fixed channel must not do an hci_conn_hold so the ACL is allowed to
> close when all dynamic L2CAP channels have closed.
The current approach is to have amp_mgr for hci_conn. It will be freed
when in hci_conn_del together with other l2cap channels in
hci_chan_list_flush and then we make amp_mgr_put() and destroy mgr.
The idea is to make a2mp chan similar to other chans and other chans do
hci_conn_hold and hci_conn_put. Otherwise I would need to add extra checks
before hci_conn_put which is ugly.
I've tested this with PTS and no memory leaks found so far.
Best regards
Andrei Emeltchenko
Andrei -
On Wed, 23 May 2012, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Handle incoming A2MP connection by creating AMP manager and
> processing A2MP messages.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/a2mp.h | 4 ++++
> net/bluetooth/a2mp.c | 12 ++++++++++++
> net/bluetooth/l2cap_core.c | 14 ++++++++++----
> 3 files changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
> index 96f9cc2..6a76e0a 100644
> --- a/include/net/bluetooth/a2mp.h
> +++ b/include/net/bluetooth/a2mp.h
> @@ -15,6 +15,8 @@
> #ifndef __A2MP_H
> #define __A2MP_H
>
> +#include <net/bluetooth/l2cap.h>
> +
> #define A2MP_FEAT_EXT 0x8000
>
> struct amp_mgr {
> @@ -118,5 +120,7 @@ struct a2mp_physlink_rsp {
>
> void amp_mgr_get(struct amp_mgr *mgr);
> int amp_mgr_put(struct amp_mgr *mgr);
> +struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
> + struct sk_buff *skb);
>
> #endif /* __A2MP_H */
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index 894e53c..dc70ee6 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -552,3 +552,15 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
>
> return mgr;
> }
> +
> +struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
> + struct sk_buff *skb)
> +{
> + struct amp_mgr *mgr;
> +
> + mgr = amp_mgr_create(conn);
> +
> + BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
> +
> + return mgr->a2mp_chan;
mgr can be NULL.
> +}
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index bd9197c..9a89c47 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -37,6 +37,7 @@
> #include <net/bluetooth/hci_core.h>
> #include <net/bluetooth/l2cap.h>
> #include <net/bluetooth/smp.h>
> +#include <net/bluetooth/a2mp.h>
>
> bool disable_ertm;
>
> @@ -5167,10 +5168,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
>
> chan = l2cap_get_chan_by_scid(conn, cid);
> if (!chan) {
> - BT_DBG("unknown cid 0x%4.4x", cid);
> - /* Drop packet and return */
> - kfree_skb(skb);
> - return 0;
> + if (cid == L2CAP_CID_A2MP) {
> + chan = a2mp_channel_create(conn, skb);
If amp_mgr_create returned NULL, then you would have to handle NULL
from a2mp_channel_create too.
> + l2cap_chan_lock(chan);
> + } else {
> + BT_DBG("unknown cid 0x%4.4x", cid);
> + /* Drop packet and return */
> + kfree_skb(skb);
> + return 0;
> + }
> }
>
> BT_DBG("chan %p, len %d", chan, skb->len);
> --
> 1.7.9.5
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Andrei -
On Wed, 23 May 2012, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Addings ops->ready() helps to separate socket and socketless
> L2CAP channels.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 1 +
> net/bluetooth/l2cap_core.c | 42 +++++++++++------------------------------
> net/bluetooth/l2cap_sock.c | 28 +++++++++++++++++++++++++++
> 3 files changed, 40 insertions(+), 31 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index a017249..83a67e8 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -530,6 +530,7 @@ struct l2cap_ops {
> struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
> unsigned long len, int nb);
> void (*unlink) (struct l2cap_chan *chan, int err);
> + void (*ready) (struct l2cap_chan *chan);
> };
>
> struct l2cap_conn {
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index c81b828..d5ea8af 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -407,7 +407,7 @@ struct l2cap_chan *l2cap_chan_create(void)
>
> atomic_set(&chan->refcnt, 1);
>
> - /* This flag is cleared in l2cap_chan_ready() */
> + /* This flag is cleared in l2cap chan ops->ready() */
> set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
>
> BT_DBG("chan %p", chan);
> @@ -953,36 +953,13 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
> l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
> }
>
> -static void l2cap_chan_ready(struct l2cap_chan *chan)
> -{
> - struct sock *sk = chan->sk;
> - struct sock *parent;
> -
> - lock_sock(sk);
> -
> - parent = bt_sk(sk)->parent;
> -
> - BT_DBG("sk %p, parent %p", sk, parent);
> -
> - /* This clears all conf flags, including CONF_NOT_COMPLETE */
> - chan->conf_state = 0;
> - __clear_chan_timer(chan);
> -
> - __l2cap_state_change(chan, BT_CONNECTED);
> - sk->sk_state_change(sk);
> -
> - if (parent)
> - parent->sk_data_ready(parent, 0);
> -
> - release_sock(sk);
> -}
> -
> static void l2cap_do_start(struct l2cap_chan *chan)
> {
> struct l2cap_conn *conn = chan->conn;
>
> if (conn->hcon->type == LE_LINK) {
> - l2cap_chan_ready(chan);
> + if (chan->ops->ready)
> + chan->ops->ready(chan);
> return;
> }
>
> @@ -1242,8 +1219,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
>
> if (conn->hcon->type == LE_LINK) {
> if (smp_conn_security(conn, chan->sec_level))
> - l2cap_chan_ready(chan);
> -
> + if (chan->ops->ready)
> + chan->ops->ready(chan);
> } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
> struct sock *sk = chan->sk;
> __clear_chan_timer(chan);
> @@ -3639,7 +3616,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
> if (err < 0)
> l2cap_send_disconn_req(chan->conn, chan, -err);
> else
> - l2cap_chan_ready(chan);
> + if (chan->ops->ready)
> + chan->ops->ready(chan);
>
> goto unlock;
> }
> @@ -3770,7 +3748,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
> if (err < 0)
> l2cap_send_disconn_req(chan->conn, chan, -err);
> else
> - l2cap_chan_ready(chan);
> + if (chan->ops->ready)
> + chan->ops->ready(chan);
> }
>
> done:
> @@ -5420,7 +5399,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
> if (chan->scid == L2CAP_CID_LE_DATA) {
> if (!status && encrypt) {
> chan->sec_level = hcon->sec_level;
> - l2cap_chan_ready(chan);
> + if (chan->ops->ready)
> + chan->ops->ready(chan);
> }
>
> l2cap_chan_unlock(chan);
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index 6749d11..2e078c4 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -978,6 +978,33 @@ static void l2cap_sock_unlink_cb(struct l2cap_chan *chan, int err)
> release_sock(sk);
> }
>
> +static void l2cap_sock_chan_ready_cb(struct l2cap_chan *chan)
> +{
> + struct sock *sk = chan->sk;
> + struct sock *parent;
> +
> + lock_sock(sk);
> +
> + parent = bt_sk(sk)->parent;
> +
> + BT_DBG("sk %p, parent %p", sk, parent);
> +
> + __clear_chan_timer(chan);
> +
> + /* This clears all conf flags, including CONF_NOT_COMPLETE */
> + chan->conf_state = 0;
> +
> + chan->state = BT_CONNECTED;
Similar to patch 1, would there be some benefit to leaving the chan
code (timer clearing, state and conf_state modification) in
l2cap_core? These things need to happen even if chan->ops->ready is
NULL.
> + sk->sk_state = BT_CONNECTED;
> +
> + sk->sk_state_change(sk);
> +
> + if (parent)
> + parent->sk_data_ready(parent, 0);
> +
> + release_sock(sk);
> +}
> +
> static struct l2cap_ops l2cap_chan_ops = {
> .name = "L2CAP Socket Interface",
> .new_connection = l2cap_sock_new_connection_cb,
> @@ -986,6 +1013,7 @@ static struct l2cap_ops l2cap_chan_ops = {
> .state_change = l2cap_sock_state_change_cb,
> .alloc_skb = l2cap_sock_alloc_skb_cb,
> .unlink = l2cap_sock_unlink_cb,
> + .ready = l2cap_sock_chan_ready_cb,
> };
>
> static void l2cap_sock_destruct(struct sock *sk)
> --
> 1.7.9.5
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Andrei -
On Wed, 23 May 2012, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> This helps to separate socket and socketless code. Now socket related
> operations moved to l2cap_sock in unlink callback for channels with sk.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 1 +
> net/bluetooth/l2cap_core.c | 19 ++-----------------
> net/bluetooth/l2cap_sock.c | 25 +++++++++++++++++++++++++
> 3 files changed, 28 insertions(+), 17 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 0142257..a017249 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -529,6 +529,7 @@ struct l2cap_ops {
> void (*state_change) (void *data, int state);
> struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
> unsigned long len, int nb);
> + void (*unlink) (struct l2cap_chan *chan, int err);
> };
>
> struct l2cap_conn {
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index e31b005..c81b828 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -493,9 +493,7 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
>
> static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> {
> - struct sock *sk = chan->sk;
> struct l2cap_conn *conn = chan->conn;
> - struct sock *parent = bt_sk(sk)->parent;
>
> __clear_chan_timer(chan);
>
> @@ -511,21 +509,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> hci_conn_put(conn->hcon);
> }
>
> - lock_sock(sk);
> -
> - __l2cap_state_change(chan, BT_CLOSED);
> - sock_set_flag(sk, SOCK_ZAPPED);
> -
> - if (err)
> - __l2cap_chan_set_err(chan, err);
> -
> - if (parent) {
> - bt_accept_unlink(sk);
> - parent->sk_data_ready(parent, 0);
> - } else
> - sk->sk_state_change(sk);
> -
> - release_sock(sk);
> + if (chan->ops->unlink)
> + chan->ops->unlink(chan, err);
>
> if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
> return;
> diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
> index 4d36605..6749d11 100644
> --- a/net/bluetooth/l2cap_sock.c
> +++ b/net/bluetooth/l2cap_sock.c
> @@ -954,6 +954,30 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
> return skb;
> }
>
> +static void l2cap_sock_unlink_cb(struct l2cap_chan *chan, int err)
> +{
> + struct sock *sk = chan->sk;
> + struct sock *parent = bt_sk(sk)->parent;
> +
> + lock_sock(sk);
> +
> + chan->state = BT_CLOSED;
I think this line had better stay in l2cap_chan_del - otherwise every
unlink callback has to do it. It's also the only modification to chan
in here. Since the channel must be locked before l2cap_chan_del is
called anyway, I don't think it matters if chan->state is modified
before or after the call to chan->ops->unlink.
> + sk->sk_state = BT_CLOSED;
> + sock_set_flag(sk, SOCK_ZAPPED);
> +
> + if (err)
> + sk->sk_err = err;
> +
> + parent = bt_sk(sk)->parent;
> + if (parent) {
> + bt_accept_unlink(sk);
> + parent->sk_data_ready(parent, 0);
> + } else
> + sk->sk_state_change(sk);
> +
> + release_sock(sk);
> +}
> +
> static struct l2cap_ops l2cap_chan_ops = {
> .name = "L2CAP Socket Interface",
> .new_connection = l2cap_sock_new_connection_cb,
> @@ -961,6 +985,7 @@ static struct l2cap_ops l2cap_chan_ops = {
> .close = l2cap_sock_close_cb,
> .state_change = l2cap_sock_state_change_cb,
> .alloc_skb = l2cap_sock_alloc_skb_cb,
> + .unlink = l2cap_sock_unlink_cb,
> };
>
> static void l2cap_sock_destruct(struct sock *sk)
> --
> 1.7.9.5
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Andrei -
On Wed, 23 May 2012, Andrei Emeltchenko wrote:
> Hi Mat,
>
> On Tue, May 22, 2012 at 10:45:07AM -0700, Mat Martineau wrote:
>>> +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
>>> +{
>>> + struct l2cap_chan *chan;
>>> +
>>> + chan = l2cap_chan_create();
>>> + if (!chan)
>>> + return NULL;
>>> +
>>> + BT_DBG("chan %p", chan);
>>> +
>>> + hci_conn_hold(conn->hcon);
>>
>> Holding the hcon will keep the ACL open after all of the other L2CAP
>> channels have closed (unless I missed some code later in the patch
>> series). The A2MP fixed channel should not keep the ACL open. If
>> the connection is not held here, then there shouldn't be a put in
>> l2cap_chan_del for the A2MP channel either.
>
> l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
> the same for fixed and normal channels.
And when does l2cap_chan_del get called for a fixed channel? The
fixed channel must not do an hci_conn_hold so the ACL is allowed to
close when all dynamic L2CAP channels have closed.
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
From: Andrei Emeltchenko <[email protected]>
Create and initialize fixed A2MP channel
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 6 ++++
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 67 +++++++++++++++++++++++++++++++++++++++++
net/bluetooth/l2cap_core.c | 6 ++--
4 files changed, 78 insertions(+), 4 deletions(-)
create mode 100644 net/bluetooth/a2mp.c
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 83a67e8..b022329 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -52,6 +52,8 @@
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
+#define L2CAP_A2MP_DEFAULT_MTU 670
+
/* L2CAP socket address */
struct sockaddr_l2 {
sa_family_t l2_family;
@@ -232,6 +234,7 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_A2MP 0x0003
#define L2CAP_CID_LE_DATA 0x0004
#define L2CAP_CID_LE_SIGNALING 0x0005
#define L2CAP_CID_SMP 0x0006
@@ -752,5 +755,8 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
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_del(struct l2cap_chan *chan, int err);
#endif /* __L2CAP_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 2dc5a57..fa6d94a 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
- hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o
+ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
+ a2mp.o
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
new file mode 100644
index 0000000..d1574f5
--- /dev/null
+++ b/net/bluetooth/a2mp.c
@@ -0,0 +1,67 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+static struct l2cap_ops a2mp_chan_ops = {
+ .name = "L2CAP A2MP channel",
+};
+
+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan;
+ int err;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ BT_DBG("chan %p", chan);
+
+ hci_conn_hold(conn->hcon);
+
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+
+ chan->ops = &a2mp_chan_ops;
+
+ l2cap_chan_set_defaults(chan);
+ chan->remote_max_tx = chan->max_tx;
+ chan->remote_tx_win = chan->tx_win;
+
+ chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ skb_queue_head_init(&chan->tx_q);
+
+ chan->mode = L2CAP_MODE_ERTM;
+
+ err = l2cap_ertm_init(chan);
+ if (err < 0) {
+ l2cap_chan_del(chan, 0);
+ return NULL;
+ }
+
+ chan->conf_state = 0;
+
+ l2cap_chan_add(conn, chan);
+
+ chan->remote_mps = chan->omtu;
+ chan->mps = chan->omtu;
+
+ return chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d5ea8af..988ee09 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -484,14 +484,14 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
list_add(&chan->list, &conn->chan_l);
}
-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)
{
mutex_lock(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
mutex_unlock(&conn->chan_lock);
}
-static void l2cap_chan_del(struct l2cap_chan *chan, int err)
+void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct l2cap_conn *conn = chan->conn;
@@ -2712,7 +2712,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
l2cap_chan_put(chan);
}
-static inline int l2cap_ertm_init(struct l2cap_chan *chan)
+int l2cap_ertm_init(struct l2cap_chan *chan)
{
int err;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Adds helper functions to count HCI devs and process A2MP Discover
Request, code makes sure that first controller in the list is
BREDR one. Trace is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 16
A2MP: Discover req: mtu/mps 670 mask: 0x0000
< ACL data: handle 11 flags 0x00 dlen 22
A2MP: Discover rsp: mtu/mps 670 mask: 0x0000
Controller list:
id 0 type 0 (BR-EDR) status 0x01 (Bluetooth only)
id 1 type 1 (802.11 AMP) status 0x01 (Bluetooth only)
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 2 +
include/net/bluetooth/hci.h | 3 ++
include/net/bluetooth/hci_core.h | 13 ++++++
net/bluetooth/a2mp.c | 85 ++++++++++++++++++++++++++++++++++++++
4 files changed, 103 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 391acd7..96f9cc2 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#define A2MP_FEAT_EXT 0x8000
+
struct amp_mgr {
struct l2cap_conn *l2cap_conn;
struct l2cap_chan *a2mp_chan;
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index edb6639..158f403 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -58,6 +58,9 @@
#define HCI_BREDR 0x00
#define HCI_AMP 0x01
+/* First BR/EDR Controller shall have ID = 0 */
+#define HCI_BREDR_ID 0
+
/* HCI device quirks */
enum {
HCI_QUIRK_RESET_ON_CLOSE,
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6e64b76..20fd573 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -641,6 +641,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
dev_set_drvdata(&hdev->dev, data);
}
+/* hci_dev_list shall be locked */
+static inline uint8_t __hci_num_ctrl(void)
+{
+ uint8_t count = 0;
+ struct list_head *p;
+
+ list_for_each(p, &hci_dev_list) {
+ count++;
+ }
+
+ return count;
+}
+
struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 4137f9b..ac095ac 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
+{
+ cl->id = 0;
+ cl->type = 0;
+ cl->status = 1;
+}
+
+/* hci_dev_list shall be locked */
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+{
+ int i = 0;
+ struct hci_dev *hdev;
+
+ __a2mp_cl_bredr(cl);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ /* Iterate through AMP controllers */
+ if (hdev->id == HCI_BREDR_ID)
+ continue;
+
+ /* Starting from second entry */
+ if (++i >= num_ctrl)
+ return;
+
+ cl[i].id = hdev->id;
+ cl[i].type = hdev->amp_type;
+ cl[i].status = hdev->amp_status;
+ }
+}
+
/* Processing A2MP messages */
static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
@@ -79,6 +109,58 @@ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_discov_req *req = (struct a2mp_discov_req *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+ struct a2mp_discov_rsp *rsp;
+ u16 ext_feat;
+ u8 num_ctrl;
+
+ if (len < sizeof(*req))
+ return -EINVAL;
+
+ skb_pull(skb, sizeof(*req));
+
+ ext_feat = le16_to_cpu(req->ext_feat);
+
+ BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->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));
+ }
+
+ read_lock(&hci_dev_list_lock);
+
+ num_ctrl = __hci_num_ctrl();
+ len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
+ rsp = kmalloc(len, GFP_ATOMIC);
+ if (!rsp) {
+ read_unlock(&hci_dev_list_lock);
+ return -ENOMEM;
+ }
+
+ rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ rsp->ext_feat = 0;
+
+ __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+
+ read_unlock(&hci_dev_list_lock);
+
+ a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
+
+ kfree(rsp);
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -109,6 +191,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_DISCOVER_REQ:
+ err = a2mp_discover_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Basic A2MP implementation.
Changes:
* PATCHv2: Rebased against recent changes, changed amp_mgr list to pointer
in hci_conn, reworked handling fixed channels and create A2MP channel patches,
adding unlink and ready callbacks to get rid of socket dependecies in L2CAP and
other minor fixes.
* PATCHv1: Rebasing against latest tree and minor fixes.
* RFCv7: Taking comments from review in linux-bluetooth.
* RFCv6: Remove some unneded check for sk since they are in different path
(A2MP decoded as a data channel wrt L2CAP signalling channel). Style fixes.
* RFCv5: Fix memory leaks, sparse warnings and taking comments from upstream.
* RFCv4: redesign code to use l2cap channel lock instead of socket lock
and general modifications. Basic implementation of HCI callback interface.
* RFCv3: redesign code to use l2cap functions instead of kernel sockets
L2CAP functions modified to work with channels without sk.
* RFCv2: rebased against "workqueue" patches.
* RFCv1: added refcnt to amp_mgr, fixed sleeping in atomic
Initially code was based of reference implementations below:
References: Code Aurora (git://codeaurora.org/kernel/msm.git) and Atheros
(search for: [PATCH 2/3] Add a2mp protocol/AMP manager, by Atheros Linux BT3)
implementations.
Andrei Emeltchenko (19):
Bluetooth: Add unlink to L2CAP channel ops
Bluetooth: Add ready to L2CAP channel ops
Bluetooth: A2MP: Create A2MP channel
Bluetooth: A2MP: AMP Manager basic functions
Bluetooth: A2MP: Build and Send msg helpers
Bluetooth: A2MP: Add chan callbacks
Bluetooth: A2MP: Definitions for A2MP commands
Bluetooth: A2MP: Define A2MP status codes
Bluetooth: A2MP: Process A2MP messages
Bluetooth: A2MP: Process A2MP Command Reject
Bluetooth: A2MP: Process A2MP Discover Request
Bluetooth: A2MP: Process A2MP Change Notify
Bluetooth: A2MP: Process A2MP Get Info Request
Bluetooth: A2MP: Process A2MP Get AMP Assoc Request
Bluetooth: A2MP: Process A2MP Create Physlink Request
Bluetooth: A2MP: Process A2MP Disc Physlink Request
Bluetooth: A2MP: Process A2MP Command Responses
Bluetooth: A2MP: Handling fixed channels
Bluetooth: A2MP: Manage incoming connections
include/net/bluetooth/a2mp.h | 126 +++++++++
include/net/bluetooth/hci.h | 3 +
include/net/bluetooth/hci_core.h | 14 +
include/net/bluetooth/l2cap.h | 9 +
net/bluetooth/Makefile | 3 +-
net/bluetooth/a2mp.c | 566 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 4 +
net/bluetooth/l2cap_core.c | 98 +++----
net/bluetooth/l2cap_sock.c | 53 ++++
9 files changed, 820 insertions(+), 56 deletions(-)
create mode 100644 include/net/bluetooth/a2mp.h
create mode 100644 net/bluetooth/a2mp.c
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Implement basic processing for AMP Manager Protocol (A2MP).
Example below shows processing unrecognized command.
...
> ACL data: handle 11 flags 0x02 dlen 12
A2MP: code 0x00 ident 3 len 0
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Command Reject: reason (0) - Command not recognized
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index eb98541..92b7671 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,70 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Handle A2MP signalling */
+static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
+{
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ struct amp_mgr *mgr = data;
+ int err = 0;
+
+ amp_mgr_get(mgr);
+
+ while (skb->len >= sizeof(*hdr)) {
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+
+ BT_DBG("code 0x%02x id %d len %d", hdr->code, hdr->ident, len);
+
+ skb_pull(skb, sizeof(*hdr));
+
+ if (len > skb->len || !hdr->ident) {
+ err = -EINVAL;
+ break;
+ }
+
+ mgr->ident = hdr->ident;
+
+ switch (hdr->code) {
+ case A2MP_COMMAND_REJ:
+ case A2MP_DISCOVER_REQ:
+ case A2MP_CHANGE_NOTIFY:
+ case A2MP_GETINFO_REQ:
+ case A2MP_GETAMPASSOC_REQ:
+ case A2MP_CREATEPHYSLINK_REQ:
+ case A2MP_DISCONNPHYSLINK_REQ:
+ case A2MP_CHANGE_RSP:
+ case A2MP_DISCOVER_RSP:
+ case A2MP_GETINFO_RSP:
+ case A2MP_GETAMPASSOC_RSP:
+ case A2MP_CREATEPHYSLINK_RSP:
+ case A2MP_DISCONNPHYSLINK_RSP:
+ default:
+ BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (err) {
+ struct a2mp_cmd_rej rej;
+ rej.reason = cpu_to_le16(0);
+
+ BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
+
+ a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
+ &rej);
+ }
+
+ /* Always free skb and return success error code to prevent
+ from sending L2CAP Disconnect over A2MP channel */
+ kfree_skb(skb);
+
+ amp_mgr_put(mgr);
+
+ return 0;
+}
+
static void a2mp_chan_state_change_cb(void *data, int state)
{
struct amp_mgr *mgr = data;
@@ -100,6 +164,7 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .recv = a2mp_chan_recv_cb,
.state_change = a2mp_chan_state_change_cb,
.close = a2mp_chan_close_cb,
.alloc_skb = a2mp_chan_alloc_skb_cb,
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Change Notify handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ac095ac..d60be75 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -161,6 +161,22 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cl *cl = (struct a2mp_cl *) skb->data;
+
+ while (skb->len >= sizeof(*cl)) {
+ BT_DBG("Controller id %d type %d status %d", cl->id, cl->type,
+ cl->status);
+ cl = (struct a2mp_cl *) skb_pull(skb, sizeof(*cl));
+ }
+
+ /* TODO send A2MP_CHANGE_RSP */
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -195,6 +211,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_CHANGE_NOTIFY:
+ err = a2mp_change_notify(mgr, skb, hdr);
+ break;
+
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Handle incoming A2MP connection by creating AMP manager and
processing A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 4 ++++
net/bluetooth/a2mp.c | 12 ++++++++++++
net/bluetooth/l2cap_core.c | 14 ++++++++++----
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 96f9cc2..6a76e0a 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#include <net/bluetooth/l2cap.h>
+
#define A2MP_FEAT_EXT 0x8000
struct amp_mgr {
@@ -118,5 +120,7 @@ struct a2mp_physlink_rsp {
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb);
#endif /* __A2MP_H */
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 894e53c..dc70ee6 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -552,3 +552,15 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
return mgr;
}
+
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct amp_mgr *mgr;
+
+ mgr = amp_mgr_create(conn);
+
+ BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
+
+ return mgr->a2mp_chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index bd9197c..9a89c47 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -37,6 +37,7 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
+#include <net/bluetooth/a2mp.h>
bool disable_ertm;
@@ -5167,10 +5168,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
- BT_DBG("unknown cid 0x%4.4x", cid);
- /* Drop packet and return */
- kfree_skb(skb);
- return 0;
+ if (cid == L2CAP_CID_A2MP) {
+ chan = a2mp_channel_create(conn, skb);
+ l2cap_chan_lock(chan);
+ } else {
+ BT_DBG("unknown cid 0x%4.4x", cid);
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
+ }
}
BT_DBG("chan %p, len %d", chan, skb->len);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Create Physlink Request.
Handles requests with invalid controler id as shown below:
...
> ACL data: handle 11 flags 0x02 dlen 50
A2MP: Create Physical Link req: local id 1 remote id 85
Assoc data:
<skipped>
< ACL data: handle 11 flags 0x00 dlen 15
A2MP: Create Physical Link rsp: local id 85 remote id 1 status 1
Invalid Controller ID
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index c98a402..ae4e38b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -244,6 +244,42 @@ clean:
return 0;
}
+static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+
+ hdev = hci_dev_get(req->remote_id);
+ if (!hdev || hdev->amp_type != HCI_AMP) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO process physlink create */
+
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+send_rsp:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -290,6 +326,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_CREATEPHYSLINK_REQ:
+ err = a2mp_createphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for future A2MP Command Reject handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 92b7671..4137f9b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,22 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Processing A2MP messages */
+static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cmd_rej *rej = (struct a2mp_cmd_rej *) skb->data;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*rej))
+ return -EINVAL;
+
+ BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason));
+
+ skb_pull(skb, sizeof(*rej));
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -89,6 +105,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
switch (hdr->code) {
case A2MP_COMMAND_REJ:
+ a2mp_command_rej(mgr, skb, hdr);
+ break;
+
case A2MP_DISCOVER_REQ:
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define AMP Manager and some basic functions.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 30 +++++++++++++++++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/a2mp.c | 54 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 4 +++
4 files changed, 89 insertions(+)
create mode 100644 include/net/bluetooth/a2mp.h
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
new file mode 100644
index 0000000..ff47540
--- /dev/null
+++ b/include/net/bluetooth/a2mp.h
@@ -0,0 +1,30 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 __A2MP_H
+#define __A2MP_H
+
+struct amp_mgr {
+ struct l2cap_conn *l2cap_conn;
+ struct l2cap_chan *a2mp_chan;
+ struct kref kref;
+ __u8 ident;
+ __u8 handle;
+ unsigned long flags;
+};
+
+void amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+
+#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index d584a47..6e64b76 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -332,6 +332,7 @@ struct hci_conn {
void *l2cap_data;
void *sco_data;
void *smp_conn;
+ struct amp_mgr *amp_mgr;
struct hci_conn *link;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d1574f5..ea900fb 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -15,6 +15,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/a2mp.h>
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
@@ -65,3 +66,56 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
return chan;
}
+
+/* AMP Manager functions */
+void amp_mgr_get(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ kref_get(&mgr->kref);
+}
+
+static void amp_mgr_destroy(struct kref *kref)
+{
+ struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref);
+
+ BT_DBG("mgr %p", mgr);
+
+ kfree(mgr);
+}
+
+int amp_mgr_put(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ return kref_put(&mgr->kref, &_mgr_destroy);
+}
+
+static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
+{
+ struct amp_mgr *mgr;
+ struct l2cap_chan *chan;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return NULL;
+
+ BT_DBG("conn %p mgr %p", conn, mgr);
+
+ mgr->l2cap_conn = conn;
+
+ chan = a2mp_chan_open(conn);
+ if (!chan) {
+ kfree(mgr);
+ return NULL;
+ }
+
+ mgr->a2mp_chan = chan;
+ chan->data = mgr;
+
+ conn->hcon->amp_mgr = mgr;
+
+ kref_init(&mgr->kref);
+
+ return mgr;
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 8163ee1..111e549 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -28,6 +28,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/a2mp.h>
static void hci_le_connect(struct hci_conn *conn)
{
@@ -411,6 +412,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_chan_list_flush(conn);
+ if (conn->amp_mgr)
+ amp_mgr_put(conn->amp_mgr);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
This helps to separate socket and socketless code. Now socket related
operations moved to l2cap_sock in unlink callback for channels with sk.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 19 ++-----------------
net/bluetooth/l2cap_sock.c | 25 +++++++++++++++++++++++++
3 files changed, 28 insertions(+), 17 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 0142257..a017249 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -529,6 +529,7 @@ struct l2cap_ops {
void (*state_change) (void *data, int state);
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
unsigned long len, int nb);
+ void (*unlink) (struct l2cap_chan *chan, int err);
};
struct l2cap_conn {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index e31b005..c81b828 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -493,9 +493,7 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
static void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
- struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
- struct sock *parent = bt_sk(sk)->parent;
__clear_chan_timer(chan);
@@ -511,21 +509,8 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}
- lock_sock(sk);
-
- __l2cap_state_change(chan, BT_CLOSED);
- sock_set_flag(sk, SOCK_ZAPPED);
-
- if (err)
- __l2cap_chan_set_err(chan, err);
-
- if (parent) {
- bt_accept_unlink(sk);
- parent->sk_data_ready(parent, 0);
- } else
- sk->sk_state_change(sk);
-
- release_sock(sk);
+ if (chan->ops->unlink)
+ chan->ops->unlink(chan, err);
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
return;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 4d36605..6749d11 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -954,6 +954,30 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
return skb;
}
+static void l2cap_sock_unlink_cb(struct l2cap_chan *chan, int err)
+{
+ struct sock *sk = chan->sk;
+ struct sock *parent = bt_sk(sk)->parent;
+
+ lock_sock(sk);
+
+ chan->state = BT_CLOSED;
+ sk->sk_state = BT_CLOSED;
+ sock_set_flag(sk, SOCK_ZAPPED);
+
+ if (err)
+ sk->sk_err = err;
+
+ parent = bt_sk(sk)->parent;
+ if (parent) {
+ bt_accept_unlink(sk);
+ parent->sk_data_ready(parent, 0);
+ } else
+ sk->sk_state_change(sk);
+
+ release_sock(sk);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -961,6 +985,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.close = l2cap_sock_close_cb,
.state_change = l2cap_sock_state_change_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
+ .unlink = l2cap_sock_unlink_cb,
};
static void l2cap_sock_destruct(struct sock *sk)
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP Get Info Request.
Example of trace log for invalid controller id is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get Info req: id 238
< ACL data: handle 11 flags 0x00 dlen 30
A2MP: Get Info rsp: id 238 status (1) Invalid Controller ID
...
Note that If the Status field is set to Invalid Controller ID all subsequent
fields in the AMP Get Info Response shall be ignored by the receiver.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d60be75..d636c0c 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -177,6 +177,40 @@ static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_info_req *req = (struct a2mp_info_req *) skb->data;
+ struct a2mp_info_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ 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)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -215,6 +249,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_GETINFO_REQ:
+ err = a2mp_getinfo_req(mgr, skb, hdr);
+ break;
+
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Add state change, close and skb allocation callbacks.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index e7e5f5f..eb98541 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,8 +63,46 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static void a2mp_chan_state_change_cb(void *data, int state)
+{
+ struct amp_mgr *mgr = data;
+ struct l2cap_chan *chan;
+
+ if (!mgr)
+ return;
+
+ chan = mgr->a2mp_chan;
+
+ BT_DBG("chan %p state %s", chan, state_to_string(state));
+
+ chan->state = state;
+
+ switch (state) {
+ case BT_CLOSED:
+ if (mgr)
+ amp_mgr_put(mgr);
+ break;
+ }
+}
+
+static void a2mp_chan_close_cb(void *data)
+{
+ struct amp_mgr *mgr = data;
+
+ l2cap_chan_destroy(mgr->a2mp_chan);
+}
+
+static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb)
+{
+ return bt_skb_alloc(len, GFP_KERNEL);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .state_change = a2mp_chan_state_change_cb,
+ .close = a2mp_chan_close_cb,
+ .alloc_skb = a2mp_chan_alloc_skb_cb,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
@@ -110,6 +148,8 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
chan->remote_mps = chan->omtu;
chan->mps = chan->omtu;
+ chan->state = BT_CONNECTED;
+
return chan;
}
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Example trace when receiving AMP Assoc Request with wrong AMP id.
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get AMP Assoc req: id 238
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Get AMP Assoc rsp: id 238 status (1) Invalid Controller ID
assoc data:
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index d636c0c..c98a402 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -211,6 +211,39 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_amp_assoc_req *req =
+ (struct a2mp_amp_assoc_req *) skb->data;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("id %d", req->id);
+
+ hdev = hci_dev_get(req->id);
+ if (!hdev || hdev->amp_type == HCI_BREDR) {
+ struct a2mp_amp_assoc_rsp rsp;
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+ goto clean;
+ }
+
+ /* Placeholder for HCI Read AMP Assoc */
+
+clean:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -253,6 +286,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_GETAMPASSOC_REQ:
+ err = a2mp_getampassoc_req(mgr, skb, hdr);
+ break;
+
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP status codes copied from Bluez patch sent by Peter Krystad
<[email protected]>.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 7cbeb91..391acd7 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -104,6 +104,16 @@ struct a2mp_physlink_rsp {
__u8 status;
} __packed;
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP fixed channel do not have sk
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/a2mp.c | 3 +--
net/bluetooth/l2cap_core.c | 17 +++++++++++++++++
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index b022329..2fd0afd 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -575,6 +575,7 @@ struct l2cap_conn {
#define L2CAP_CHAN_RAW 1
#define L2CAP_CHAN_CONN_LESS 2
#define L2CAP_CHAN_CONN_ORIENTED 3
+#define L2CAP_CHAN_CONN_FIX_A2MP 4
/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index a454f8b..894e53c 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -466,8 +466,7 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
hci_conn_hold(conn->hcon);
- chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
- chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->ops = &a2mp_chan_ops;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 988ee09..bd9197c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -465,6 +465,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
+ case L2CAP_CHAN_CONN_FIX_A2MP:
+ chan->scid = L2CAP_CID_A2MP;
+ chan->dcid = L2CAP_CID_A2MP;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ break;
+
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
@@ -1014,6 +1021,11 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
__clear_ack_timer(chan);
}
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ __l2cap_state_change(chan, BT_DISCONN);
+ return;
+ }
+
req.dcid = cpu_to_le16(chan->dcid);
req.scid = cpu_to_le16(chan->scid);
l2cap_send_cmd(conn, l2cap_get_ident(conn),
@@ -1217,6 +1229,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_lock(chan);
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
if (chan->ops->ready)
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Disconnect Physlink Request.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ae4e38b..16598f9 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -280,6 +280,39 @@ send_rsp:
return 0;
}
+static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+ hdev = hci_dev_get(req->local_id);
+ if (!hdev) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO Disconnect Phys Link here */
+
+ hci_dev_put(hdev);
+
+send_rsp:
+ a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -330,6 +363,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_DISCONNPHYSLINK_REQ:
+ err = a2mp_discphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
case A2MP_GETINFO_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP responses, print cmd code and ident for now.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 16598f9..a454f8b 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -313,6 +313,15 @@ send_rsp:
return 0;
}
+static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ BT_DBG("ident %d code %d", hdr->ident, hdr->code);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -372,6 +381,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
case A2MP_GETAMPASSOC_RSP:
case A2MP_CREATEPHYSLINK_RSP:
case A2MP_DISCONNPHYSLINK_RSP:
+ err = a2mp_cmd_rsp(mgr, skb, hdr);
+ break;
+
default:
BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
err = -EINVAL;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define A2MP command IDs and packet structures.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 73 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 654df60..7cbeb91 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -31,6 +31,79 @@ struct a2mp_cmd {
__u8 data[0];
} __packed;
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ 0x01
+struct a2mp_cmd_rej {
+ __le16 reason;
+ __u8 data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ 0x02
+struct a2mp_discov_req {
+ __le16 mtu;
+ __le16 ext_feat;
+} __packed;
+
+struct a2mp_cl {
+ __u8 id;
+ __u8 type;
+ __u8 status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP 0x03
+struct a2mp_discov_rsp {
+ __le16 mtu;
+ __le16 ext_feat;
+ struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+
+#define A2MP_GETINFO_REQ 0x06
+struct a2mp_info_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETINFO_RSP 0x07
+struct a2mp_info_rsp {
+ __u8 id;
+ __u8 status;
+ __le32 total_bw;
+ __le32 max_bw;
+ __le32 min_latency;
+ __le16 pal_cap;
+ __le16 assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ 0x08
+struct a2mp_amp_assoc_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP 0x09
+struct a2mp_amp_assoc_rsp {
+ __u8 id;
+ __u8 status;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ 0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP 0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 status;
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Helper function to build and send A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 7 +++++++
net/bluetooth/a2mp.c | 46 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index ff47540..654df60 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -24,6 +24,13 @@ struct amp_mgr {
unsigned long flags;
};
+struct a2mp_cmd {
+ __u8 code;
+ __u8 ident;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ea900fb..e7e5f5f 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -17,6 +17,52 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>
+/* A2MP build & send command helper functions */
+static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
+{
+ struct a2mp_cmd *cmd;
+ int plen;
+
+ plen = sizeof(*cmd) + len;
+ cmd = kzalloc(plen, GFP_KERNEL);
+ if (!cmd)
+ return NULL;
+
+ cmd->code = code;
+ cmd->ident = ident;
+ cmd->len = cpu_to_le16(len);
+
+ memcpy(cmd->data, data, len);
+
+ return cmd;
+}
+
+static 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;
+ u16 total_len = len + sizeof(*cmd);
+ struct kvec iv;
+ struct msghdr msg;
+
+ cmd = __a2mp_build(code, ident, len, data);
+ if (!cmd)
+ return;
+
+ iv.iov_base = cmd;
+ iv.iov_len = total_len;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_iov = (struct iovec *) &iv;
+ msg.msg_iovlen = 1;
+
+ l2cap_chan_send(chan, &msg, total_len, 0);
+
+ kfree(cmd);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
};
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Addings ops->ready() helps to separate socket and socketless
L2CAP channels.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/l2cap_core.c | 42 +++++++++++------------------------------
net/bluetooth/l2cap_sock.c | 28 +++++++++++++++++++++++++++
3 files changed, 40 insertions(+), 31 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index a017249..83a67e8 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -530,6 +530,7 @@ struct l2cap_ops {
struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan,
unsigned long len, int nb);
void (*unlink) (struct l2cap_chan *chan, int err);
+ void (*ready) (struct l2cap_chan *chan);
};
struct l2cap_conn {
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index c81b828..d5ea8af 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -407,7 +407,7 @@ struct l2cap_chan *l2cap_chan_create(void)
atomic_set(&chan->refcnt, 1);
- /* This flag is cleared in l2cap_chan_ready() */
+ /* This flag is cleared in l2cap chan ops->ready() */
set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
BT_DBG("chan %p", chan);
@@ -953,36 +953,13 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
}
-static void l2cap_chan_ready(struct l2cap_chan *chan)
-{
- struct sock *sk = chan->sk;
- struct sock *parent;
-
- lock_sock(sk);
-
- parent = bt_sk(sk)->parent;
-
- BT_DBG("sk %p, parent %p", sk, parent);
-
- /* This clears all conf flags, including CONF_NOT_COMPLETE */
- chan->conf_state = 0;
- __clear_chan_timer(chan);
-
- __l2cap_state_change(chan, BT_CONNECTED);
- sk->sk_state_change(sk);
-
- if (parent)
- parent->sk_data_ready(parent, 0);
-
- release_sock(sk);
-}
-
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
if (conn->hcon->type == LE_LINK) {
- l2cap_chan_ready(chan);
+ if (chan->ops->ready)
+ chan->ops->ready(chan);
return;
}
@@ -1242,8 +1219,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
- l2cap_chan_ready(chan);
-
+ if (chan->ops->ready)
+ chan->ops->ready(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
struct sock *sk = chan->sk;
__clear_chan_timer(chan);
@@ -3639,7 +3616,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (err < 0)
l2cap_send_disconn_req(chan->conn, chan, -err);
else
- l2cap_chan_ready(chan);
+ if (chan->ops->ready)
+ chan->ops->ready(chan);
goto unlock;
}
@@ -3770,7 +3748,8 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (err < 0)
l2cap_send_disconn_req(chan->conn, chan, -err);
else
- l2cap_chan_ready(chan);
+ if (chan->ops->ready)
+ chan->ops->ready(chan);
}
done:
@@ -5420,7 +5399,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (chan->scid == L2CAP_CID_LE_DATA) {
if (!status && encrypt) {
chan->sec_level = hcon->sec_level;
- l2cap_chan_ready(chan);
+ if (chan->ops->ready)
+ chan->ops->ready(chan);
}
l2cap_chan_unlock(chan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 6749d11..2e078c4 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -978,6 +978,33 @@ static void l2cap_sock_unlink_cb(struct l2cap_chan *chan, int err)
release_sock(sk);
}
+static void l2cap_sock_chan_ready_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->sk;
+ struct sock *parent;
+
+ lock_sock(sk);
+
+ parent = bt_sk(sk)->parent;
+
+ BT_DBG("sk %p, parent %p", sk, parent);
+
+ __clear_chan_timer(chan);
+
+ /* This clears all conf flags, including CONF_NOT_COMPLETE */
+ chan->conf_state = 0;
+
+ chan->state = BT_CONNECTED;
+ sk->sk_state = BT_CONNECTED;
+
+ sk->sk_state_change(sk);
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+
+ release_sock(sk);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -986,6 +1013,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.state_change = l2cap_sock_state_change_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
.unlink = l2cap_sock_unlink_cb,
+ .ready = l2cap_sock_chan_ready_cb,
};
static void l2cap_sock_destruct(struct sock *sk)
--
1.7.9.5
Hi Mat,
On Tue, May 22, 2012 at 10:45:07AM -0700, Mat Martineau wrote:
> >+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> >+{
> >+ struct l2cap_chan *chan;
> >+
> >+ chan = l2cap_chan_create();
> >+ if (!chan)
> >+ return NULL;
> >+
> >+ BT_DBG("chan %p", chan);
> >+
> >+ hci_conn_hold(conn->hcon);
>
> Holding the hcon will keep the ACL open after all of the other L2CAP
> channels have closed (unless I missed some code later in the patch
> series). The A2MP fixed channel should not keep the ACL open. If
> the connection is not held here, then there shouldn't be a put in
> l2cap_chan_del for the A2MP channel either.
l2cap_chan_del makes hci_conn_put, also for a2mp channel. The behavior is
the same for fixed and normal channels.
Best regards
Andrei Emeltchenko
Andrei -
On Fri, 18 May 2012, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Create and initialize fixed A2MP channel
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 5 ++++
> net/bluetooth/Makefile | 3 ++-
> net/bluetooth/a2mp.c | 59 +++++++++++++++++++++++++++++++++++++++++
> net/bluetooth/l2cap_core.c | 4 +--
> 4 files changed, 68 insertions(+), 3 deletions(-)
> create mode 100644 net/bluetooth/a2mp.c
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 8fdfaca..8cc739f 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -52,6 +52,8 @@
> #define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
> #define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
>
> +#define L2CAP_A2MP_DEFAULT_MTU 670
> +
> /* L2CAP socket address */
> struct sockaddr_l2 {
> sa_family_t l2_family;
> @@ -232,6 +234,7 @@ struct l2cap_conn_rsp {
> /* channel indentifier */
> #define L2CAP_CID_SIGNALING 0x0001
> #define L2CAP_CID_CONN_LESS 0x0002
> +#define L2CAP_CID_A2MP 0x0003
> #define L2CAP_CID_LE_DATA 0x0004
> #define L2CAP_CID_LE_SIGNALING 0x0005
> #define L2CAP_CID_SMP 0x0006
> @@ -931,5 +934,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
> void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
> 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);
>
> #endif /* __L2CAP_H */
> diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
> index 2dc5a57..fa6d94a 100644
> --- a/net/bluetooth/Makefile
> +++ b/net/bluetooth/Makefile
> @@ -9,4 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
> obj-$(CONFIG_BT_HIDP) += hidp/
>
> bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
> - hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o
> + hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
> + a2mp.o
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> new file mode 100644
> index 0000000..1f733b5
> --- /dev/null
> +++ b/net/bluetooth/a2mp.c
> @@ -0,0 +1,59 @@
> +/*
> + Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
> + 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 <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include <net/bluetooth/l2cap.h>
> +
> +static struct l2cap_ops a2mp_chan_ops = {
> + .name = "L2CAP A2MP channel",
> +};
> +
> +static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
> +{
> + struct l2cap_chan *chan;
> +
> + chan = l2cap_chan_create();
> + if (!chan)
> + return NULL;
> +
> + BT_DBG("chan %p", chan);
> +
> + hci_conn_hold(conn->hcon);
Holding the hcon will keep the ACL open after all of the other L2CAP
channels have closed (unless I missed some code later in the patch
series). The A2MP fixed channel should not keep the ACL open. If the
connection is not held here, then there shouldn't be a put in
l2cap_chan_del for the A2MP channel either.
> +
> + chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
> + chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
> + chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
> +
> + chan->ops = &a2mp_chan_ops;
> +
> + l2cap_chan_set_defaults(chan);
> + chan->remote_max_tx = chan->max_tx;
> + chan->remote_tx_win = chan->tx_win;
> +
> + chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
> + chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
> +
> + skb_queue_head_init(&chan->tx_q);
> +
> + chan->mode = L2CAP_MODE_ERTM;
> + l2cap_ertm_init(chan);
> +
> + l2cap_chan_add(conn, chan);
> +
> + chan->remote_mps = chan->omtu;
> + chan->mps = chan->omtu;
> +
> + return chan;
> +}
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index 65e56d5..ee333f0 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -469,7 +469,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
> list_add(&chan->list, &conn->chan_l);
> }
>
> -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)
> {
> mutex_lock(&conn->chan_lock);
> __l2cap_chan_add(conn, chan);
> @@ -2349,7 +2349,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
> l2cap_chan_put(chan);
> }
>
> -static inline int l2cap_ertm_init(struct l2cap_chan *chan)
> +int l2cap_ertm_init(struct l2cap_chan *chan)
> {
> int err;
>
> --
> 1.7.9.5
Regards,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Andrei -
On Tue, 22 May 2012, Andrei Emeltchenko wrote:
> Hi Mat,
>
> On Mon, May 21, 2012 at 03:45:38PM -0700, Mat Martineau wrote:
> ...
>>> @@ -4652,10 +4653,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
>>>
>>> chan = l2cap_get_chan_by_scid(conn, cid);
>>> if (!chan) {
>>> - BT_DBG("unknown cid 0x%4.4x", cid);
>>> - /* Drop packet and return */
>>> - kfree_skb(skb);
>>> - return 0;
>>> + if (cid == L2CAP_CID_A2MP) {
>>> + chan = a2mp_channel_create(conn, skb);
>>
>> This allocates a new AMP manager for each incoming command, doesn't
>> it? I'd expect the manager to be created once when it is first
>> needed, then remain active until the ACL is closed.
>
> No. If AMP channel exist it will be returned with l2cap_get_chan_by_scid.
> Channel exist while ACL connection exist.
Oh, ok - I see that it is added to the channel list in l2cap_chan_add.
Thanks,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
On Tue, 22 May 2012, Andrei Emeltchenko wrote:
> Hi Mat,
>
> Thanks for review,
>
> On Mon, May 21, 2012 at 03:28:57PM -0700, Mat Martineau wrote:
>>> @@ -480,7 +487,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
>>> {
>>> struct sock *sk = chan->sk;
>>> struct l2cap_conn *conn = chan->conn;
>>> - struct sock *parent = bt_sk(sk)->parent;
>>> + struct sock *parent;
>>>
>>> __clear_chan_timer(chan);
>>>
>>> @@ -496,6 +503,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
>>> hci_conn_put(conn->hcon);
>>> }
>>>
>>> + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
>>> + goto clean;
>>> +
>>
>> Since this has more to do with socketless channels than A2MP, it
>> might be better to do something more generic here like add a
>> callback to l2cap_ops so this socket code can be removed from
>> l2cap_core rather than worked around.
>
> This can be done. What would be good function name? l2cap_chan_del_cb,
> l2cap_chan_unlink ?
>
> The downside is that the code will be used only once ...
I think the long term goal is to get all socket code out of
l2cap_core.c and rework RFCOMM to directly use l2cap_chan instead of
sockets, so the callback would be used elsewhere.
>>
>>> lock_sock(sk);
>>>
>>> __l2cap_state_change(chan, BT_CLOSED);
>>> @@ -504,6 +514,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
>>> if (err)
>>> __l2cap_chan_set_err(chan, err);
>>>
>>> + parent = bt_sk(sk)->parent;
>>> if (parent) {
>>> bt_accept_unlink(sk);
>>> parent->sk_data_ready(parent, 0);
>>> @@ -515,6 +526,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
>>> if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
>>> return;
>>>
>>> +clean:
>>> skb_queue_purge(&chan->tx_q);
>>>
>>> if (chan->mode == L2CAP_MODE_ERTM) {
>>> @@ -992,6 +1004,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
>>> if (!conn)
>>> return;
>>>
>>> + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
>>> + return;
>>> +
>>
>> There are places in the ERTM state machine that call
>> l2cap_send_disconn_req() to tear down an unrecoverable ERTM
>> connection. If the connection remained up, I'm not sure how ERTM
>> will behave.
>
> I can rework this part so that in a case of fixed channel we do not send
> Disc Req and do not lock sk. So this would adds clearing timers and then
> return.
Ok.
>
>> I don't know that the spec is very clear about what to do when ERTM
>> must disconnect the channel, but the channel is fixed.
>
> I think we shall do nothing.
>
>> I think it would be best to tell the owner of the channel (A2MP in
>> this case) that the channel has been disconnected using the
>> state_change l2cap op, even if no disconnect request is sent to the
>> remote device. This way a new A2MP fixed channel can be started up
>> so there is some chance that the two sides of the A2MP ERTM
>> connection can sync up again.
>
> I can add state_change and the code would look like:
>
> 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;
>
> if (!conn)
> return;
>
> if (chan->mode == L2CAP_MODE_ERTM) {
> __clear_retrans_timer(chan);
> __clear_monitor_timer(chan);
> __clear_ack_timer(chan);
> }
>
> if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
> __l2cap_state_change(chan, BT_DISCONN);
> return;
> }
>
> 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);
>
> lock_sock(sk);
> __l2cap_state_change(chan, BT_DISCONN);
> __l2cap_chan_set_err(chan, err);
> release_sock(sk);
> }
Looks fine to me. The A2MP fixed channel is a special case in this
regard.
>
> Then function might be renamed to l2cap_disconnect_chan()
>
>>> if (chan->mode == L2CAP_MODE_ERTM) {
>>> __clear_retrans_timer(chan);
>>> __clear_monitor_timer(chan);
>>> @@ -1201,6 +1216,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
>>>
>>> l2cap_chan_lock(chan);
>>>
>>> + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
>>> + l2cap_chan_unlock(chan);
>>> + continue;
>>> + }
>>> +
>>
>> This bypasses the socket code in that loop, but also means the state
>> change is not sent to the AMP manager.
>
> Yes, but state is already BT_CONNECTED for fixed channel so it does not
> need to change.
Since the ERTM fixed channel can be created synchronously now (unlike
the older Code Aurora Forum code), I think you're right that the state
change notification isn't needed here any more.
>> Is there a way to fix up the
>> state change code in l2cap_ops so the socket code can be removed
>> from l2cap_conn_ready and other socketless L2CAP channels will also
>> work without special case code?
>
> If we would invoke sk->sk_state_change(sk) each time we call state_change
> it would be easily done. Otherwise we might need special ops for
> sk_state_change.
Right. I'm not sure why they need to be separate - there may be
some good reason, or maybe it's just hanging around from older code.
>>> if (conn->hcon->type == LE_LINK) {
>>> if (smp_conn_security(conn, chan->sec_level))
>>> l2cap_chan_ready(chan);
>>> @@ -1581,7 +1601,8 @@ static void l2cap_monitor_timeout(struct work_struct *work)
>>> l2cap_chan_lock(chan);
>>>
>>> if (chan->retry_count >= chan->remote_max_tx) {
>>> - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
>>> + if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
>>> + l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
>>
>> The spec defines the MaxTransmit value as 0xff for the A2MP channel,
>> not 0x00 (for infinite retries). There is therefore an expectation
>> that the ERTM connection will reset after too many retries - and I
>> don't think it is ok to skip the disconnect here.
>
> I will delete this chunk at all since we already check for fixed channel
> in l2cap_send_disconn_req
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Mat,
Thanks for review,
On Mon, May 21, 2012 at 03:28:57PM -0700, Mat Martineau wrote:
> >@@ -480,7 +487,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> >{
> > struct sock *sk = chan->sk;
> > struct l2cap_conn *conn = chan->conn;
> >- struct sock *parent = bt_sk(sk)->parent;
> >+ struct sock *parent;
> >
> > __clear_chan_timer(chan);
> >
> >@@ -496,6 +503,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> > hci_conn_put(conn->hcon);
> > }
> >
> >+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
> >+ goto clean;
> >+
>
> Since this has more to do with socketless channels than A2MP, it
> might be better to do something more generic here like add a
> callback to l2cap_ops so this socket code can be removed from
> l2cap_core rather than worked around.
This can be done. What would be good function name? l2cap_chan_del_cb,
l2cap_chan_unlink ?
The downside is that the code will be used only once ...
>
> > lock_sock(sk);
> >
> > __l2cap_state_change(chan, BT_CLOSED);
> >@@ -504,6 +514,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> > if (err)
> > __l2cap_chan_set_err(chan, err);
> >
> >+ parent = bt_sk(sk)->parent;
> > if (parent) {
> > bt_accept_unlink(sk);
> > parent->sk_data_ready(parent, 0);
> >@@ -515,6 +526,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> > if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
> > return;
> >
> >+clean:
> > skb_queue_purge(&chan->tx_q);
> >
> > if (chan->mode == L2CAP_MODE_ERTM) {
> >@@ -992,6 +1004,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
> > if (!conn)
> > return;
> >
> >+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
> >+ return;
> >+
>
> There are places in the ERTM state machine that call
> l2cap_send_disconn_req() to tear down an unrecoverable ERTM
> connection. If the connection remained up, I'm not sure how ERTM
> will behave.
I can rework this part so that in a case of fixed channel we do not send
Disc Req and do not lock sk. So this would adds clearing timers and then
return.
> I don't know that the spec is very clear about what to do when ERTM
> must disconnect the channel, but the channel is fixed.
I think we shall do nothing.
> I think it would be best to tell the owner of the channel (A2MP in
> this case) that the channel has been disconnected using the
> state_change l2cap op, even if no disconnect request is sent to the
> remote device. This way a new A2MP fixed channel can be started up
> so there is some chance that the two sides of the A2MP ERTM
> connection can sync up again.
I can add state_change and the code would look like:
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;
if (!conn)
return;
if (chan->mode == L2CAP_MODE_ERTM) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
}
if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
__l2cap_state_change(chan, BT_DISCONN);
return;
}
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);
lock_sock(sk);
__l2cap_state_change(chan, BT_DISCONN);
__l2cap_chan_set_err(chan, err);
release_sock(sk);
}
Then function might be renamed to l2cap_disconnect_chan()
> > if (chan->mode == L2CAP_MODE_ERTM) {
> > __clear_retrans_timer(chan);
> > __clear_monitor_timer(chan);
> >@@ -1201,6 +1216,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
> >
> > l2cap_chan_lock(chan);
> >
> >+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
> >+ l2cap_chan_unlock(chan);
> >+ continue;
> >+ }
> >+
>
> This bypasses the socket code in that loop, but also means the state
> change is not sent to the AMP manager.
Yes, but state is already BT_CONNECTED for fixed channel so it does not
need to change.
> Is there a way to fix up the
> state change code in l2cap_ops so the socket code can be removed
> from l2cap_conn_ready and other socketless L2CAP channels will also
> work without special case code?
If we would invoke sk->sk_state_change(sk) each time we call state_change
it would be easily done. Otherwise we might need special ops for
sk_state_change.
> > if (conn->hcon->type == LE_LINK) {
> > if (smp_conn_security(conn, chan->sec_level))
> > l2cap_chan_ready(chan);
> >@@ -1581,7 +1601,8 @@ static void l2cap_monitor_timeout(struct work_struct *work)
> > l2cap_chan_lock(chan);
> >
> > if (chan->retry_count >= chan->remote_max_tx) {
> >- l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
> >+ if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
> >+ l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
>
> The spec defines the MaxTransmit value as 0xff for the A2MP channel,
> not 0x00 (for infinite retries). There is therefore an expectation
> that the ERTM connection will reset after too many retries - and I
> don't think it is ok to skip the disconnect here.
I will delete this chunk at all since we already check for fixed channel
in l2cap_send_disconn_req
Best regards
Andrei Emeltchenko
Hi Mat,
On Mon, May 21, 2012 at 03:45:38PM -0700, Mat Martineau wrote:
...
> >@@ -4652,10 +4653,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
> >
> > chan = l2cap_get_chan_by_scid(conn, cid);
> > if (!chan) {
> >- BT_DBG("unknown cid 0x%4.4x", cid);
> >- /* Drop packet and return */
> >- kfree_skb(skb);
> >- return 0;
> >+ if (cid == L2CAP_CID_A2MP) {
> >+ chan = a2mp_channel_create(conn, skb);
>
> This allocates a new AMP manager for each incoming command, doesn't
> it? I'd expect the manager to be created once when it is first
> needed, then remain active until the ACL is closed.
No. If AMP channel exist it will be returned with l2cap_get_chan_by_scid.
Channel exist while ACL connection exist.
> >--
> >1.7.9.5
>
> How do you plan to create an AMP manager if you don't get an A2MP
> command from the remote device first?
I have code which is not yet sent. Something like:
+void l2cap_discover_amp(struct l2cap_conn *conn)
+{
+ struct a2mp_discov_req req;
+ struct amp_mgr *mgr;
+
+ BT_DBG("%p", conn);
+
+ mgr = amp_mgr_lookup(conn);
+ if (!mgr) {
+ mgr = amp_mgr_create(conn);
+ if (!mgr)
+ return;
+ else
+ amp_mgr_get(mgr);
+ }
+
+ req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ req.ext_feat = 0;
+ a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req);
+
+ amp_mgr_put(mgr);
+}
Best regards
Andrei Emeltchenko
On Fri, 18 May 2012, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> Handle incoming A2MP connection by creating AMP manager and
> processing A2MP messages.
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/a2mp.h | 4 ++++
> net/bluetooth/a2mp.c | 12 ++++++++++++
> net/bluetooth/l2cap_core.c | 14 ++++++++++----
> 3 files changed, 26 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
> index 980c266..b4f74c6 100644
> --- a/include/net/bluetooth/a2mp.h
> +++ b/include/net/bluetooth/a2mp.h
> @@ -15,6 +15,8 @@
> #ifndef __A2MP_H
> #define __A2MP_H
>
> +#include <net/bluetooth/l2cap.h>
> +
> #define A2MP_FEAT_EXT 0x8000
>
> struct amp_mgr {
> @@ -119,5 +121,7 @@ struct a2mp_physlink_rsp {
>
> void amp_mgr_get(struct amp_mgr *mgr);
> int amp_mgr_put(struct amp_mgr *mgr);
> +struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
> + struct sk_buff *skb);
>
> #endif /* __A2MP_H */
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index 86b2078..dda297d 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -544,3 +544,15 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
>
> return mgr;
> }
> +
> +struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
> + struct sk_buff *skb)
> +{
> + struct amp_mgr *mgr;
> +
> + mgr = amp_mgr_create(conn);
> +
> + BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
> +
> + return mgr->a2mp_chan;
> +}
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index b5c4229..8c9250e 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -56,6 +56,7 @@
> #include <net/bluetooth/hci_core.h>
> #include <net/bluetooth/l2cap.h>
> #include <net/bluetooth/smp.h>
> +#include <net/bluetooth/a2mp.h>
>
> bool disable_ertm;
>
> @@ -4652,10 +4653,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
>
> chan = l2cap_get_chan_by_scid(conn, cid);
> if (!chan) {
> - BT_DBG("unknown cid 0x%4.4x", cid);
> - /* Drop packet and return */
> - kfree_skb(skb);
> - return 0;
> + if (cid == L2CAP_CID_A2MP) {
> + chan = a2mp_channel_create(conn, skb);
This allocates a new AMP manager for each incoming command, doesn't
it? I'd expect the manager to be created once when it is first
needed, then remain active until the ACL is closed.
> + l2cap_chan_lock(chan);
> + } else {
> + BT_DBG("unknown cid 0x%4.4x", cid);
> + /* Drop packet and return */
> + kfree_skb(skb);
> + return 0;
> + }
> }
>
> BT_DBG("chan %p, len %d", chan, skb->len);
> --
> 1.7.9.5
How do you plan to create an AMP manager if you don't get an A2MP
command from the remote device first?
Regards,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Andrei -
On Fri, 18 May 2012, Andrei Emeltchenko wrote:
> From: Andrei Emeltchenko <[email protected]>
>
> A2MP fixed channel do not have sk
>
> Signed-off-by: Andrei Emeltchenko <[email protected]>
> ---
> include/net/bluetooth/l2cap.h | 1 +
> net/bluetooth/a2mp.c | 3 +--
> net/bluetooth/l2cap_core.c | 25 +++++++++++++++++++++++--
> 3 files changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index 8cc739f..db47103 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -582,6 +582,7 @@ struct l2cap_conn {
> #define L2CAP_CHAN_RAW 1
> #define L2CAP_CHAN_CONN_LESS 2
> #define L2CAP_CHAN_CONN_ORIENTED 3
> +#define L2CAP_CHAN_CONN_FIX_A2MP 4
>
> /* ----- L2CAP socket info ----- */
> #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
> diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
> index 4616da1..86b2078 100644
> --- a/net/bluetooth/a2mp.c
> +++ b/net/bluetooth/a2mp.c
> @@ -465,8 +465,7 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
>
> hci_conn_hold(conn->hcon);
>
> - chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
> - chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
> + chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
> chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
>
> chan->ops = &a2mp_chan_ops;
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index ee333f0..b5c4229 100644
> --- a/net/bluetooth/l2cap_core.c
> +++ b/net/bluetooth/l2cap_core.c
> @@ -450,6 +450,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
> chan->omtu = L2CAP_DEFAULT_MTU;
> break;
>
> + case L2CAP_CHAN_CONN_FIX_A2MP:
> + chan->scid = L2CAP_CID_A2MP;
> + chan->dcid = L2CAP_CID_A2MP;
> + chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
> + chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
> + break;
> +
> default:
> /* Raw socket can send/recv signalling messages only */
> chan->scid = L2CAP_CID_SIGNALING;
> @@ -480,7 +487,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> {
> struct sock *sk = chan->sk;
> struct l2cap_conn *conn = chan->conn;
> - struct sock *parent = bt_sk(sk)->parent;
> + struct sock *parent;
>
> __clear_chan_timer(chan);
>
> @@ -496,6 +503,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> hci_conn_put(conn->hcon);
> }
>
> + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
> + goto clean;
> +
Since this has more to do with socketless channels than A2MP, it might
be better to do something more generic here like add a callback to
l2cap_ops so this socket code can be removed from l2cap_core rather
than worked around.
> lock_sock(sk);
>
> __l2cap_state_change(chan, BT_CLOSED);
> @@ -504,6 +514,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> if (err)
> __l2cap_chan_set_err(chan, err);
>
> + parent = bt_sk(sk)->parent;
> if (parent) {
> bt_accept_unlink(sk);
> parent->sk_data_ready(parent, 0);
> @@ -515,6 +526,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
> if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
> return;
>
> +clean:
> skb_queue_purge(&chan->tx_q);
>
> if (chan->mode == L2CAP_MODE_ERTM) {
> @@ -992,6 +1004,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
> if (!conn)
> return;
>
> + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
> + return;
> +
There are places in the ERTM state machine that call
l2cap_send_disconn_req() to tear down an unrecoverable ERTM
connection. If the connection remained up, I'm not sure how ERTM will
behave.
I don't know that the spec is very clear about what to do
when ERTM must disconnect the channel, but the channel is fixed.
I think it would be best to tell the owner of the channel (A2MP in
this case) that the channel has been disconnected using the
state_change l2cap op, even if no disconnect request is sent to the
remote device. This way a new A2MP fixed channel can be started up so
there is some chance that the two sides of the A2MP ERTM connection
can sync up again.
> if (chan->mode == L2CAP_MODE_ERTM) {
> __clear_retrans_timer(chan);
> __clear_monitor_timer(chan);
> @@ -1201,6 +1216,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
>
> l2cap_chan_lock(chan);
>
> + if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
> + l2cap_chan_unlock(chan);
> + continue;
> + }
> +
This bypasses the socket code in that loop, but also means the state
change is not sent to the AMP manager. Is there a way to fix up the
state change code in l2cap_ops so the socket code can be removed from
l2cap_conn_ready and other socketless L2CAP channels will also work
without special case code?
> if (conn->hcon->type == LE_LINK) {
> if (smp_conn_security(conn, chan->sec_level))
> l2cap_chan_ready(chan);
> @@ -1581,7 +1601,8 @@ static void l2cap_monitor_timeout(struct work_struct *work)
> l2cap_chan_lock(chan);
>
> if (chan->retry_count >= chan->remote_max_tx) {
> - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
> + if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
> + l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
The spec defines the MaxTransmit value as 0xff for the A2MP channel,
not 0x00 (for infinite retries). There is therefore an expectation
that the ERTM connection will reset after too many retries - and I
don't think it is ok to skip the disconnect here.
> l2cap_chan_unlock(chan);
> l2cap_chan_put(chan);
> return;
> --
> 1.7.9.5
Regards,
--
Mat Martineau
Employee of Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
Hi Andrei,
* Andrei Emeltchenko <[email protected]> [2012-05-18 17:25:33 +0300]:
> From: Andrei Emeltchenko <[email protected]>
>
> Basic A2MP implementation.
Please rebase against latest bluetooth-next, then we should be ready to apply
this.
Gustavo
From: Andrei Emeltchenko <[email protected]>
Placeholder for future A2MP Command Reject handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 8a2dfbf..a104f7a 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,22 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Processing A2MP messages */
+static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cmd_rej *rej = (struct a2mp_cmd_rej *) skb->data;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*rej))
+ return -EINVAL;
+
+ BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason));
+
+ skb_pull(skb, sizeof(*rej));
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -89,6 +105,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
switch (hdr->code) {
case A2MP_COMMAND_REJ:
+ a2mp_command_rej(mgr, skb, hdr);
+ break;
+
case A2MP_DISCOVER_REQ:
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP Get Info Request.
Example of trace log for invalid controller id is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get Info req: id 238
< ACL data: handle 11 flags 0x00 dlen 30
A2MP: Get Info rsp: id 238 status (1) Invalid Controller ID
...
Note that If the Status field is set to Invalid Controller ID all subsequent
fields in the AMP Get Info Response shall be ignored by the receiver.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 77efb7d..e48e7f3 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -177,6 +177,40 @@ static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_info_req *req = (struct a2mp_info_req *) skb->data;
+ struct a2mp_info_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ 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)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -215,6 +249,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_GETINFO_REQ:
+ err = a2mp_getinfo_req(mgr, skb, hdr);
+ break;
+
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define A2MP command IDs and packet structures.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 73 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 2061e90..52e3a70 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -32,6 +32,79 @@ struct a2mp_cmd {
__u8 data[0];
} __packed;
+/* A2MP command codes */
+#define A2MP_COMMAND_REJ 0x01
+struct a2mp_cmd_rej {
+ __le16 reason;
+ __u8 data[0];
+} __packed;
+
+#define A2MP_DISCOVER_REQ 0x02
+struct a2mp_discov_req {
+ __le16 mtu;
+ __le16 ext_feat;
+} __packed;
+
+struct a2mp_cl {
+ __u8 id;
+ __u8 type;
+ __u8 status;
+} __packed;
+
+#define A2MP_DISCOVER_RSP 0x03
+struct a2mp_discov_rsp {
+ __le16 mtu;
+ __le16 ext_feat;
+ struct a2mp_cl cl[0];
+} __packed;
+
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+
+#define A2MP_GETINFO_REQ 0x06
+struct a2mp_info_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETINFO_RSP 0x07
+struct a2mp_info_rsp {
+ __u8 id;
+ __u8 status;
+ __le32 total_bw;
+ __le32 max_bw;
+ __le32 min_latency;
+ __le16 pal_cap;
+ __le16 assoc_size;
+} __packed;
+
+#define A2MP_GETAMPASSOC_REQ 0x08
+struct a2mp_amp_assoc_req {
+ __u8 id;
+} __packed;
+
+#define A2MP_GETAMPASSOC_RSP 0x09
+struct a2mp_amp_assoc_rsp {
+ __u8 id;
+ __u8 status;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_REQ 0x0A
+#define A2MP_DISCONNPHYSLINK_REQ 0x0C
+struct a2mp_physlink_req {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 amp_assoc[0];
+} __packed;
+
+#define A2MP_CREATEPHYSLINK_RSP 0x0B
+#define A2MP_DISCONNPHYSLINK_RSP 0x0D
+struct a2mp_physlink_rsp {
+ __u8 local_id;
+ __u8 remote_id;
+ __u8 status;
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Adds helper functions to count HCI devs and process A2MP Discover
Request, code makes sure that first controller in the list is
BREDR one. Trace is shown below:
...
> ACL data: handle 11 flags 0x02 dlen 16
A2MP: Discover req: mtu/mps 670 mask: 0x0000
< ACL data: handle 11 flags 0x00 dlen 22
A2MP: Discover rsp: mtu/mps 670 mask: 0x0000
Controller list:
id 0 type 0 (BR-EDR) status 0x01 (Bluetooth only)
id 1 type 1 (802.11 AMP) status 0x01 (Bluetooth only)
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 2 +
include/net/bluetooth/hci_core.h | 13 ++++++
net/bluetooth/a2mp.c | 85 ++++++++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 1f93628..980c266 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#define A2MP_FEAT_EXT 0x8000
+
struct amp_mgr {
struct list_head list;
struct l2cap_conn *l2cap_conn;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 3d9b07f..612b93e 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -641,6 +641,19 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data)
dev_set_drvdata(&hdev->dev, data);
}
+/* hci_dev_list shall be locked */
+static inline uint8_t __hci_num_ctrl(void)
+{
+ uint8_t count = 0;
+ struct list_head *p;
+
+ list_for_each(p, &hci_dev_list) {
+ count++;
+ }
+
+ return count;
+}
+
struct hci_dev *hci_dev_get(int index);
struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index a104f7a..ab6ef31 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,36 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
+{
+ cl->id = 0;
+ cl->type = 0;
+ cl->status = 1;
+}
+
+/* hci_dev_list shall be locked */
+static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl)
+{
+ int i = 0;
+ struct hci_dev *hdev;
+
+ __a2mp_cl_bredr(cl);
+
+ list_for_each_entry(hdev, &hci_dev_list, list) {
+ /* Iterate through AMP controllers */
+ if (hdev->id == HCI_BREDR_ID)
+ continue;
+
+ /* Starting from second entry */
+ if (++i >= num_ctrl)
+ return;
+
+ cl[i].id = hdev->id;
+ cl[i].type = hdev->amp_type;
+ cl[i].status = hdev->amp_status;
+ }
+}
+
/* Processing A2MP messages */
static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
@@ -79,6 +109,58 @@ static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_discov_req *req = (struct a2mp_discov_req *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+ struct a2mp_discov_rsp *rsp;
+ u16 ext_feat;
+ u8 num_ctrl;
+
+ if (len < sizeof(*req))
+ return -EINVAL;
+
+ skb_pull(skb, sizeof(*req));
+
+ ext_feat = le16_to_cpu(req->ext_feat);
+
+ BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->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));
+ }
+
+ read_lock(&hci_dev_list_lock);
+
+ num_ctrl = __hci_num_ctrl();
+ len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp);
+ rsp = kmalloc(len, GFP_ATOMIC);
+ if (!rsp) {
+ read_unlock(&hci_dev_list_lock);
+ return -ENOMEM;
+ }
+
+ rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ rsp->ext_feat = 0;
+
+ __a2mp_add_cl(mgr, rsp->cl, num_ctrl);
+
+ read_unlock(&hci_dev_list_lock);
+
+ a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
+
+ kfree(rsp);
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -109,6 +191,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_DISCOVER_REQ:
+ err = a2mp_discover_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_NOTIFY:
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Disconnect Physlink Request.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ca37116..69deec0 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -280,6 +280,39 @@ send_rsp:
return 0;
}
+static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+ hdev = hci_dev_get(req->local_id);
+ if (!hdev) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO Disconnect Phys Link here */
+
+ hci_dev_put(hdev);
+
+send_rsp:
+ a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -330,6 +363,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_DISCONNPHYSLINK_REQ:
+ err = a2mp_discphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
case A2MP_GETINFO_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Change Notify handler.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index ab6ef31..77efb7d 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -161,6 +161,22 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_cl *cl = (struct a2mp_cl *) skb->data;
+
+ while (skb->len >= sizeof(*cl)) {
+ BT_DBG("Controller id %d type %d status %d", cl->id, cl->type,
+ cl->status);
+ cl = (struct a2mp_cl *) skb_pull(skb, sizeof(*cl));
+ }
+
+ /* TODO send A2MP_CHANGE_RSP */
+
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -195,6 +211,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_CHANGE_NOTIFY:
+ err = a2mp_change_notify(mgr, skb, hdr);
+ break;
+
case A2MP_GETINFO_REQ:
case A2MP_GETAMPASSOC_REQ:
case A2MP_CREATEPHYSLINK_REQ:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP status codes copied from Bluez patch sent by Peter Krystad
<[email protected]>.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 52e3a70..1f93628 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -105,6 +105,16 @@ struct a2mp_physlink_rsp {
__u8 status;
} __packed;
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Example trace when receiving AMP Assoc Request with wrong AMP id.
...
> ACL data: handle 11 flags 0x02 dlen 13
A2MP: Get AMP Assoc req: id 238
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Get AMP Assoc rsp: id 238 status (1) Invalid Controller ID
assoc data:
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index e48e7f3..5498d67 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -211,6 +211,39 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_amp_assoc_req *req =
+ (struct a2mp_amp_assoc_req *) skb->data;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("id %d", req->id);
+
+ hdev = hci_dev_get(req->id);
+ if (!hdev || hdev->amp_type == HCI_BREDR) {
+ struct a2mp_amp_assoc_rsp rsp;
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+ goto clean;
+ }
+
+ /* Placeholder for HCI Read AMP Assoc */
+
+clean:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ skb_pull(skb, sizeof(*req));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -253,6 +286,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_GETAMPASSOC_REQ:
+ err = a2mp_getampassoc_req(mgr, skb, hdr);
+ break;
+
case A2MP_CREATEPHYSLINK_REQ:
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
A2MP fixed channel do not have sk
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 1 +
net/bluetooth/a2mp.c | 3 +--
net/bluetooth/l2cap_core.c | 25 +++++++++++++++++++++++--
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 8cc739f..db47103 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -582,6 +582,7 @@ struct l2cap_conn {
#define L2CAP_CHAN_RAW 1
#define L2CAP_CHAN_CONN_LESS 2
#define L2CAP_CHAN_CONN_ORIENTED 3
+#define L2CAP_CHAN_CONN_FIX_A2MP 4
/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 4616da1..86b2078 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -465,8 +465,7 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
hci_conn_hold(conn->hcon);
- chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
- chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
chan->ops = &a2mp_chan_ops;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ee333f0..b5c4229 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -450,6 +450,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->omtu = L2CAP_DEFAULT_MTU;
break;
+ case L2CAP_CHAN_CONN_FIX_A2MP:
+ chan->scid = L2CAP_CID_A2MP;
+ chan->dcid = L2CAP_CID_A2MP;
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ break;
+
default:
/* Raw socket can send/recv signalling messages only */
chan->scid = L2CAP_CID_SIGNALING;
@@ -480,7 +487,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
struct l2cap_conn *conn = chan->conn;
- struct sock *parent = bt_sk(sk)->parent;
+ struct sock *parent;
__clear_chan_timer(chan);
@@ -496,6 +503,9 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
hci_conn_put(conn->hcon);
}
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
+ goto clean;
+
lock_sock(sk);
__l2cap_state_change(chan, BT_CLOSED);
@@ -504,6 +514,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (err)
__l2cap_chan_set_err(chan, err);
+ parent = bt_sk(sk)->parent;
if (parent) {
bt_accept_unlink(sk);
parent->sk_data_ready(parent, 0);
@@ -515,6 +526,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
return;
+clean:
skb_queue_purge(&chan->tx_q);
if (chan->mode == L2CAP_MODE_ERTM) {
@@ -992,6 +1004,9 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
if (!conn)
return;
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
+ return;
+
if (chan->mode == L2CAP_MODE_ERTM) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
@@ -1201,6 +1216,11 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
l2cap_chan_lock(chan);
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+ l2cap_chan_unlock(chan);
+ continue;
+ }
+
if (conn->hcon->type == LE_LINK) {
if (smp_conn_security(conn, chan->sec_level))
l2cap_chan_ready(chan);
@@ -1581,7 +1601,8 @@ static void l2cap_monitor_timeout(struct work_struct *work)
l2cap_chan_lock(chan);
if (chan->retry_count >= chan->remote_max_tx) {
- l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
+ if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
+ l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
l2cap_chan_unlock(chan);
l2cap_chan_put(chan);
return;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Placeholder for A2MP Create Physlink Request.
Handles requests with invalid controler id as shown below:
...
> ACL data: handle 11 flags 0x02 dlen 50
A2MP: Create Physical Link req: local id 1 remote id 85
Assoc data:
<skipped>
< ACL data: handle 11 flags 0x00 dlen 15
A2MP: Create Physical Link rsp: local id 85 remote id 1 status 1
Invalid Controller ID
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 39 +++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 5498d67..ca37116 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -244,6 +244,42 @@ clean:
return 0;
}
+static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_physlink_req *req = (struct a2mp_physlink_req *) skb->data;
+ struct a2mp_physlink_rsp rsp;
+ struct hci_dev *hdev;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*req))
+ return -EINVAL;
+
+ BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
+
+ rsp.local_id = req->remote_id;
+ rsp.remote_id = req->local_id;
+
+ hdev = hci_dev_get(req->remote_id);
+ if (!hdev || hdev->amp_type != HCI_AMP) {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ goto send_rsp;
+ }
+
+ /* TODO process physlink create */
+
+ rsp.status = A2MP_STATUS_SUCCESS;
+
+send_rsp:
+ if (hdev)
+ hci_dev_put(hdev);
+
+ a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -290,6 +326,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
break;
case A2MP_CREATEPHYSLINK_REQ:
+ err = a2mp_createphyslink_req(mgr, skb, hdr);
+ break;
+
case A2MP_DISCONNPHYSLINK_REQ:
case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Process A2MP responses, print cmd code and ident for now.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 69deec0..4616da1 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -313,6 +313,15 @@ send_rsp:
return 0;
}
+static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ BT_DBG("ident %d code %d", hdr->ident, hdr->code);
+
+ skb_pull(skb, le16_to_cpu(hdr->len));
+ return 0;
+}
+
/* Handle A2MP signalling */
static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
{
@@ -372,6 +381,9 @@ static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
case A2MP_GETAMPASSOC_RSP:
case A2MP_CREATEPHYSLINK_RSP:
case A2MP_DISCONNPHYSLINK_RSP:
+ err = a2mp_cmd_rsp(mgr, skb, hdr);
+ break;
+
default:
BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
err = -EINVAL;
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Implement basic processing for AMP Manager Protocol (A2MP).
Example below shows processing unrecognized command.
...
> ACL data: handle 11 flags 0x02 dlen 12
A2MP: code 0x00 ident 3 len 0
< ACL data: handle 11 flags 0x00 dlen 14
A2MP: Command Reject: reason (0) - Command not recognized
...
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 67a7ad4..8a2dfbf 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,6 +63,70 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+/* Handle A2MP signalling */
+static int a2mp_chan_recv_cb(void *data, struct sk_buff *skb)
+{
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ struct amp_mgr *mgr = data;
+ int err = 0;
+
+ amp_mgr_get(mgr);
+
+ while (skb->len >= sizeof(*hdr)) {
+ struct a2mp_cmd *hdr = (struct a2mp_cmd *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+
+ BT_DBG("code 0x%02x id %d len %d", hdr->code, hdr->ident, len);
+
+ skb_pull(skb, sizeof(*hdr));
+
+ if (len > skb->len || !hdr->ident) {
+ err = -EINVAL;
+ break;
+ }
+
+ mgr->ident = hdr->ident;
+
+ switch (hdr->code) {
+ case A2MP_COMMAND_REJ:
+ case A2MP_DISCOVER_REQ:
+ case A2MP_CHANGE_NOTIFY:
+ case A2MP_GETINFO_REQ:
+ case A2MP_GETAMPASSOC_REQ:
+ case A2MP_CREATEPHYSLINK_REQ:
+ case A2MP_DISCONNPHYSLINK_REQ:
+ case A2MP_CHANGE_RSP:
+ case A2MP_DISCOVER_RSP:
+ case A2MP_GETINFO_RSP:
+ case A2MP_GETAMPASSOC_RSP:
+ case A2MP_CREATEPHYSLINK_RSP:
+ case A2MP_DISCONNPHYSLINK_RSP:
+ default:
+ BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ if (err) {
+ struct a2mp_cmd_rej rej;
+ rej.reason = cpu_to_le16(0);
+
+ BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
+
+ a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
+ &rej);
+ }
+
+ /* Always free skb and return success error code to prevent
+ from sending L2CAP Disconnect over A2MP channel */
+ kfree_skb(skb);
+
+ amp_mgr_put(mgr);
+
+ return 0;
+}
+
static void a2mp_chan_state_change_cb(void *data, int state)
{
struct amp_mgr *mgr = data;
@@ -100,6 +164,7 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .recv = a2mp_chan_recv_cb,
.state_change = a2mp_chan_state_change_cb,
.close = a2mp_chan_close_cb,
.alloc_skb = a2mp_chan_alloc_skb_cb,
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Handle incoming A2MP connection by creating AMP manager and
processing A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 4 ++++
net/bluetooth/a2mp.c | 12 ++++++++++++
net/bluetooth/l2cap_core.c | 14 ++++++++++----
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 980c266..b4f74c6 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -15,6 +15,8 @@
#ifndef __A2MP_H
#define __A2MP_H
+#include <net/bluetooth/l2cap.h>
+
#define A2MP_FEAT_EXT 0x8000
struct amp_mgr {
@@ -119,5 +121,7 @@ struct a2mp_physlink_rsp {
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb);
#endif /* __A2MP_H */
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 86b2078..dda297d 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -544,3 +544,15 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
return mgr;
}
+
+struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct amp_mgr *mgr;
+
+ mgr = amp_mgr_create(conn);
+
+ BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
+
+ return mgr->a2mp_chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b5c4229..8c9250e 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -56,6 +56,7 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
+#include <net/bluetooth/a2mp.h>
bool disable_ertm;
@@ -4652,10 +4653,15 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
chan = l2cap_get_chan_by_scid(conn, cid);
if (!chan) {
- BT_DBG("unknown cid 0x%4.4x", cid);
- /* Drop packet and return */
- kfree_skb(skb);
- return 0;
+ if (cid == L2CAP_CID_A2MP) {
+ chan = a2mp_channel_create(conn, skb);
+ l2cap_chan_lock(chan);
+ } else {
+ BT_DBG("unknown cid 0x%4.4x", cid);
+ /* Drop packet and return */
+ kfree_skb(skb);
+ return 0;
+ }
}
BT_DBG("chan %p, len %d", chan, skb->len);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Add state change, close and skb allocation callbacks.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
net/bluetooth/a2mp.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index cb3bf74..67a7ad4 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -63,8 +63,46 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+static void a2mp_chan_state_change_cb(void *data, int state)
+{
+ struct amp_mgr *mgr = data;
+ struct l2cap_chan *chan;
+
+ if (!mgr)
+ return;
+
+ chan = mgr->a2mp_chan;
+
+ BT_DBG("chan %p state %s", chan, state_to_string(state));
+
+ chan->state = state;
+
+ switch (state) {
+ case BT_CLOSED:
+ if (mgr)
+ amp_mgr_put(mgr);
+ break;
+ }
+}
+
+static void a2mp_chan_close_cb(void *data)
+{
+ struct amp_mgr *mgr = data;
+
+ l2cap_chan_destroy(mgr->a2mp_chan);
+}
+
+static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
+ unsigned long len, int nb)
+{
+ return bt_skb_alloc(len, GFP_KERNEL);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
+ .state_change = a2mp_chan_state_change_cb,
+ .close = a2mp_chan_close_cb,
+ .alloc_skb = a2mp_chan_alloc_skb_cb,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
@@ -102,6 +140,8 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
chan->remote_mps = chan->omtu;
chan->mps = chan->omtu;
+ chan->state = BT_CONNECTED;
+
return chan;
}
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Define AMP Manager and some basic functions.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 31 ++++++++++++++++++++++
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/a2mp.c | 54 ++++++++++++++++++++++++++++++++++++++
net/bluetooth/hci_conn.c | 15 +++++++++++
4 files changed, 101 insertions(+)
create mode 100644 include/net/bluetooth/a2mp.h
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
new file mode 100644
index 0000000..1c55514
--- /dev/null
+++ b/include/net/bluetooth/a2mp.h
@@ -0,0 +1,31 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 __A2MP_H
+#define __A2MP_H
+
+struct amp_mgr {
+ struct list_head list;
+ struct l2cap_conn *l2cap_conn;
+ struct l2cap_chan *a2mp_chan;
+ struct kref kref;
+ __u8 ident;
+ __u8 handle;
+ unsigned long flags;
+};
+
+void amp_mgr_get(struct amp_mgr *mgr);
+int amp_mgr_put(struct amp_mgr *mgr);
+
+#endif /* __A2MP_H */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 20b7d6b..3d9b07f 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -321,6 +321,7 @@ struct hci_conn {
struct sk_buff_head data_q;
struct list_head chan_list;
+ struct list_head mgr_list;
struct delayed_work disc_work;
struct timer_list idle_timer;
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 1f733b5..85cd29a 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -15,6 +15,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/a2mp.h>
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
@@ -57,3 +58,56 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
return chan;
}
+
+/* AMP Manager functions */
+void amp_mgr_get(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ kref_get(&mgr->kref);
+}
+
+static void amp_mgr_destroy(struct kref *kref)
+{
+ struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref);
+
+ BT_DBG("mgr %p", mgr);
+
+ kfree(mgr);
+}
+
+int amp_mgr_put(struct amp_mgr *mgr)
+{
+ BT_DBG("mgr %p", mgr);
+
+ return kref_put(&mgr->kref, &_mgr_destroy);
+}
+
+static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
+{
+ struct amp_mgr *mgr;
+ struct l2cap_chan *chan;
+
+ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+ if (!mgr)
+ return NULL;
+
+ BT_DBG("conn %p mgr %p", conn, mgr);
+
+ mgr->l2cap_conn = conn;
+
+ chan = a2mp_chan_open(conn);
+ if (!chan) {
+ kfree(mgr);
+ return NULL;
+ }
+
+ mgr->a2mp_chan = chan;
+ chan->data = mgr;
+
+ list_add(&mgr->list, &conn->hcon->mgr_list);
+
+ kref_init(&mgr->kref);
+
+ return mgr;
+}
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 971601e..b3aa4f1 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -42,6 +42,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/a2mp.h>
static void hci_le_connect(struct hci_conn *conn)
{
@@ -373,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
INIT_LIST_HEAD(&conn->chan_list);
+ INIT_LIST_HEAD(&conn->mgr_list);
+
INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
@@ -393,6 +396,16 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
return conn;
}
+static void hci_amp_mgr_list_flush(struct hci_conn *conn)
+{
+ struct amp_mgr *mgr, *n;
+
+ BT_DBG("conn %p", conn);
+
+ list_for_each_entry_safe(mgr, n, &conn->mgr_list, list)
+ amp_mgr_put(mgr);
+}
+
int hci_conn_del(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
@@ -428,6 +441,8 @@ int hci_conn_del(struct hci_conn *conn)
hci_chan_list_flush(conn);
+ hci_amp_mgr_list_flush(conn);
+
hci_conn_hash_del(hdev, conn);
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Helper function to build and send A2MP messages.
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/a2mp.h | 7 +++++++
net/bluetooth/a2mp.c | 46 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+)
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h
index 1c55514..2061e90 100644
--- a/include/net/bluetooth/a2mp.h
+++ b/include/net/bluetooth/a2mp.h
@@ -25,6 +25,13 @@ struct amp_mgr {
unsigned long flags;
};
+struct a2mp_cmd {
+ __u8 code;
+ __u8 ident;
+ __le16 len;
+ __u8 data[0];
+} __packed;
+
void amp_mgr_get(struct amp_mgr *mgr);
int amp_mgr_put(struct amp_mgr *mgr);
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 85cd29a..cb3bf74 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -17,6 +17,52 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>
+/* A2MP build & send command helper functions */
+static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
+{
+ struct a2mp_cmd *cmd;
+ int plen;
+
+ plen = sizeof(*cmd) + len;
+ cmd = kzalloc(plen, GFP_KERNEL);
+ if (!cmd)
+ return NULL;
+
+ cmd->code = code;
+ cmd->ident = ident;
+ cmd->len = cpu_to_le16(len);
+
+ memcpy(cmd->data, data, len);
+
+ return cmd;
+}
+
+static 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;
+ u16 total_len = len + sizeof(*cmd);
+ struct kvec iv;
+ struct msghdr msg;
+
+ cmd = __a2mp_build(code, ident, len, data);
+ if (!cmd)
+ return;
+
+ iv.iov_base = cmd;
+ iv.iov_len = total_len;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_iov = (struct iovec *) &iv;
+ msg.msg_iovlen = 1;
+
+ l2cap_chan_send(chan, &msg, total_len, 0);
+
+ kfree(cmd);
+}
+
static struct l2cap_ops a2mp_chan_ops = {
.name = "L2CAP A2MP channel",
};
--
1.7.9.5
From: Andrei Emeltchenko <[email protected]>
Create and initialize fixed A2MP channel
Signed-off-by: Andrei Emeltchenko <[email protected]>
---
include/net/bluetooth/l2cap.h | 5 ++++
net/bluetooth/Makefile | 3 ++-
net/bluetooth/a2mp.c | 59 +++++++++++++++++++++++++++++++++++++++++
net/bluetooth/l2cap_core.c | 4 +--
4 files changed, 68 insertions(+), 3 deletions(-)
create mode 100644 net/bluetooth/a2mp.c
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 8fdfaca..8cc739f 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -52,6 +52,8 @@
#define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000)
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
+#define L2CAP_A2MP_DEFAULT_MTU 670
+
/* L2CAP socket address */
struct sockaddr_l2 {
sa_family_t l2_family;
@@ -232,6 +234,7 @@ struct l2cap_conn_rsp {
/* channel indentifier */
#define L2CAP_CID_SIGNALING 0x0001
#define L2CAP_CID_CONN_LESS 0x0002
+#define L2CAP_CID_A2MP 0x0003
#define L2CAP_CID_LE_DATA 0x0004
#define L2CAP_CID_LE_SIGNALING 0x0005
#define L2CAP_CID_SMP 0x0006
@@ -931,5 +934,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
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);
#endif /* __L2CAP_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 2dc5a57..fa6d94a 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
- hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o
+ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
+ a2mp.o
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
new file mode 100644
index 0000000..1f733b5
--- /dev/null
+++ b/net/bluetooth/a2mp.c
@@ -0,0 +1,59 @@
+/*
+ Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved.
+ 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 <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+static struct l2cap_ops a2mp_chan_ops = {
+ .name = "L2CAP A2MP channel",
+};
+
+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_chan_create();
+ if (!chan)
+ return NULL;
+
+ BT_DBG("chan %p", chan);
+
+ hci_conn_hold(conn->hcon);
+
+ chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+ chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+
+ chan->ops = &a2mp_chan_ops;
+
+ l2cap_chan_set_defaults(chan);
+ chan->remote_max_tx = chan->max_tx;
+ chan->remote_tx_win = chan->tx_win;
+
+ chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ skb_queue_head_init(&chan->tx_q);
+
+ chan->mode = L2CAP_MODE_ERTM;
+ l2cap_ertm_init(chan);
+
+ l2cap_chan_add(conn, chan);
+
+ chan->remote_mps = chan->omtu;
+ chan->mps = chan->omtu;
+
+ return chan;
+}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 65e56d5..ee333f0 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -469,7 +469,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
list_add(&chan->list, &conn->chan_l);
}
-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)
{
mutex_lock(&conn->chan_lock);
__l2cap_chan_add(conn, chan);
@@ -2349,7 +2349,7 @@ static void l2cap_ack_timeout(struct work_struct *work)
l2cap_chan_put(chan);
}
-static inline int l2cap_ertm_init(struct l2cap_chan *chan)
+int l2cap_ertm_init(struct l2cap_chan *chan)
{
int err;
--
1.7.9.5