diff options
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 1250 |
1 files changed, 1167 insertions, 83 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f827fd908380..f5ef7a3374c7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -22,7 +22,7 @@ | |||
22 | 22 | ||
23 | /* Bluetooth HCI Management interface */ | 23 | /* Bluetooth HCI Management interface */ |
24 | 24 | ||
25 | #include <asm/uaccess.h> | 25 | #include <linux/uaccess.h> |
26 | #include <asm/unaligned.h> | 26 | #include <asm/unaligned.h> |
27 | 27 | ||
28 | #include <net/bluetooth/bluetooth.h> | 28 | #include <net/bluetooth/bluetooth.h> |
@@ -32,6 +32,16 @@ | |||
32 | #define MGMT_VERSION 0 | 32 | #define MGMT_VERSION 0 |
33 | #define MGMT_REVISION 1 | 33 | #define MGMT_REVISION 1 |
34 | 34 | ||
35 | struct pending_cmd { | ||
36 | struct list_head list; | ||
37 | __u16 opcode; | ||
38 | int index; | ||
39 | void *cmd; | ||
40 | struct sock *sk; | ||
41 | }; | ||
42 | |||
43 | LIST_HEAD(cmd_list); | ||
44 | |||
35 | static int cmd_status(struct sock *sk, u16 cmd, u8 status) | 45 | static int cmd_status(struct sock *sk, u16 cmd, u8 status) |
36 | { | 46 | { |
37 | struct sk_buff *skb; | 47 | struct sk_buff *skb; |
@@ -59,29 +69,26 @@ static int cmd_status(struct sock *sk, u16 cmd, u8 status) | |||
59 | return 0; | 69 | return 0; |
60 | } | 70 | } |
61 | 71 | ||
62 | static int read_version(struct sock *sk) | 72 | static int cmd_complete(struct sock *sk, u16 cmd, void *rp, size_t rp_len) |
63 | { | 73 | { |
64 | struct sk_buff *skb; | 74 | struct sk_buff *skb; |
65 | struct mgmt_hdr *hdr; | 75 | struct mgmt_hdr *hdr; |
66 | struct mgmt_ev_cmd_complete *ev; | 76 | struct mgmt_ev_cmd_complete *ev; |
67 | struct mgmt_rp_read_version *rp; | ||
68 | 77 | ||
69 | BT_DBG("sock %p", sk); | 78 | BT_DBG("sock %p", sk); |
70 | 79 | ||
71 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); | 80 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC); |
72 | if (!skb) | 81 | if (!skb) |
73 | return -ENOMEM; | 82 | return -ENOMEM; |
74 | 83 | ||
75 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | 84 | hdr = (void *) skb_put(skb, sizeof(*hdr)); |
76 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
77 | hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); | ||
78 | 85 | ||
79 | ev = (void *) skb_put(skb, sizeof(*ev)); | 86 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); |
80 | put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode); | 87 | hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); |
81 | 88 | ||
82 | rp = (void *) skb_put(skb, sizeof(*rp)); | 89 | ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); |
83 | rp->version = MGMT_VERSION; | 90 | put_unaligned_le16(cmd, &ev->opcode); |
84 | put_unaligned_le16(MGMT_REVISION, &rp->revision); | 91 | memcpy(ev->data, rp, rp_len); |
85 | 92 | ||
86 | if (sock_queue_rcv_skb(sk, skb) < 0) | 93 | if (sock_queue_rcv_skb(sk, skb) < 0) |
87 | kfree_skb(skb); | 94 | kfree_skb(skb); |
@@ -89,16 +96,25 @@ static int read_version(struct sock *sk) | |||
89 | return 0; | 96 | return 0; |
90 | } | 97 | } |
91 | 98 | ||
99 | static int read_version(struct sock *sk) | ||
100 | { | ||
101 | struct mgmt_rp_read_version rp; | ||
102 | |||
103 | BT_DBG("sock %p", sk); | ||
104 | |||
105 | rp.version = MGMT_VERSION; | ||
106 | put_unaligned_le16(MGMT_REVISION, &rp.revision); | ||
107 | |||
108 | return cmd_complete(sk, MGMT_OP_READ_VERSION, &rp, sizeof(rp)); | ||
109 | } | ||
110 | |||
92 | static int read_index_list(struct sock *sk) | 111 | static int read_index_list(struct sock *sk) |
93 | { | 112 | { |
94 | struct sk_buff *skb; | ||
95 | struct mgmt_hdr *hdr; | ||
96 | struct mgmt_ev_cmd_complete *ev; | ||
97 | struct mgmt_rp_read_index_list *rp; | 113 | struct mgmt_rp_read_index_list *rp; |
98 | struct list_head *p; | 114 | struct list_head *p; |
99 | size_t body_len; | 115 | size_t rp_len; |
100 | u16 count; | 116 | u16 count; |
101 | int i; | 117 | int i, err; |
102 | 118 | ||
103 | BT_DBG("sock %p", sk); | 119 | BT_DBG("sock %p", sk); |
104 | 120 | ||
@@ -109,43 +125,43 @@ static int read_index_list(struct sock *sk) | |||
109 | count++; | 125 | count++; |
110 | } | 126 | } |
111 | 127 | ||
112 | body_len = sizeof(*ev) + sizeof(*rp) + (2 * count); | 128 | rp_len = sizeof(*rp) + (2 * count); |
113 | skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC); | 129 | rp = kmalloc(rp_len, GFP_ATOMIC); |
114 | if (!skb) | 130 | if (!rp) { |
131 | read_unlock(&hci_dev_list_lock); | ||
115 | return -ENOMEM; | 132 | return -ENOMEM; |
133 | } | ||
116 | 134 | ||
117 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
118 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
119 | hdr->len = cpu_to_le16(body_len); | ||
120 | |||
121 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
122 | put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode); | ||
123 | |||
124 | rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count)); | ||
125 | put_unaligned_le16(count, &rp->num_controllers); | 135 | put_unaligned_le16(count, &rp->num_controllers); |
126 | 136 | ||
127 | i = 0; | 137 | i = 0; |
128 | list_for_each(p, &hci_dev_list) { | 138 | list_for_each(p, &hci_dev_list) { |
129 | struct hci_dev *d = list_entry(p, struct hci_dev, list); | 139 | struct hci_dev *d = list_entry(p, struct hci_dev, list); |
140 | |||
141 | hci_del_off_timer(d); | ||
142 | |||
143 | set_bit(HCI_MGMT, &d->flags); | ||
144 | |||
145 | if (test_bit(HCI_SETUP, &d->flags)) | ||
146 | continue; | ||
147 | |||
130 | put_unaligned_le16(d->id, &rp->index[i++]); | 148 | put_unaligned_le16(d->id, &rp->index[i++]); |
131 | BT_DBG("Added hci%u", d->id); | 149 | BT_DBG("Added hci%u", d->id); |
132 | } | 150 | } |
133 | 151 | ||
134 | read_unlock(&hci_dev_list_lock); | 152 | read_unlock(&hci_dev_list_lock); |
135 | 153 | ||
136 | if (sock_queue_rcv_skb(sk, skb) < 0) | 154 | err = cmd_complete(sk, MGMT_OP_READ_INDEX_LIST, rp, rp_len); |
137 | kfree_skb(skb); | ||
138 | 155 | ||
139 | return 0; | 156 | kfree(rp); |
157 | |||
158 | return err; | ||
140 | } | 159 | } |
141 | 160 | ||
142 | static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) | 161 | static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) |
143 | { | 162 | { |
144 | struct sk_buff *skb; | 163 | struct mgmt_rp_read_info rp; |
145 | struct mgmt_hdr *hdr; | 164 | struct mgmt_cp_read_info *cp = (void *) data; |
146 | struct mgmt_ev_cmd_complete *ev; | ||
147 | struct mgmt_rp_read_info *rp; | ||
148 | struct mgmt_cp_read_info *cp; | ||
149 | struct hci_dev *hdev; | 165 | struct hci_dev *hdev; |
150 | u16 dev_id; | 166 | u16 dev_id; |
151 | 167 | ||
@@ -154,18 +170,333 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) | |||
154 | if (len != 2) | 170 | if (len != 2) |
155 | return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL); | 171 | return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL); |
156 | 172 | ||
157 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); | 173 | dev_id = get_unaligned_le16(&cp->index); |
174 | |||
175 | BT_DBG("request for hci%u", dev_id); | ||
176 | |||
177 | hdev = hci_dev_get(dev_id); | ||
178 | if (!hdev) | ||
179 | return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); | ||
180 | |||
181 | hci_del_off_timer(hdev); | ||
182 | |||
183 | hci_dev_lock_bh(hdev); | ||
184 | |||
185 | set_bit(HCI_MGMT, &hdev->flags); | ||
186 | |||
187 | put_unaligned_le16(hdev->id, &rp.index); | ||
188 | rp.type = hdev->dev_type; | ||
189 | |||
190 | rp.powered = test_bit(HCI_UP, &hdev->flags); | ||
191 | rp.connectable = test_bit(HCI_PSCAN, &hdev->flags); | ||
192 | rp.discoverable = test_bit(HCI_ISCAN, &hdev->flags); | ||
193 | rp.pairable = test_bit(HCI_PSCAN, &hdev->flags); | ||
194 | |||
195 | if (test_bit(HCI_AUTH, &hdev->flags)) | ||
196 | rp.sec_mode = 3; | ||
197 | else if (hdev->ssp_mode > 0) | ||
198 | rp.sec_mode = 4; | ||
199 | else | ||
200 | rp.sec_mode = 2; | ||
201 | |||
202 | bacpy(&rp.bdaddr, &hdev->bdaddr); | ||
203 | memcpy(rp.features, hdev->features, 8); | ||
204 | memcpy(rp.dev_class, hdev->dev_class, 3); | ||
205 | put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); | ||
206 | rp.hci_ver = hdev->hci_ver; | ||
207 | put_unaligned_le16(hdev->hci_rev, &rp.hci_rev); | ||
208 | |||
209 | hci_dev_unlock_bh(hdev); | ||
210 | hci_dev_put(hdev); | ||
211 | |||
212 | return cmd_complete(sk, MGMT_OP_READ_INFO, &rp, sizeof(rp)); | ||
213 | } | ||
214 | |||
215 | static void mgmt_pending_free(struct pending_cmd *cmd) | ||
216 | { | ||
217 | sock_put(cmd->sk); | ||
218 | kfree(cmd->cmd); | ||
219 | kfree(cmd); | ||
220 | } | ||
221 | |||
222 | static int mgmt_pending_add(struct sock *sk, u16 opcode, int index, | ||
223 | void *data, u16 len) | ||
224 | { | ||
225 | struct pending_cmd *cmd; | ||
226 | |||
227 | cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); | ||
228 | if (!cmd) | ||
229 | return -ENOMEM; | ||
230 | |||
231 | cmd->opcode = opcode; | ||
232 | cmd->index = index; | ||
233 | |||
234 | cmd->cmd = kmalloc(len, GFP_ATOMIC); | ||
235 | if (!cmd->cmd) { | ||
236 | kfree(cmd); | ||
237 | return -ENOMEM; | ||
238 | } | ||
239 | |||
240 | memcpy(cmd->cmd, data, len); | ||
241 | |||
242 | cmd->sk = sk; | ||
243 | sock_hold(sk); | ||
244 | |||
245 | list_add(&cmd->list, &cmd_list); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static void mgmt_pending_foreach(u16 opcode, int index, | ||
251 | void (*cb)(struct pending_cmd *cmd, void *data), | ||
252 | void *data) | ||
253 | { | ||
254 | struct list_head *p, *n; | ||
255 | |||
256 | list_for_each_safe(p, n, &cmd_list) { | ||
257 | struct pending_cmd *cmd; | ||
258 | |||
259 | cmd = list_entry(p, struct pending_cmd, list); | ||
260 | |||
261 | if (cmd->opcode != opcode) | ||
262 | continue; | ||
263 | |||
264 | if (index >= 0 && cmd->index != index) | ||
265 | continue; | ||
266 | |||
267 | cb(cmd, data); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) | ||
272 | { | ||
273 | struct list_head *p; | ||
274 | |||
275 | list_for_each(p, &cmd_list) { | ||
276 | struct pending_cmd *cmd; | ||
277 | |||
278 | cmd = list_entry(p, struct pending_cmd, list); | ||
279 | |||
280 | if (cmd->opcode != opcode) | ||
281 | continue; | ||
282 | |||
283 | if (index >= 0 && cmd->index != index) | ||
284 | continue; | ||
285 | |||
286 | return cmd; | ||
287 | } | ||
288 | |||
289 | return NULL; | ||
290 | } | ||
291 | |||
292 | static void mgmt_pending_remove(u16 opcode, int index) | ||
293 | { | ||
294 | struct pending_cmd *cmd; | ||
295 | |||
296 | cmd = mgmt_pending_find(opcode, index); | ||
297 | if (cmd == NULL) | ||
298 | return; | ||
299 | |||
300 | list_del(&cmd->list); | ||
301 | mgmt_pending_free(cmd); | ||
302 | } | ||
303 | |||
304 | static int set_powered(struct sock *sk, unsigned char *data, u16 len) | ||
305 | { | ||
306 | struct mgmt_mode *cp; | ||
307 | struct hci_dev *hdev; | ||
308 | u16 dev_id; | ||
309 | int ret, up; | ||
310 | |||
311 | cp = (void *) data; | ||
312 | dev_id = get_unaligned_le16(&cp->index); | ||
313 | |||
314 | BT_DBG("request for hci%u", dev_id); | ||
315 | |||
316 | hdev = hci_dev_get(dev_id); | ||
317 | if (!hdev) | ||
318 | return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV); | ||
319 | |||
320 | hci_dev_lock_bh(hdev); | ||
321 | |||
322 | up = test_bit(HCI_UP, &hdev->flags); | ||
323 | if ((cp->val && up) || (!cp->val && !up)) { | ||
324 | ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY); | ||
325 | goto failed; | ||
326 | } | ||
327 | |||
328 | if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) { | ||
329 | ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY); | ||
330 | goto failed; | ||
331 | } | ||
332 | |||
333 | ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len); | ||
334 | if (ret < 0) | ||
335 | goto failed; | ||
336 | |||
337 | if (cp->val) | ||
338 | queue_work(hdev->workqueue, &hdev->power_on); | ||
339 | else | ||
340 | queue_work(hdev->workqueue, &hdev->power_off); | ||
341 | |||
342 | ret = 0; | ||
343 | |||
344 | failed: | ||
345 | hci_dev_unlock_bh(hdev); | ||
346 | hci_dev_put(hdev); | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) | ||
351 | { | ||
352 | struct mgmt_mode *cp; | ||
353 | struct hci_dev *hdev; | ||
354 | u16 dev_id; | ||
355 | u8 scan; | ||
356 | int err; | ||
357 | |||
358 | cp = (void *) data; | ||
359 | dev_id = get_unaligned_le16(&cp->index); | ||
360 | |||
361 | BT_DBG("request for hci%u", dev_id); | ||
362 | |||
363 | hdev = hci_dev_get(dev_id); | ||
364 | if (!hdev) | ||
365 | return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV); | ||
366 | |||
367 | hci_dev_lock_bh(hdev); | ||
368 | |||
369 | if (!test_bit(HCI_UP, &hdev->flags)) { | ||
370 | err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); | ||
371 | goto failed; | ||
372 | } | ||
373 | |||
374 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || | ||
375 | mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { | ||
376 | err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); | ||
377 | goto failed; | ||
378 | } | ||
379 | |||
380 | if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && | ||
381 | test_bit(HCI_PSCAN, &hdev->flags)) { | ||
382 | err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY); | ||
383 | goto failed; | ||
384 | } | ||
385 | |||
386 | err = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len); | ||
387 | if (err < 0) | ||
388 | goto failed; | ||
389 | |||
390 | scan = SCAN_PAGE; | ||
391 | |||
392 | if (cp->val) | ||
393 | scan |= SCAN_INQUIRY; | ||
394 | |||
395 | err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); | ||
396 | if (err < 0) | ||
397 | mgmt_pending_remove(MGMT_OP_SET_DISCOVERABLE, dev_id); | ||
398 | |||
399 | failed: | ||
400 | hci_dev_unlock_bh(hdev); | ||
401 | hci_dev_put(hdev); | ||
402 | |||
403 | return err; | ||
404 | } | ||
405 | |||
406 | static int set_connectable(struct sock *sk, unsigned char *data, u16 len) | ||
407 | { | ||
408 | struct mgmt_mode *cp; | ||
409 | struct hci_dev *hdev; | ||
410 | u16 dev_id; | ||
411 | u8 scan; | ||
412 | int err; | ||
413 | |||
414 | cp = (void *) data; | ||
415 | dev_id = get_unaligned_le16(&cp->index); | ||
416 | |||
417 | BT_DBG("request for hci%u", dev_id); | ||
418 | |||
419 | hdev = hci_dev_get(dev_id); | ||
420 | if (!hdev) | ||
421 | return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV); | ||
422 | |||
423 | hci_dev_lock_bh(hdev); | ||
424 | |||
425 | if (!test_bit(HCI_UP, &hdev->flags)) { | ||
426 | err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN); | ||
427 | goto failed; | ||
428 | } | ||
429 | |||
430 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || | ||
431 | mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { | ||
432 | err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY); | ||
433 | goto failed; | ||
434 | } | ||
435 | |||
436 | if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { | ||
437 | err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); | ||
438 | goto failed; | ||
439 | } | ||
440 | |||
441 | err = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len); | ||
442 | if (err < 0) | ||
443 | goto failed; | ||
444 | |||
445 | if (cp->val) | ||
446 | scan = SCAN_PAGE; | ||
447 | else | ||
448 | scan = 0; | ||
449 | |||
450 | err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); | ||
451 | if (err < 0) | ||
452 | mgmt_pending_remove(MGMT_OP_SET_CONNECTABLE, dev_id); | ||
453 | |||
454 | failed: | ||
455 | hci_dev_unlock_bh(hdev); | ||
456 | hci_dev_put(hdev); | ||
457 | |||
458 | return err; | ||
459 | } | ||
460 | |||
461 | static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) | ||
462 | { | ||
463 | struct sk_buff *skb; | ||
464 | struct mgmt_hdr *hdr; | ||
465 | |||
466 | skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); | ||
158 | if (!skb) | 467 | if (!skb) |
159 | return -ENOMEM; | 468 | return -ENOMEM; |
160 | 469 | ||
470 | bt_cb(skb)->channel = HCI_CHANNEL_CONTROL; | ||
471 | |||
161 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | 472 | hdr = (void *) skb_put(skb, sizeof(*hdr)); |
162 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | 473 | hdr->opcode = cpu_to_le16(event); |
163 | hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); | 474 | hdr->len = cpu_to_le16(data_len); |
164 | 475 | ||
165 | ev = (void *) skb_put(skb, sizeof(*ev)); | 476 | memcpy(skb_put(skb, data_len), data, data_len); |
166 | put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode); | ||
167 | 477 | ||
168 | rp = (void *) skb_put(skb, sizeof(*rp)); | 478 | hci_send_to_sock(NULL, skb, skip_sk); |
479 | kfree_skb(skb); | ||
480 | |||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) | ||
485 | { | ||
486 | struct mgmt_mode rp; | ||
487 | |||
488 | put_unaligned_le16(index, &rp.index); | ||
489 | rp.val = val; | ||
490 | |||
491 | return cmd_complete(sk, opcode, &rp, sizeof(rp)); | ||
492 | } | ||
493 | |||
494 | static int set_pairable(struct sock *sk, unsigned char *data, u16 len) | ||
495 | { | ||
496 | struct mgmt_mode *cp, ev; | ||
497 | struct hci_dev *hdev; | ||
498 | u16 dev_id; | ||
499 | int err; | ||
169 | 500 | ||
170 | cp = (void *) data; | 501 | cp = (void *) data; |
171 | dev_id = get_unaligned_le16(&cp->index); | 502 | dev_id = get_unaligned_le16(&cp->index); |
@@ -173,43 +504,547 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) | |||
173 | BT_DBG("request for hci%u", dev_id); | 504 | BT_DBG("request for hci%u", dev_id); |
174 | 505 | ||
175 | hdev = hci_dev_get(dev_id); | 506 | hdev = hci_dev_get(dev_id); |
176 | if (!hdev) { | 507 | if (!hdev) |
177 | kfree_skb(skb); | 508 | return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV); |
178 | return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); | 509 | |
510 | hci_dev_lock_bh(hdev); | ||
511 | |||
512 | if (cp->val) | ||
513 | set_bit(HCI_PAIRABLE, &hdev->flags); | ||
514 | else | ||
515 | clear_bit(HCI_PAIRABLE, &hdev->flags); | ||
516 | |||
517 | err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val); | ||
518 | if (err < 0) | ||
519 | goto failed; | ||
520 | |||
521 | put_unaligned_le16(dev_id, &ev.index); | ||
522 | ev.val = cp->val; | ||
523 | |||
524 | err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk); | ||
525 | |||
526 | failed: | ||
527 | hci_dev_unlock_bh(hdev); | ||
528 | hci_dev_put(hdev); | ||
529 | |||
530 | return err; | ||
531 | } | ||
532 | |||
533 | static u8 get_service_classes(struct hci_dev *hdev) | ||
534 | { | ||
535 | struct list_head *p; | ||
536 | u8 val = 0; | ||
537 | |||
538 | list_for_each(p, &hdev->uuids) { | ||
539 | struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); | ||
540 | |||
541 | val |= uuid->svc_hint; | ||
542 | } | ||
543 | |||
544 | return val; | ||
545 | } | ||
546 | |||
547 | static int update_class(struct hci_dev *hdev) | ||
548 | { | ||
549 | u8 cod[3]; | ||
550 | |||
551 | BT_DBG("%s", hdev->name); | ||
552 | |||
553 | if (test_bit(HCI_SERVICE_CACHE, &hdev->flags)) | ||
554 | return 0; | ||
555 | |||
556 | cod[0] = hdev->minor_class; | ||
557 | cod[1] = hdev->major_class; | ||
558 | cod[2] = get_service_classes(hdev); | ||
559 | |||
560 | if (memcmp(cod, hdev->dev_class, 3) == 0) | ||
561 | return 0; | ||
562 | |||
563 | return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); | ||
564 | } | ||
565 | |||
566 | static int add_uuid(struct sock *sk, unsigned char *data, u16 len) | ||
567 | { | ||
568 | struct mgmt_cp_add_uuid *cp; | ||
569 | struct hci_dev *hdev; | ||
570 | struct bt_uuid *uuid; | ||
571 | u16 dev_id; | ||
572 | int err; | ||
573 | |||
574 | cp = (void *) data; | ||
575 | dev_id = get_unaligned_le16(&cp->index); | ||
576 | |||
577 | BT_DBG("request for hci%u", dev_id); | ||
578 | |||
579 | hdev = hci_dev_get(dev_id); | ||
580 | if (!hdev) | ||
581 | return cmd_status(sk, MGMT_OP_ADD_UUID, ENODEV); | ||
582 | |||
583 | hci_dev_lock_bh(hdev); | ||
584 | |||
585 | uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); | ||
586 | if (!uuid) { | ||
587 | err = -ENOMEM; | ||
588 | goto failed; | ||
179 | } | 589 | } |
180 | 590 | ||
591 | memcpy(uuid->uuid, cp->uuid, 16); | ||
592 | uuid->svc_hint = cp->svc_hint; | ||
593 | |||
594 | list_add(&uuid->list, &hdev->uuids); | ||
595 | |||
596 | err = update_class(hdev); | ||
597 | if (err < 0) | ||
598 | goto failed; | ||
599 | |||
600 | err = cmd_complete(sk, MGMT_OP_ADD_UUID, &dev_id, sizeof(dev_id)); | ||
601 | |||
602 | failed: | ||
603 | hci_dev_unlock_bh(hdev); | ||
604 | hci_dev_put(hdev); | ||
605 | |||
606 | return err; | ||
607 | } | ||
608 | |||
609 | static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) | ||
610 | { | ||
611 | struct list_head *p, *n; | ||
612 | struct mgmt_cp_add_uuid *cp; | ||
613 | struct hci_dev *hdev; | ||
614 | u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
615 | u16 dev_id; | ||
616 | int err, found; | ||
617 | |||
618 | cp = (void *) data; | ||
619 | dev_id = get_unaligned_le16(&cp->index); | ||
620 | |||
621 | BT_DBG("request for hci%u", dev_id); | ||
622 | |||
623 | hdev = hci_dev_get(dev_id); | ||
624 | if (!hdev) | ||
625 | return cmd_status(sk, MGMT_OP_REMOVE_UUID, ENODEV); | ||
626 | |||
181 | hci_dev_lock_bh(hdev); | 627 | hci_dev_lock_bh(hdev); |
182 | 628 | ||
183 | put_unaligned_le16(hdev->id, &rp->index); | 629 | if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { |
184 | rp->type = hdev->dev_type; | 630 | err = hci_uuids_clear(hdev); |
631 | goto unlock; | ||
632 | } | ||
185 | 633 | ||
186 | rp->powered = test_bit(HCI_UP, &hdev->flags); | 634 | found = 0; |
187 | rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); | ||
188 | rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); | ||
189 | 635 | ||
190 | if (test_bit(HCI_AUTH, &hdev->flags)) | 636 | list_for_each_safe(p, n, &hdev->uuids) { |
191 | rp->sec_mode = 3; | 637 | struct bt_uuid *match = list_entry(p, struct bt_uuid, list); |
192 | else if (hdev->ssp_mode > 0) | ||
193 | rp->sec_mode = 4; | ||
194 | else | ||
195 | rp->sec_mode = 2; | ||
196 | 638 | ||
197 | bacpy(&rp->bdaddr, &hdev->bdaddr); | 639 | if (memcmp(match->uuid, cp->uuid, 16) != 0) |
198 | memcpy(rp->features, hdev->features, 8); | 640 | continue; |
199 | memcpy(rp->dev_class, hdev->dev_class, 3); | ||
200 | put_unaligned_le16(hdev->manufacturer, &rp->manufacturer); | ||
201 | rp->hci_ver = hdev->hci_ver; | ||
202 | put_unaligned_le16(hdev->hci_rev, &rp->hci_rev); | ||
203 | 641 | ||
642 | list_del(&match->list); | ||
643 | found++; | ||
644 | } | ||
645 | |||
646 | if (found == 0) { | ||
647 | err = cmd_status(sk, MGMT_OP_REMOVE_UUID, ENOENT); | ||
648 | goto unlock; | ||
649 | } | ||
650 | |||
651 | err = update_class(hdev); | ||
652 | if (err < 0) | ||
653 | goto unlock; | ||
654 | |||
655 | err = cmd_complete(sk, MGMT_OP_REMOVE_UUID, &dev_id, sizeof(dev_id)); | ||
656 | |||
657 | unlock: | ||
204 | hci_dev_unlock_bh(hdev); | 658 | hci_dev_unlock_bh(hdev); |
205 | hci_dev_put(hdev); | 659 | hci_dev_put(hdev); |
206 | 660 | ||
207 | if (sock_queue_rcv_skb(sk, skb) < 0) | 661 | return err; |
208 | kfree_skb(skb); | 662 | } |
663 | |||
664 | static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) | ||
665 | { | ||
666 | struct hci_dev *hdev; | ||
667 | struct mgmt_cp_set_dev_class *cp; | ||
668 | u16 dev_id; | ||
669 | int err; | ||
670 | |||
671 | cp = (void *) data; | ||
672 | dev_id = get_unaligned_le16(&cp->index); | ||
673 | |||
674 | BT_DBG("request for hci%u", dev_id); | ||
675 | |||
676 | hdev = hci_dev_get(dev_id); | ||
677 | if (!hdev) | ||
678 | return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV); | ||
679 | |||
680 | hci_dev_lock_bh(hdev); | ||
681 | |||
682 | hdev->major_class = cp->major; | ||
683 | hdev->minor_class = cp->minor; | ||
684 | |||
685 | err = update_class(hdev); | ||
686 | |||
687 | if (err == 0) | ||
688 | err = cmd_complete(sk, MGMT_OP_SET_DEV_CLASS, &dev_id, | ||
689 | sizeof(dev_id)); | ||
690 | |||
691 | hci_dev_unlock_bh(hdev); | ||
692 | hci_dev_put(hdev); | ||
693 | |||
694 | return err; | ||
695 | } | ||
696 | |||
697 | static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) | ||
698 | { | ||
699 | struct hci_dev *hdev; | ||
700 | struct mgmt_cp_set_service_cache *cp; | ||
701 | u16 dev_id; | ||
702 | int err; | ||
703 | |||
704 | cp = (void *) data; | ||
705 | dev_id = get_unaligned_le16(&cp->index); | ||
706 | |||
707 | hdev = hci_dev_get(dev_id); | ||
708 | if (!hdev) | ||
709 | return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV); | ||
710 | |||
711 | hci_dev_lock_bh(hdev); | ||
712 | |||
713 | BT_DBG("hci%u enable %d", dev_id, cp->enable); | ||
714 | |||
715 | if (cp->enable) { | ||
716 | set_bit(HCI_SERVICE_CACHE, &hdev->flags); | ||
717 | err = 0; | ||
718 | } else { | ||
719 | clear_bit(HCI_SERVICE_CACHE, &hdev->flags); | ||
720 | err = update_class(hdev); | ||
721 | } | ||
722 | |||
723 | if (err == 0) | ||
724 | err = cmd_complete(sk, MGMT_OP_SET_SERVICE_CACHE, &dev_id, | ||
725 | sizeof(dev_id)); | ||
726 | |||
727 | hci_dev_unlock_bh(hdev); | ||
728 | hci_dev_put(hdev); | ||
729 | |||
730 | return err; | ||
731 | } | ||
732 | |||
733 | static int load_keys(struct sock *sk, unsigned char *data, u16 len) | ||
734 | { | ||
735 | struct hci_dev *hdev; | ||
736 | struct mgmt_cp_load_keys *cp; | ||
737 | u16 dev_id, key_count, expected_len; | ||
738 | int i; | ||
739 | |||
740 | cp = (void *) data; | ||
741 | dev_id = get_unaligned_le16(&cp->index); | ||
742 | key_count = get_unaligned_le16(&cp->key_count); | ||
743 | |||
744 | expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); | ||
745 | if (expected_len != len) { | ||
746 | BT_ERR("load_keys: expected %u bytes, got %u bytes", | ||
747 | len, expected_len); | ||
748 | return -EINVAL; | ||
749 | } | ||
750 | |||
751 | hdev = hci_dev_get(dev_id); | ||
752 | if (!hdev) | ||
753 | return cmd_status(sk, MGMT_OP_LOAD_KEYS, ENODEV); | ||
754 | |||
755 | BT_DBG("hci%u debug_keys %u key_count %u", dev_id, cp->debug_keys, | ||
756 | key_count); | ||
757 | |||
758 | hci_dev_lock_bh(hdev); | ||
759 | |||
760 | hci_link_keys_clear(hdev); | ||
761 | |||
762 | set_bit(HCI_LINK_KEYS, &hdev->flags); | ||
763 | |||
764 | if (cp->debug_keys) | ||
765 | set_bit(HCI_DEBUG_KEYS, &hdev->flags); | ||
766 | else | ||
767 | clear_bit(HCI_DEBUG_KEYS, &hdev->flags); | ||
768 | |||
769 | for (i = 0; i < key_count; i++) { | ||
770 | struct mgmt_key_info *key = &cp->keys[i]; | ||
771 | |||
772 | hci_add_link_key(hdev, 0, &key->bdaddr, key->val, key->type, | ||
773 | key->pin_len); | ||
774 | } | ||
775 | |||
776 | hci_dev_unlock_bh(hdev); | ||
777 | hci_dev_put(hdev); | ||
209 | 778 | ||
210 | return 0; | 779 | return 0; |
211 | } | 780 | } |
212 | 781 | ||
782 | static int remove_key(struct sock *sk, unsigned char *data, u16 len) | ||
783 | { | ||
784 | struct hci_dev *hdev; | ||
785 | struct mgmt_cp_remove_key *cp; | ||
786 | struct hci_conn *conn; | ||
787 | u16 dev_id; | ||
788 | int err; | ||
789 | |||
790 | cp = (void *) data; | ||
791 | dev_id = get_unaligned_le16(&cp->index); | ||
792 | |||
793 | hdev = hci_dev_get(dev_id); | ||
794 | if (!hdev) | ||
795 | return cmd_status(sk, MGMT_OP_REMOVE_KEY, ENODEV); | ||
796 | |||
797 | hci_dev_lock_bh(hdev); | ||
798 | |||
799 | err = hci_remove_link_key(hdev, &cp->bdaddr); | ||
800 | if (err < 0) { | ||
801 | err = cmd_status(sk, MGMT_OP_REMOVE_KEY, -err); | ||
802 | goto unlock; | ||
803 | } | ||
804 | |||
805 | err = 0; | ||
806 | |||
807 | if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) | ||
808 | goto unlock; | ||
809 | |||
810 | conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); | ||
811 | if (conn) { | ||
812 | struct hci_cp_disconnect dc; | ||
813 | |||
814 | put_unaligned_le16(conn->handle, &dc.handle); | ||
815 | dc.reason = 0x13; /* Remote User Terminated Connection */ | ||
816 | err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL); | ||
817 | } | ||
818 | |||
819 | unlock: | ||
820 | hci_dev_unlock_bh(hdev); | ||
821 | hci_dev_put(hdev); | ||
822 | |||
823 | return err; | ||
824 | } | ||
825 | |||
826 | static int disconnect(struct sock *sk, unsigned char *data, u16 len) | ||
827 | { | ||
828 | struct hci_dev *hdev; | ||
829 | struct mgmt_cp_disconnect *cp; | ||
830 | struct hci_cp_disconnect dc; | ||
831 | struct hci_conn *conn; | ||
832 | u16 dev_id; | ||
833 | int err; | ||
834 | |||
835 | BT_DBG(""); | ||
836 | |||
837 | cp = (void *) data; | ||
838 | dev_id = get_unaligned_le16(&cp->index); | ||
839 | |||
840 | hdev = hci_dev_get(dev_id); | ||
841 | if (!hdev) | ||
842 | return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); | ||
843 | |||
844 | hci_dev_lock_bh(hdev); | ||
845 | |||
846 | if (!test_bit(HCI_UP, &hdev->flags)) { | ||
847 | err = cmd_status(sk, MGMT_OP_DISCONNECT, ENETDOWN); | ||
848 | goto failed; | ||
849 | } | ||
850 | |||
851 | if (mgmt_pending_find(MGMT_OP_DISCONNECT, dev_id)) { | ||
852 | err = cmd_status(sk, MGMT_OP_DISCONNECT, EBUSY); | ||
853 | goto failed; | ||
854 | } | ||
855 | |||
856 | conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); | ||
857 | if (!conn) { | ||
858 | err = cmd_status(sk, MGMT_OP_DISCONNECT, ENOTCONN); | ||
859 | goto failed; | ||
860 | } | ||
861 | |||
862 | err = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, dev_id, data, len); | ||
863 | if (err < 0) | ||
864 | goto failed; | ||
865 | |||
866 | put_unaligned_le16(conn->handle, &dc.handle); | ||
867 | dc.reason = 0x13; /* Remote User Terminated Connection */ | ||
868 | |||
869 | err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); | ||
870 | if (err < 0) | ||
871 | mgmt_pending_remove(MGMT_OP_DISCONNECT, dev_id); | ||
872 | |||
873 | failed: | ||
874 | hci_dev_unlock_bh(hdev); | ||
875 | hci_dev_put(hdev); | ||
876 | |||
877 | return err; | ||
878 | } | ||
879 | |||
880 | static int get_connections(struct sock *sk, unsigned char *data, u16 len) | ||
881 | { | ||
882 | struct mgmt_cp_get_connections *cp; | ||
883 | struct mgmt_rp_get_connections *rp; | ||
884 | struct hci_dev *hdev; | ||
885 | struct list_head *p; | ||
886 | size_t rp_len; | ||
887 | u16 dev_id, count; | ||
888 | int i, err; | ||
889 | |||
890 | BT_DBG(""); | ||
891 | |||
892 | cp = (void *) data; | ||
893 | dev_id = get_unaligned_le16(&cp->index); | ||
894 | |||
895 | hdev = hci_dev_get(dev_id); | ||
896 | if (!hdev) | ||
897 | return cmd_status(sk, MGMT_OP_GET_CONNECTIONS, ENODEV); | ||
898 | |||
899 | hci_dev_lock_bh(hdev); | ||
900 | |||
901 | count = 0; | ||
902 | list_for_each(p, &hdev->conn_hash.list) { | ||
903 | count++; | ||
904 | } | ||
905 | |||
906 | rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t)); | ||
907 | rp = kmalloc(rp_len, GFP_ATOMIC); | ||
908 | if (!rp) { | ||
909 | err = -ENOMEM; | ||
910 | goto unlock; | ||
911 | } | ||
912 | |||
913 | put_unaligned_le16(dev_id, &rp->index); | ||
914 | put_unaligned_le16(count, &rp->conn_count); | ||
915 | |||
916 | read_lock(&hci_dev_list_lock); | ||
917 | |||
918 | i = 0; | ||
919 | list_for_each(p, &hdev->conn_hash.list) { | ||
920 | struct hci_conn *c = list_entry(p, struct hci_conn, list); | ||
921 | |||
922 | bacpy(&rp->conn[i++], &c->dst); | ||
923 | } | ||
924 | |||
925 | read_unlock(&hci_dev_list_lock); | ||
926 | |||
927 | err = cmd_complete(sk, MGMT_OP_GET_CONNECTIONS, rp, rp_len); | ||
928 | |||
929 | unlock: | ||
930 | kfree(rp); | ||
931 | hci_dev_unlock_bh(hdev); | ||
932 | hci_dev_put(hdev); | ||
933 | return err; | ||
934 | } | ||
935 | |||
936 | static int pin_code_reply(struct sock *sk, unsigned char *data, u16 len) | ||
937 | { | ||
938 | struct hci_dev *hdev; | ||
939 | struct mgmt_cp_pin_code_reply *cp; | ||
940 | struct hci_cp_pin_code_reply reply; | ||
941 | u16 dev_id; | ||
942 | int err; | ||
943 | |||
944 | BT_DBG(""); | ||
945 | |||
946 | cp = (void *) data; | ||
947 | dev_id = get_unaligned_le16(&cp->index); | ||
948 | |||
949 | hdev = hci_dev_get(dev_id); | ||
950 | if (!hdev) | ||
951 | return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); | ||
952 | |||
953 | hci_dev_lock_bh(hdev); | ||
954 | |||
955 | if (!test_bit(HCI_UP, &hdev->flags)) { | ||
956 | err = cmd_status(sk, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); | ||
957 | goto failed; | ||
958 | } | ||
959 | |||
960 | err = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, dev_id, data, len); | ||
961 | if (err < 0) | ||
962 | goto failed; | ||
963 | |||
964 | bacpy(&reply.bdaddr, &cp->bdaddr); | ||
965 | reply.pin_len = cp->pin_len; | ||
966 | memcpy(reply.pin_code, cp->pin_code, 16); | ||
967 | |||
968 | err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); | ||
969 | if (err < 0) | ||
970 | mgmt_pending_remove(MGMT_OP_PIN_CODE_REPLY, dev_id); | ||
971 | |||
972 | failed: | ||
973 | hci_dev_unlock_bh(hdev); | ||
974 | hci_dev_put(hdev); | ||
975 | |||
976 | return err; | ||
977 | } | ||
978 | |||
979 | static int pin_code_neg_reply(struct sock *sk, unsigned char *data, u16 len) | ||
980 | { | ||
981 | struct hci_dev *hdev; | ||
982 | struct mgmt_cp_pin_code_neg_reply *cp; | ||
983 | u16 dev_id; | ||
984 | int err; | ||
985 | |||
986 | BT_DBG(""); | ||
987 | |||
988 | cp = (void *) data; | ||
989 | dev_id = get_unaligned_le16(&cp->index); | ||
990 | |||
991 | hdev = hci_dev_get(dev_id); | ||
992 | if (!hdev) | ||
993 | return cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENODEV); | ||
994 | |||
995 | hci_dev_lock_bh(hdev); | ||
996 | |||
997 | if (!test_bit(HCI_UP, &hdev->flags)) { | ||
998 | err = cmd_status(sk, MGMT_OP_PIN_CODE_NEG_REPLY, ENETDOWN); | ||
999 | goto failed; | ||
1000 | } | ||
1001 | |||
1002 | err = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, dev_id, | ||
1003 | data, len); | ||
1004 | if (err < 0) | ||
1005 | goto failed; | ||
1006 | |||
1007 | err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(bdaddr_t), | ||
1008 | &cp->bdaddr); | ||
1009 | if (err < 0) | ||
1010 | mgmt_pending_remove(MGMT_OP_PIN_CODE_NEG_REPLY, dev_id); | ||
1011 | |||
1012 | failed: | ||
1013 | hci_dev_unlock_bh(hdev); | ||
1014 | hci_dev_put(hdev); | ||
1015 | |||
1016 | return err; | ||
1017 | } | ||
1018 | |||
1019 | static int set_io_capability(struct sock *sk, unsigned char *data, u16 len) | ||
1020 | { | ||
1021 | struct hci_dev *hdev; | ||
1022 | struct mgmt_cp_set_io_capability *cp; | ||
1023 | u16 dev_id; | ||
1024 | |||
1025 | BT_DBG(""); | ||
1026 | |||
1027 | cp = (void *) data; | ||
1028 | dev_id = get_unaligned_le16(&cp->index); | ||
1029 | |||
1030 | hdev = hci_dev_get(dev_id); | ||
1031 | if (!hdev) | ||
1032 | return cmd_status(sk, MGMT_OP_SET_IO_CAPABILITY, ENODEV); | ||
1033 | |||
1034 | hci_dev_lock_bh(hdev); | ||
1035 | |||
1036 | hdev->io_capability = cp->io_capability; | ||
1037 | |||
1038 | BT_DBG("%s IO capability set to 0x%02x", hdev->name, | ||
1039 | hdev->io_capability); | ||
1040 | |||
1041 | hci_dev_unlock_bh(hdev); | ||
1042 | hci_dev_put(hdev); | ||
1043 | |||
1044 | return cmd_complete(sk, MGMT_OP_SET_IO_CAPABILITY, | ||
1045 | &dev_id, sizeof(dev_id)); | ||
1046 | } | ||
1047 | |||
213 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 1048 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) |
214 | { | 1049 | { |
215 | unsigned char *buf; | 1050 | unsigned char *buf; |
@@ -250,6 +1085,51 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
250 | case MGMT_OP_READ_INFO: | 1085 | case MGMT_OP_READ_INFO: |
251 | err = read_controller_info(sk, buf + sizeof(*hdr), len); | 1086 | err = read_controller_info(sk, buf + sizeof(*hdr), len); |
252 | break; | 1087 | break; |
1088 | case MGMT_OP_SET_POWERED: | ||
1089 | err = set_powered(sk, buf + sizeof(*hdr), len); | ||
1090 | break; | ||
1091 | case MGMT_OP_SET_DISCOVERABLE: | ||
1092 | err = set_discoverable(sk, buf + sizeof(*hdr), len); | ||
1093 | break; | ||
1094 | case MGMT_OP_SET_CONNECTABLE: | ||
1095 | err = set_connectable(sk, buf + sizeof(*hdr), len); | ||
1096 | break; | ||
1097 | case MGMT_OP_SET_PAIRABLE: | ||
1098 | err = set_pairable(sk, buf + sizeof(*hdr), len); | ||
1099 | break; | ||
1100 | case MGMT_OP_ADD_UUID: | ||
1101 | err = add_uuid(sk, buf + sizeof(*hdr), len); | ||
1102 | break; | ||
1103 | case MGMT_OP_REMOVE_UUID: | ||
1104 | err = remove_uuid(sk, buf + sizeof(*hdr), len); | ||
1105 | break; | ||
1106 | case MGMT_OP_SET_DEV_CLASS: | ||
1107 | err = set_dev_class(sk, buf + sizeof(*hdr), len); | ||
1108 | break; | ||
1109 | case MGMT_OP_SET_SERVICE_CACHE: | ||
1110 | err = set_service_cache(sk, buf + sizeof(*hdr), len); | ||
1111 | break; | ||
1112 | case MGMT_OP_LOAD_KEYS: | ||
1113 | err = load_keys(sk, buf + sizeof(*hdr), len); | ||
1114 | break; | ||
1115 | case MGMT_OP_REMOVE_KEY: | ||
1116 | err = remove_key(sk, buf + sizeof(*hdr), len); | ||
1117 | break; | ||
1118 | case MGMT_OP_DISCONNECT: | ||
1119 | err = disconnect(sk, buf + sizeof(*hdr), len); | ||
1120 | break; | ||
1121 | case MGMT_OP_GET_CONNECTIONS: | ||
1122 | err = get_connections(sk, buf + sizeof(*hdr), len); | ||
1123 | break; | ||
1124 | case MGMT_OP_PIN_CODE_REPLY: | ||
1125 | err = pin_code_reply(sk, buf + sizeof(*hdr), len); | ||
1126 | break; | ||
1127 | case MGMT_OP_PIN_CODE_NEG_REPLY: | ||
1128 | err = pin_code_neg_reply(sk, buf + sizeof(*hdr), len); | ||
1129 | break; | ||
1130 | case MGMT_OP_SET_IO_CAPABILITY: | ||
1131 | err = set_io_capability(sk, buf + sizeof(*hdr), len); | ||
1132 | break; | ||
253 | default: | 1133 | default: |
254 | BT_DBG("Unknown op %u", opcode); | 1134 | BT_DBG("Unknown op %u", opcode); |
255 | err = cmd_status(sk, opcode, 0x01); | 1135 | err = cmd_status(sk, opcode, 0x01); |
@@ -266,43 +1146,247 @@ done: | |||
266 | return err; | 1146 | return err; |
267 | } | 1147 | } |
268 | 1148 | ||
269 | static int mgmt_event(u16 event, void *data, u16 data_len) | 1149 | int mgmt_index_added(u16 index) |
270 | { | 1150 | { |
271 | struct sk_buff *skb; | 1151 | struct mgmt_ev_index_added ev; |
272 | struct mgmt_hdr *hdr; | ||
273 | 1152 | ||
274 | skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); | 1153 | put_unaligned_le16(index, &ev.index); |
275 | if (!skb) | ||
276 | return -ENOMEM; | ||
277 | 1154 | ||
278 | bt_cb(skb)->channel = HCI_CHANNEL_CONTROL; | 1155 | return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL); |
1156 | } | ||
279 | 1157 | ||
280 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | 1158 | int mgmt_index_removed(u16 index) |
281 | hdr->opcode = cpu_to_le16(event); | 1159 | { |
282 | hdr->len = cpu_to_le16(data_len); | 1160 | struct mgmt_ev_index_added ev; |
283 | 1161 | ||
284 | memcpy(skb_put(skb, data_len), data, data_len); | 1162 | put_unaligned_le16(index, &ev.index); |
285 | 1163 | ||
286 | hci_send_to_sock(NULL, skb); | 1164 | return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); |
287 | kfree_skb(skb); | 1165 | } |
288 | 1166 | ||
289 | return 0; | 1167 | struct cmd_lookup { |
1168 | u8 val; | ||
1169 | struct sock *sk; | ||
1170 | }; | ||
1171 | |||
1172 | static void mode_rsp(struct pending_cmd *cmd, void *data) | ||
1173 | { | ||
1174 | struct mgmt_mode *cp = cmd->cmd; | ||
1175 | struct cmd_lookup *match = data; | ||
1176 | |||
1177 | if (cp->val != match->val) | ||
1178 | return; | ||
1179 | |||
1180 | send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val); | ||
1181 | |||
1182 | list_del(&cmd->list); | ||
1183 | |||
1184 | if (match->sk == NULL) { | ||
1185 | match->sk = cmd->sk; | ||
1186 | sock_hold(match->sk); | ||
1187 | } | ||
1188 | |||
1189 | mgmt_pending_free(cmd); | ||
290 | } | 1190 | } |
291 | 1191 | ||
292 | int mgmt_index_added(u16 index) | 1192 | int mgmt_powered(u16 index, u8 powered) |
293 | { | 1193 | { |
294 | struct mgmt_ev_index_added ev; | 1194 | struct mgmt_mode ev; |
1195 | struct cmd_lookup match = { powered, NULL }; | ||
1196 | int ret; | ||
1197 | |||
1198 | mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); | ||
295 | 1199 | ||
296 | put_unaligned_le16(index, &ev.index); | 1200 | put_unaligned_le16(index, &ev.index); |
1201 | ev.val = powered; | ||
1202 | |||
1203 | ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk); | ||
297 | 1204 | ||
298 | return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev)); | 1205 | if (match.sk) |
1206 | sock_put(match.sk); | ||
1207 | |||
1208 | return ret; | ||
299 | } | 1209 | } |
300 | 1210 | ||
301 | int mgmt_index_removed(u16 index) | 1211 | int mgmt_discoverable(u16 index, u8 discoverable) |
302 | { | 1212 | { |
303 | struct mgmt_ev_index_added ev; | 1213 | struct mgmt_mode ev; |
1214 | struct cmd_lookup match = { discoverable, NULL }; | ||
1215 | int ret; | ||
1216 | |||
1217 | mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, | ||
1218 | mode_rsp, &match); | ||
1219 | |||
1220 | put_unaligned_le16(index, &ev.index); | ||
1221 | ev.val = discoverable; | ||
1222 | |||
1223 | ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk); | ||
1224 | |||
1225 | if (match.sk) | ||
1226 | sock_put(match.sk); | ||
1227 | |||
1228 | return ret; | ||
1229 | } | ||
1230 | |||
1231 | int mgmt_connectable(u16 index, u8 connectable) | ||
1232 | { | ||
1233 | struct mgmt_mode ev; | ||
1234 | struct cmd_lookup match = { connectable, NULL }; | ||
1235 | int ret; | ||
1236 | |||
1237 | mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); | ||
1238 | |||
1239 | put_unaligned_le16(index, &ev.index); | ||
1240 | ev.val = connectable; | ||
1241 | |||
1242 | ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); | ||
1243 | |||
1244 | if (match.sk) | ||
1245 | sock_put(match.sk); | ||
1246 | |||
1247 | return ret; | ||
1248 | } | ||
1249 | |||
1250 | int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) | ||
1251 | { | ||
1252 | struct mgmt_ev_new_key ev; | ||
1253 | |||
1254 | memset(&ev, 0, sizeof(ev)); | ||
1255 | |||
1256 | put_unaligned_le16(index, &ev.index); | ||
1257 | |||
1258 | bacpy(&ev.key.bdaddr, &key->bdaddr); | ||
1259 | ev.key.type = key->type; | ||
1260 | memcpy(ev.key.val, key->val, 16); | ||
1261 | ev.key.pin_len = key->pin_len; | ||
1262 | ev.old_key_type = old_key_type; | ||
1263 | |||
1264 | return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); | ||
1265 | } | ||
1266 | |||
1267 | int mgmt_connected(u16 index, bdaddr_t *bdaddr) | ||
1268 | { | ||
1269 | struct mgmt_ev_connected ev; | ||
304 | 1270 | ||
305 | put_unaligned_le16(index, &ev.index); | 1271 | put_unaligned_le16(index, &ev.index); |
1272 | bacpy(&ev.bdaddr, bdaddr); | ||
306 | 1273 | ||
307 | return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev)); | 1274 | return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL); |
1275 | } | ||
1276 | |||
1277 | static void disconnect_rsp(struct pending_cmd *cmd, void *data) | ||
1278 | { | ||
1279 | struct mgmt_cp_disconnect *cp = cmd->cmd; | ||
1280 | struct sock **sk = data; | ||
1281 | struct mgmt_rp_disconnect rp; | ||
1282 | |||
1283 | put_unaligned_le16(cmd->index, &rp.index); | ||
1284 | bacpy(&rp.bdaddr, &cp->bdaddr); | ||
1285 | |||
1286 | cmd_complete(cmd->sk, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); | ||
1287 | |||
1288 | *sk = cmd->sk; | ||
1289 | sock_hold(*sk); | ||
1290 | |||
1291 | list_del(&cmd->list); | ||
1292 | mgmt_pending_free(cmd); | ||
1293 | } | ||
1294 | |||
1295 | int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) | ||
1296 | { | ||
1297 | struct mgmt_ev_disconnected ev; | ||
1298 | struct sock *sk = NULL; | ||
1299 | int err; | ||
1300 | |||
1301 | mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); | ||
1302 | |||
1303 | put_unaligned_le16(index, &ev.index); | ||
1304 | bacpy(&ev.bdaddr, bdaddr); | ||
1305 | |||
1306 | err = mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), sk); | ||
1307 | |||
1308 | if (sk) | ||
1309 | sock_put(sk); | ||
1310 | |||
1311 | return err; | ||
1312 | } | ||
1313 | |||
1314 | int mgmt_disconnect_failed(u16 index) | ||
1315 | { | ||
1316 | struct pending_cmd *cmd; | ||
1317 | int err; | ||
1318 | |||
1319 | cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index); | ||
1320 | if (!cmd) | ||
1321 | return -ENOENT; | ||
1322 | |||
1323 | err = cmd_status(cmd->sk, MGMT_OP_DISCONNECT, EIO); | ||
1324 | |||
1325 | list_del(&cmd->list); | ||
1326 | mgmt_pending_free(cmd); | ||
1327 | |||
1328 | return err; | ||
1329 | } | ||
1330 | |||
1331 | int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) | ||
1332 | { | ||
1333 | struct mgmt_ev_connect_failed ev; | ||
1334 | |||
1335 | put_unaligned_le16(index, &ev.index); | ||
1336 | bacpy(&ev.bdaddr, bdaddr); | ||
1337 | ev.status = status; | ||
1338 | |||
1339 | return mgmt_event(MGMT_EV_CONNECT_FAILED, &ev, sizeof(ev), NULL); | ||
1340 | } | ||
1341 | |||
1342 | int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) | ||
1343 | { | ||
1344 | struct mgmt_ev_pin_code_request ev; | ||
1345 | |||
1346 | put_unaligned_le16(index, &ev.index); | ||
1347 | bacpy(&ev.bdaddr, bdaddr); | ||
1348 | |||
1349 | return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, &ev, sizeof(ev), NULL); | ||
1350 | } | ||
1351 | |||
1352 | int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) | ||
1353 | { | ||
1354 | struct pending_cmd *cmd; | ||
1355 | int err; | ||
1356 | |||
1357 | cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); | ||
1358 | if (!cmd) | ||
1359 | return -ENOENT; | ||
1360 | |||
1361 | if (status != 0) | ||
1362 | err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_REPLY, status); | ||
1363 | else | ||
1364 | err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_REPLY, | ||
1365 | bdaddr, sizeof(*bdaddr)); | ||
1366 | |||
1367 | list_del(&cmd->list); | ||
1368 | mgmt_pending_free(cmd); | ||
1369 | |||
1370 | return err; | ||
1371 | } | ||
1372 | |||
1373 | int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) | ||
1374 | { | ||
1375 | struct pending_cmd *cmd; | ||
1376 | int err; | ||
1377 | |||
1378 | cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); | ||
1379 | if (!cmd) | ||
1380 | return -ENOENT; | ||
1381 | |||
1382 | if (status != 0) | ||
1383 | err = cmd_status(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, status); | ||
1384 | else | ||
1385 | err = cmd_complete(cmd->sk, MGMT_OP_PIN_CODE_NEG_REPLY, | ||
1386 | bdaddr, sizeof(*bdaddr)); | ||
1387 | |||
1388 | list_del(&cmd->list); | ||
1389 | mgmt_pending_free(cmd); | ||
1390 | |||
1391 | return err; | ||
308 | } | 1392 | } |