diff options
-rw-r--r-- | include/net/bluetooth/a2mp.h | 2 | ||||
-rw-r--r-- | include/net/bluetooth/amp.h | 21 | ||||
-rw-r--r-- | include/net/bluetooth/hci.h | 2 | ||||
-rw-r--r-- | include/net/bluetooth/hci_core.h | 8 | ||||
-rw-r--r-- | net/bluetooth/Makefile | 2 | ||||
-rw-r--r-- | net/bluetooth/a2mp.c | 56 | ||||
-rw-r--r-- | net/bluetooth/amp.c | 45 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 41 |
8 files changed, 171 insertions, 6 deletions
diff --git a/include/net/bluetooth/a2mp.h b/include/net/bluetooth/a2mp.h index af594685112e..d5476719fa4c 100644 --- a/include/net/bluetooth/a2mp.h +++ b/include/net/bluetooth/a2mp.h | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | enum amp_mgr_state { | 22 | enum amp_mgr_state { |
23 | READ_LOC_AMP_INFO, | 23 | READ_LOC_AMP_INFO, |
24 | READ_LOC_AMP_ASSOC, | ||
24 | }; | 25 | }; |
25 | 26 | ||
26 | struct amp_mgr { | 27 | struct amp_mgr { |
@@ -134,5 +135,6 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, | |||
134 | struct amp_mgr *amp_mgr_lookup_by_state(u8 state); | 135 | struct amp_mgr *amp_mgr_lookup_by_state(u8 state); |
135 | void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); | 136 | void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data); |
136 | void a2mp_send_getinfo_rsp(struct hci_dev *hdev); | 137 | void a2mp_send_getinfo_rsp(struct hci_dev *hdev); |
138 | void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status); | ||
137 | 139 | ||
138 | #endif /* __A2MP_H */ | 140 | #endif /* __A2MP_H */ |
diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h new file mode 100644 index 000000000000..e8616751bc81 --- /dev/null +++ b/include/net/bluetooth/amp.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | Copyright (c) 2011,2012 Intel Corp. | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License version 2 and | ||
6 | only version 2 as published by the Free Software Foundation. | ||
7 | |||
8 | This program is distributed in the hope that it will be useful, | ||
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef __AMP_H | ||
15 | #define __AMP_H | ||
16 | |||
17 | void amp_read_loc_info(struct hci_dev *hdev, struct amp_mgr *mgr); | ||
18 | void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle); | ||
19 | void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr); | ||
20 | |||
21 | #endif /* __AMP_H */ | ||
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4be26abf6dad..ccc08e57c107 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h | |||
@@ -33,6 +33,8 @@ | |||
33 | #define HCI_LINK_KEY_SIZE 16 | 33 | #define HCI_LINK_KEY_SIZE 16 |
34 | #define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE) | 34 | #define HCI_AMP_LINK_KEY_SIZE (2 * HCI_LINK_KEY_SIZE) |
35 | 35 | ||
36 | #define HCI_MAX_AMP_ASSOC_SIZE 672 | ||
37 | |||
36 | /* HCI dev events */ | 38 | /* HCI dev events */ |
37 | #define HCI_DEV_REG 1 | 39 | #define HCI_DEV_REG 1 |
38 | #define HCI_DEV_UNREG 2 | 40 | #define HCI_DEV_UNREG 2 |
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e7d454609881..b1c7d0607244 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
@@ -124,6 +124,12 @@ struct le_scan_params { | |||
124 | 124 | ||
125 | #define HCI_MAX_SHORT_NAME_LENGTH 10 | 125 | #define HCI_MAX_SHORT_NAME_LENGTH 10 |
126 | 126 | ||
127 | struct amp_assoc { | ||
128 | __u16 len; | ||
129 | __u16 offset; | ||
130 | __u8 data[HCI_MAX_AMP_ASSOC_SIZE]; | ||
131 | }; | ||
132 | |||
127 | #define NUM_REASSEMBLY 4 | 133 | #define NUM_REASSEMBLY 4 |
128 | struct hci_dev { | 134 | struct hci_dev { |
129 | struct list_head list; | 135 | struct list_head list; |
@@ -177,6 +183,8 @@ struct hci_dev { | |||
177 | __u32 amp_max_flush_to; | 183 | __u32 amp_max_flush_to; |
178 | __u32 amp_be_flush_to; | 184 | __u32 amp_be_flush_to; |
179 | 185 | ||
186 | struct amp_assoc loc_assoc; | ||
187 | |||
180 | __u8 flow_ctl_mode; | 188 | __u8 flow_ctl_mode; |
181 | 189 | ||
182 | unsigned int auto_accept_delay; | 190 | unsigned int auto_accept_delay; |
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index fa6d94a4602a..dea6a287daca 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile | |||
@@ -10,4 +10,4 @@ obj-$(CONFIG_BT_HIDP) += hidp/ | |||
10 | 10 | ||
11 | bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ | 11 | bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ |
12 | hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ | 12 | hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ |
13 | a2mp.o | 13 | a2mp.o amp.o |
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0e97b3b42ab1..71400612843d 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <net/bluetooth/hci_core.h> | 16 | #include <net/bluetooth/hci_core.h> |
17 | #include <net/bluetooth/l2cap.h> | 17 | #include <net/bluetooth/l2cap.h> |
18 | #include <net/bluetooth/a2mp.h> | 18 | #include <net/bluetooth/a2mp.h> |
19 | #include <net/bluetooth/amp.h> | ||
19 | 20 | ||
20 | /* Global AMP Manager list */ | 21 | /* Global AMP Manager list */ |
21 | LIST_HEAD(amp_mgr_list); | 22 | LIST_HEAD(amp_mgr_list); |
@@ -218,26 +219,37 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, | |||
218 | { | 219 | { |
219 | struct a2mp_amp_assoc_req *req = (void *) skb->data; | 220 | struct a2mp_amp_assoc_req *req = (void *) skb->data; |
220 | struct hci_dev *hdev; | 221 | struct hci_dev *hdev; |
222 | struct amp_mgr *tmp; | ||
221 | 223 | ||
222 | if (le16_to_cpu(hdr->len) < sizeof(*req)) | 224 | if (le16_to_cpu(hdr->len) < sizeof(*req)) |
223 | return -EINVAL; | 225 | return -EINVAL; |
224 | 226 | ||
225 | BT_DBG("id %d", req->id); | 227 | BT_DBG("id %d", req->id); |
226 | 228 | ||
229 | /* Make sure that other request is not processed */ | ||
230 | tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); | ||
231 | |||
227 | hdev = hci_dev_get(req->id); | 232 | hdev = hci_dev_get(req->id); |
228 | if (!hdev || hdev->amp_type == HCI_BREDR) { | 233 | if (!hdev || hdev->amp_type == HCI_BREDR || tmp) { |
229 | struct a2mp_amp_assoc_rsp rsp; | 234 | struct a2mp_amp_assoc_rsp rsp; |
230 | rsp.id = req->id; | 235 | rsp.id = req->id; |
231 | rsp.status = A2MP_STATUS_INVALID_CTRL_ID; | 236 | |
237 | if (tmp) { | ||
238 | rsp.status = A2MP_STATUS_COLLISION_OCCURED; | ||
239 | amp_mgr_put(tmp); | ||
240 | } else { | ||
241 | rsp.status = A2MP_STATUS_INVALID_CTRL_ID; | ||
242 | } | ||
232 | 243 | ||
233 | a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), | 244 | a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), |
234 | &rsp); | 245 | &rsp); |
235 | goto clean; | 246 | |
247 | goto done; | ||
236 | } | 248 | } |
237 | 249 | ||
238 | /* Placeholder for HCI Read AMP Assoc */ | 250 | amp_read_loc_assoc(hdev, mgr); |
239 | 251 | ||
240 | clean: | 252 | done: |
241 | if (hdev) | 253 | if (hdev) |
242 | hci_dev_put(hdev); | 254 | hci_dev_put(hdev); |
243 | 255 | ||
@@ -624,3 +636,37 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev) | |||
624 | a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); | 636 | a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); |
625 | amp_mgr_put(mgr); | 637 | amp_mgr_put(mgr); |
626 | } | 638 | } |
639 | |||
640 | void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) | ||
641 | { | ||
642 | struct amp_mgr *mgr; | ||
643 | struct amp_assoc *loc_assoc = &hdev->loc_assoc; | ||
644 | struct a2mp_amp_assoc_rsp *rsp; | ||
645 | size_t len; | ||
646 | |||
647 | mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); | ||
648 | if (!mgr) | ||
649 | return; | ||
650 | |||
651 | BT_DBG("%s mgr %p", hdev->name, mgr); | ||
652 | |||
653 | len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len; | ||
654 | rsp = kzalloc(len, GFP_KERNEL); | ||
655 | if (!rsp) { | ||
656 | amp_mgr_put(mgr); | ||
657 | return; | ||
658 | } | ||
659 | |||
660 | rsp->id = hdev->id; | ||
661 | |||
662 | if (status) { | ||
663 | rsp->status = A2MP_STATUS_INVALID_CTRL_ID; | ||
664 | } else { | ||
665 | rsp->status = A2MP_STATUS_SUCCESS; | ||
666 | memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len); | ||
667 | } | ||
668 | |||
669 | a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp); | ||
670 | amp_mgr_put(mgr); | ||
671 | kfree(rsp); | ||
672 | } | ||
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c new file mode 100644 index 000000000000..2d4e79ee06a5 --- /dev/null +++ b/net/bluetooth/amp.c | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | Copyright (c) 2011,2012 Intel Corp. | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License version 2 and | ||
6 | only version 2 as published by the Free Software Foundation. | ||
7 | |||
8 | This program is distributed in the hope that it will be useful, | ||
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <net/bluetooth/bluetooth.h> | ||
15 | #include <net/bluetooth/hci.h> | ||
16 | #include <net/bluetooth/hci_core.h> | ||
17 | #include <net/bluetooth/a2mp.h> | ||
18 | #include <net/bluetooth/amp.h> | ||
19 | |||
20 | void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle) | ||
21 | { | ||
22 | struct hci_cp_read_local_amp_assoc cp; | ||
23 | struct amp_assoc *loc_assoc = &hdev->loc_assoc; | ||
24 | |||
25 | BT_DBG("%s handle %d", hdev->name, phy_handle); | ||
26 | |||
27 | cp.phy_handle = phy_handle; | ||
28 | cp.max_len = cpu_to_le16(hdev->amp_assoc_size); | ||
29 | cp.len_so_far = cpu_to_le16(loc_assoc->offset); | ||
30 | |||
31 | hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); | ||
32 | } | ||
33 | |||
34 | void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr) | ||
35 | { | ||
36 | struct hci_cp_read_local_amp_assoc cp; | ||
37 | |||
38 | memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc)); | ||
39 | memset(&cp, 0, sizeof(cp)); | ||
40 | |||
41 | cp.max_len = cpu_to_le16(hdev->amp_assoc_size); | ||
42 | |||
43 | mgr->state = READ_LOC_AMP_ASSOC; | ||
44 | hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp); | ||
45 | } | ||
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eb45774165f4..a4240f7a142e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <net/bluetooth/hci_core.h> | 31 | #include <net/bluetooth/hci_core.h> |
32 | #include <net/bluetooth/mgmt.h> | 32 | #include <net/bluetooth/mgmt.h> |
33 | #include <net/bluetooth/a2mp.h> | 33 | #include <net/bluetooth/a2mp.h> |
34 | #include <net/bluetooth/amp.h> | ||
34 | 35 | ||
35 | /* Handle HCI Event packets */ | 36 | /* Handle HCI Event packets */ |
36 | 37 | ||
@@ -866,6 +867,42 @@ a2mp_rsp: | |||
866 | a2mp_send_getinfo_rsp(hdev); | 867 | a2mp_send_getinfo_rsp(hdev); |
867 | } | 868 | } |
868 | 869 | ||
870 | static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev, | ||
871 | struct sk_buff *skb) | ||
872 | { | ||
873 | struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data; | ||
874 | struct amp_assoc *assoc = &hdev->loc_assoc; | ||
875 | size_t rem_len, frag_len; | ||
876 | |||
877 | BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); | ||
878 | |||
879 | if (rp->status) | ||
880 | goto a2mp_rsp; | ||
881 | |||
882 | frag_len = skb->len - sizeof(*rp); | ||
883 | rem_len = __le16_to_cpu(rp->rem_len); | ||
884 | |||
885 | if (rem_len > frag_len) { | ||
886 | BT_DBG("frag_len %d rem_len %d", frag_len, rem_len); | ||
887 | |||
888 | memcpy(assoc->data + assoc->offset, rp->frag, frag_len); | ||
889 | assoc->offset += frag_len; | ||
890 | |||
891 | /* Read other fragments */ | ||
892 | amp_read_loc_assoc_frag(hdev, rp->phy_handle); | ||
893 | |||
894 | return; | ||
895 | } | ||
896 | |||
897 | memcpy(assoc->data + assoc->offset, rp->frag, rem_len); | ||
898 | assoc->len = assoc->offset + rem_len; | ||
899 | assoc->offset = 0; | ||
900 | |||
901 | a2mp_rsp: | ||
902 | /* Send A2MP Rsp when all fragments are received */ | ||
903 | a2mp_send_getampassoc_rsp(hdev, rp->status); | ||
904 | } | ||
905 | |||
869 | static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, | 906 | static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, |
870 | struct sk_buff *skb) | 907 | struct sk_buff *skb) |
871 | { | 908 | { |
@@ -2318,6 +2355,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) | |||
2318 | hci_cc_read_local_amp_info(hdev, skb); | 2355 | hci_cc_read_local_amp_info(hdev, skb); |
2319 | break; | 2356 | break; |
2320 | 2357 | ||
2358 | case HCI_OP_READ_LOCAL_AMP_ASSOC: | ||
2359 | hci_cc_read_local_amp_assoc(hdev, skb); | ||
2360 | break; | ||
2361 | |||
2321 | case HCI_OP_DELETE_STORED_LINK_KEY: | 2362 | case HCI_OP_DELETE_STORED_LINK_KEY: |
2322 | hci_cc_delete_stored_link_key(hdev, skb); | 2363 | hci_cc_delete_stored_link_key(hdev, skb); |
2323 | break; | 2364 | break; |