blob: a08f4ce03182239990399093d2d70a48ad1edd5b [file] [log] [blame]
Johan Hedberg03811012010-12-08 00:21:06 +02001/*
2 BlueZ - Bluetooth protocol stack for Linux
3 Copyright (C) 2010 Nokia Corporation
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation;
8
9 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
13 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
19 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
20 SOFTWARE IS DISCLAIMED.
21*/
22
23/* Bluetooth HCI Management interface */
24
25#include <asm/uaccess.h>
26#include <asm/unaligned.h>
27
28#include <net/bluetooth/bluetooth.h>
29#include <net/bluetooth/hci_core.h>
30#include <net/bluetooth/mgmt.h>
31
Johan Hedberg02d98122010-12-13 21:07:04 +020032#define MGMT_VERSION 0
33#define MGMT_REVISION 1
34
Johan Hedbergeec8d2b2010-12-16 10:17:38 +020035struct pending_cmd {
36 struct list_head list;
37 __u16 opcode;
38 int index;
39 void *cmd;
40 struct sock *sk;
41};
42
43LIST_HEAD(cmd_list);
44
Johan Hedbergf7b64e692010-12-13 21:07:06 +020045static int cmd_status(struct sock *sk, u16 cmd, u8 status)
46{
47 struct sk_buff *skb;
48 struct mgmt_hdr *hdr;
49 struct mgmt_ev_cmd_status *ev;
50
51 BT_DBG("sock %p", sk);
52
53 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
54 if (!skb)
55 return -ENOMEM;
56
57 hdr = (void *) skb_put(skb, sizeof(*hdr));
58
59 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
60 hdr->len = cpu_to_le16(sizeof(*ev));
61
62 ev = (void *) skb_put(skb, sizeof(*ev));
63 ev->status = status;
64 put_unaligned_le16(cmd, &ev->opcode);
65
66 if (sock_queue_rcv_skb(sk, skb) < 0)
67 kfree_skb(skb);
68
69 return 0;
70}
71
Johan Hedberg02d98122010-12-13 21:07:04 +020072static int read_version(struct sock *sk)
73{
74 struct sk_buff *skb;
75 struct mgmt_hdr *hdr;
76 struct mgmt_ev_cmd_complete *ev;
77 struct mgmt_rp_read_version *rp;
78
79 BT_DBG("sock %p", sk);
80
81 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
82 if (!skb)
83 return -ENOMEM;
84
85 hdr = (void *) skb_put(skb, sizeof(*hdr));
86 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
87 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
88
89 ev = (void *) skb_put(skb, sizeof(*ev));
90 put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode);
91
92 rp = (void *) skb_put(skb, sizeof(*rp));
93 rp->version = MGMT_VERSION;
94 put_unaligned_le16(MGMT_REVISION, &rp->revision);
95
96 if (sock_queue_rcv_skb(sk, skb) < 0)
97 kfree_skb(skb);
98
99 return 0;
100}
101
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200102static int read_index_list(struct sock *sk)
103{
104 struct sk_buff *skb;
105 struct mgmt_hdr *hdr;
106 struct mgmt_ev_cmd_complete *ev;
107 struct mgmt_rp_read_index_list *rp;
108 struct list_head *p;
109 size_t body_len;
110 u16 count;
111 int i;
112
113 BT_DBG("sock %p", sk);
114
115 read_lock(&hci_dev_list_lock);
116
117 count = 0;
118 list_for_each(p, &hci_dev_list) {
119 count++;
120 }
121
122 body_len = sizeof(*ev) + sizeof(*rp) + (2 * count);
123 skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC);
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100124 if (!skb) {
125 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200126 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100127 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200128
129 hdr = (void *) skb_put(skb, sizeof(*hdr));
130 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
131 hdr->len = cpu_to_le16(body_len);
132
133 ev = (void *) skb_put(skb, sizeof(*ev));
134 put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode);
135
136 rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count));
137 put_unaligned_le16(count, &rp->num_controllers);
138
139 i = 0;
140 list_for_each(p, &hci_dev_list) {
141 struct hci_dev *d = list_entry(p, struct hci_dev, list);
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200142
143 hci_del_off_timer(d);
144
Johan Hedbergebc99fe2011-01-04 11:54:26 +0200145 set_bit(HCI_MGMT, &d->flags);
146
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200147 if (test_bit(HCI_SETUP, &d->flags))
148 continue;
149
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200150 put_unaligned_le16(d->id, &rp->index[i++]);
151 BT_DBG("Added hci%u", d->id);
152 }
153
154 read_unlock(&hci_dev_list_lock);
155
156 if (sock_queue_rcv_skb(sk, skb) < 0)
157 kfree_skb(skb);
158
159 return 0;
160}
161
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200162static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200163{
164 struct sk_buff *skb;
165 struct mgmt_hdr *hdr;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200166 struct mgmt_ev_cmd_complete *ev;
167 struct mgmt_rp_read_info *rp;
168 struct mgmt_cp_read_info *cp;
169 struct hci_dev *hdev;
170 u16 dev_id;
Johan Hedberg03811012010-12-08 00:21:06 +0200171
172 BT_DBG("sock %p", sk);
173
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200174 if (len != 2)
175 return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
176
177 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
Johan Hedberg03811012010-12-08 00:21:06 +0200178 if (!skb)
Johan Hedberge41d8b42010-12-13 21:07:03 +0200179 return -ENOMEM;
Johan Hedberg03811012010-12-08 00:21:06 +0200180
181 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200182 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
183 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200184
185 ev = (void *) skb_put(skb, sizeof(*ev));
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200186 put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
187
188 rp = (void *) skb_put(skb, sizeof(*rp));
189
190 cp = (void *) data;
191 dev_id = get_unaligned_le16(&cp->index);
192
193 BT_DBG("request for hci%u", dev_id);
194
195 hdev = hci_dev_get(dev_id);
196 if (!hdev) {
197 kfree_skb(skb);
198 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
199 }
200
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200201 hci_del_off_timer(hdev);
202
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200203 hci_dev_lock_bh(hdev);
204
Johan Hedbergebc99fe2011-01-04 11:54:26 +0200205 set_bit(HCI_MGMT, &hdev->flags);
206
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200207 put_unaligned_le16(hdev->id, &rp->index);
208 rp->type = hdev->dev_type;
209
210 rp->powered = test_bit(HCI_UP, &hdev->flags);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200211 rp->connectable = test_bit(HCI_PSCAN, &hdev->flags);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200212 rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags);
213 rp->pairable = test_bit(HCI_PSCAN, &hdev->flags);
214
215 if (test_bit(HCI_AUTH, &hdev->flags))
216 rp->sec_mode = 3;
217 else if (hdev->ssp_mode > 0)
218 rp->sec_mode = 4;
219 else
220 rp->sec_mode = 2;
221
222 bacpy(&rp->bdaddr, &hdev->bdaddr);
223 memcpy(rp->features, hdev->features, 8);
224 memcpy(rp->dev_class, hdev->dev_class, 3);
225 put_unaligned_le16(hdev->manufacturer, &rp->manufacturer);
226 rp->hci_ver = hdev->hci_ver;
227 put_unaligned_le16(hdev->hci_rev, &rp->hci_rev);
228
229 hci_dev_unlock_bh(hdev);
230 hci_dev_put(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200231
232 if (sock_queue_rcv_skb(sk, skb) < 0)
233 kfree_skb(skb);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200234
235 return 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200236}
237
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200238static void mgmt_pending_free(struct pending_cmd *cmd)
239{
240 sock_put(cmd->sk);
241 kfree(cmd->cmd);
242 kfree(cmd);
243}
244
245static int mgmt_pending_add(struct sock *sk, u16 opcode, int index,
246 void *data, u16 len)
247{
248 struct pending_cmd *cmd;
249
250 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
251 if (!cmd)
252 return -ENOMEM;
253
254 cmd->opcode = opcode;
255 cmd->index = index;
256
257 cmd->cmd = kmalloc(len, GFP_ATOMIC);
258 if (!cmd->cmd) {
259 kfree(cmd);
260 return -ENOMEM;
261 }
262
263 memcpy(cmd->cmd, data, len);
264
265 cmd->sk = sk;
266 sock_hold(sk);
267
268 list_add(&cmd->list, &cmd_list);
269
270 return 0;
271}
272
273static void mgmt_pending_foreach(u16 opcode, int index,
274 void (*cb)(struct pending_cmd *cmd, void *data),
275 void *data)
276{
277 struct list_head *p, *n;
278
279 list_for_each_safe(p, n, &cmd_list) {
280 struct pending_cmd *cmd;
281
282 cmd = list_entry(p, struct pending_cmd, list);
283
284 if (cmd->opcode != opcode)
285 continue;
286
287 if (index >= 0 && cmd->index != index)
288 continue;
289
290 cb(cmd, data);
291 }
292}
293
294static struct pending_cmd *mgmt_pending_find(u16 opcode, int index)
295{
296 struct list_head *p;
297
298 list_for_each(p, &cmd_list) {
299 struct pending_cmd *cmd;
300
301 cmd = list_entry(p, struct pending_cmd, list);
302
303 if (cmd->opcode != opcode)
304 continue;
305
306 if (index >= 0 && cmd->index != index)
307 continue;
308
309 return cmd;
310 }
311
312 return NULL;
313}
314
Johan Hedberg73f22f62010-12-29 16:00:25 +0200315static void mgmt_pending_remove(u16 opcode, int index)
316{
317 struct pending_cmd *cmd;
318
319 cmd = mgmt_pending_find(opcode, index);
320 if (cmd == NULL)
321 return;
322
323 list_del(&cmd->list);
324 mgmt_pending_free(cmd);
325}
326
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200327static int set_powered(struct sock *sk, unsigned char *data, u16 len)
328{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200329 struct mgmt_mode *cp;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200330 struct hci_dev *hdev;
331 u16 dev_id;
332 int ret, up;
333
334 cp = (void *) data;
335 dev_id = get_unaligned_le16(&cp->index);
336
337 BT_DBG("request for hci%u", dev_id);
338
339 hdev = hci_dev_get(dev_id);
340 if (!hdev)
341 return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV);
342
343 hci_dev_lock_bh(hdev);
344
345 up = test_bit(HCI_UP, &hdev->flags);
Johan Hedberg72a734e2010-12-30 00:38:22 +0200346 if ((cp->val && up) || (!cp->val && !up)) {
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200347 ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY);
348 goto failed;
349 }
350
351 if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) {
352 ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY);
353 goto failed;
354 }
355
356 ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len);
357 if (ret < 0)
358 goto failed;
359
Johan Hedberg72a734e2010-12-30 00:38:22 +0200360 if (cp->val)
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200361 queue_work(hdev->workqueue, &hdev->power_on);
362 else
363 queue_work(hdev->workqueue, &hdev->power_off);
364
365 ret = 0;
366
367failed:
368 hci_dev_unlock_bh(hdev);
369 hci_dev_put(hdev);
370 return ret;
371}
372
Johan Hedberg73f22f62010-12-29 16:00:25 +0200373static int set_discoverable(struct sock *sk, unsigned char *data, u16 len)
374{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200375 struct mgmt_mode *cp;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200376 struct hci_dev *hdev;
377 u16 dev_id;
378 u8 scan;
379 int err;
380
381 cp = (void *) data;
382 dev_id = get_unaligned_le16(&cp->index);
383
384 BT_DBG("request for hci%u", dev_id);
385
386 hdev = hci_dev_get(dev_id);
387 if (!hdev)
388 return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV);
389
390 hci_dev_lock_bh(hdev);
391
392 if (!test_bit(HCI_UP, &hdev->flags)) {
393 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
394 goto failed;
395 }
396
397 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) ||
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200398 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) {
Johan Hedberg73f22f62010-12-29 16:00:25 +0200399 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY);
400 goto failed;
401 }
402
Johan Hedberg72a734e2010-12-30 00:38:22 +0200403 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
Johan Hedberg73f22f62010-12-29 16:00:25 +0200404 test_bit(HCI_PSCAN, &hdev->flags)) {
405 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY);
406 goto failed;
407 }
408
409 err = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len);
410 if (err < 0)
411 goto failed;
412
413 scan = SCAN_PAGE;
414
Johan Hedberg72a734e2010-12-30 00:38:22 +0200415 if (cp->val)
Johan Hedberg73f22f62010-12-29 16:00:25 +0200416 scan |= SCAN_INQUIRY;
417
418 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
419 if (err < 0)
420 mgmt_pending_remove(MGMT_OP_SET_DISCOVERABLE, dev_id);
421
422failed:
423 hci_dev_unlock_bh(hdev);
424 hci_dev_put(hdev);
425
426 return err;
427}
428
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200429static int set_connectable(struct sock *sk, unsigned char *data, u16 len)
430{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200431 struct mgmt_mode *cp;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200432 struct hci_dev *hdev;
433 u16 dev_id;
434 u8 scan;
435 int err;
436
437 cp = (void *) data;
438 dev_id = get_unaligned_le16(&cp->index);
439
440 BT_DBG("request for hci%u", dev_id);
441
442 hdev = hci_dev_get(dev_id);
443 if (!hdev)
444 return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV);
445
446 hci_dev_lock_bh(hdev);
447
448 if (!test_bit(HCI_UP, &hdev->flags)) {
449 err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN);
450 goto failed;
451 }
452
453 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) ||
454 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) {
455 err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY);
456 goto failed;
457 }
458
Johan Hedberg72a734e2010-12-30 00:38:22 +0200459 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200460 err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY);
461 goto failed;
462 }
463
464 err = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len);
465 if (err < 0)
466 goto failed;
467
Johan Hedberg72a734e2010-12-30 00:38:22 +0200468 if (cp->val)
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200469 scan = SCAN_PAGE;
470 else
471 scan = 0;
472
473 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
474 if (err < 0)
475 mgmt_pending_remove(MGMT_OP_SET_CONNECTABLE, dev_id);
476
477failed:
478 hci_dev_unlock_bh(hdev);
479 hci_dev_put(hdev);
480
481 return err;
482}
483
Johan Hedbergc542a062011-01-26 13:11:03 +0200484static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
485{
486 struct sk_buff *skb;
487 struct mgmt_hdr *hdr;
488
489 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
490 if (!skb)
491 return -ENOMEM;
492
493 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
494
495 hdr = (void *) skb_put(skb, sizeof(*hdr));
496 hdr->opcode = cpu_to_le16(event);
497 hdr->len = cpu_to_le16(data_len);
498
499 memcpy(skb_put(skb, data_len), data, data_len);
500
501 hci_send_to_sock(NULL, skb, skip_sk);
502 kfree_skb(skb);
503
504 return 0;
505}
506
Johan Hedberg053f0212011-01-26 13:07:10 +0200507static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
508{
509 struct mgmt_hdr *hdr;
510 struct mgmt_ev_cmd_complete *ev;
511 struct mgmt_mode *rp;
512 struct sk_buff *skb;
513
514 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
515 if (!skb)
516 return -ENOMEM;
517
518 hdr = (void *) skb_put(skb, sizeof(*hdr));
519 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
520 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
521
522 ev = (void *) skb_put(skb, sizeof(*ev));
523 put_unaligned_le16(opcode, &ev->opcode);
524
525 rp = (void *) skb_put(skb, sizeof(*rp));
526 put_unaligned_le16(index, &rp->index);
527 rp->val = val;
528
529 if (sock_queue_rcv_skb(sk, skb) < 0)
530 kfree_skb(skb);
531
532 return 0;
533}
534
Johan Hedbergc542a062011-01-26 13:11:03 +0200535static int set_pairable(struct sock *sk, unsigned char *data, u16 len)
536{
537 struct mgmt_mode *cp, ev;
538 struct hci_dev *hdev;
539 u16 dev_id;
540 int err;
541
542 cp = (void *) data;
543 dev_id = get_unaligned_le16(&cp->index);
544
545 BT_DBG("request for hci%u", dev_id);
546
547 hdev = hci_dev_get(dev_id);
548 if (!hdev)
549 return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV);
550
551 hci_dev_lock_bh(hdev);
552
553 if (cp->val)
554 set_bit(HCI_PAIRABLE, &hdev->flags);
555 else
556 clear_bit(HCI_PAIRABLE, &hdev->flags);
557
558 err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val);
559 if (err < 0)
560 goto failed;
561
562 put_unaligned_le16(dev_id, &ev.index);
563 ev.val = cp->val;
564
565 err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk);
566
567failed:
568 hci_dev_unlock_bh(hdev);
569 hci_dev_put(hdev);
570
571 return err;
572}
573
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200574static int index_rsp(struct sock *sk, u16 opcode, u16 index)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200575{
576 struct mgmt_hdr *hdr;
577 struct mgmt_ev_cmd_complete *ev;
578 struct sk_buff *skb;
579
580 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(index), GFP_ATOMIC);
581 if (!skb)
582 return -ENOMEM;
583
584 hdr = (void *) skb_put(skb, sizeof(*hdr));
585 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
586 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(index));
587
588 ev = (void *) skb_put(skb, sizeof(*ev));
589 put_unaligned_le16(opcode, &ev->opcode);
590
591 put_unaligned_le16(index, skb_put(skb, sizeof(index)));
592
593 if (sock_queue_rcv_skb(sk, skb) < 0)
594 kfree_skb(skb);
595
596 return 0;
597}
598
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200599static u8 get_service_classes(struct hci_dev *hdev)
600{
601 struct list_head *p;
602 u8 val = 0;
603
604 list_for_each(p, &hdev->uuids) {
605 struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
606
607 val |= uuid->svc_hint;
608 }
609
610 return val;
611}
612
613static int update_class(struct hci_dev *hdev)
614{
615 u8 cod[3];
616
617 BT_DBG("%s", hdev->name);
618
619 if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
620 return 0;
621
622 cod[0] = hdev->minor_class;
623 cod[1] = hdev->major_class;
624 cod[2] = get_service_classes(hdev);
625
626 if (memcmp(cod, hdev->dev_class, 3) == 0)
627 return 0;
628
629 return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
630}
631
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200632static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
633{
634 struct mgmt_cp_add_uuid *cp;
635 struct hci_dev *hdev;
636 struct bt_uuid *uuid;
637 u16 dev_id;
638 int err;
639
640 cp = (void *) data;
641 dev_id = get_unaligned_le16(&cp->index);
642
643 BT_DBG("request for hci%u", dev_id);
644
645 hdev = hci_dev_get(dev_id);
646 if (!hdev)
647 return cmd_status(sk, MGMT_OP_ADD_UUID, ENODEV);
648
649 hci_dev_lock_bh(hdev);
650
651 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
652 if (!uuid) {
653 err = -ENOMEM;
654 goto failed;
655 }
656
657 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200658 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200659
660 list_add(&uuid->list, &hdev->uuids);
661
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200662 err = update_class(hdev);
663 if (err < 0)
664 goto failed;
665
666 err = index_rsp(sk, MGMT_OP_ADD_UUID, dev_id);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200667
668failed:
669 hci_dev_unlock_bh(hdev);
670 hci_dev_put(hdev);
671
672 return err;
673}
674
675static int remove_uuid(struct sock *sk, unsigned char *data, u16 len)
676{
677 struct list_head *p, *n;
678 struct mgmt_cp_add_uuid *cp;
679 struct hci_dev *hdev;
680 u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
681 u16 dev_id;
682 int err, found;
683
684 cp = (void *) data;
685 dev_id = get_unaligned_le16(&cp->index);
686
687 BT_DBG("request for hci%u", dev_id);
688
689 hdev = hci_dev_get(dev_id);
690 if (!hdev)
691 return cmd_status(sk, MGMT_OP_REMOVE_UUID, ENODEV);
692
693 hci_dev_lock_bh(hdev);
694
695 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
696 err = hci_uuids_clear(hdev);
697 goto unlock;
698 }
699
700 found = 0;
701
702 list_for_each_safe(p, n, &hdev->uuids) {
703 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
704
705 if (memcmp(match->uuid, cp->uuid, 16) != 0)
706 continue;
707
708 list_del(&match->list);
709 found++;
710 }
711
712 if (found == 0) {
713 err = cmd_status(sk, MGMT_OP_REMOVE_UUID, ENOENT);
714 goto unlock;
715 }
716
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200717 err = update_class(hdev);
718 if (err < 0)
719 goto unlock;
720
721 err = index_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200722
723unlock:
724 hci_dev_unlock_bh(hdev);
725 hci_dev_put(hdev);
726
727 return err;
728}
729
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200730static int set_dev_class(struct sock *sk, unsigned char *data, u16 len)
731{
732 struct hci_dev *hdev;
733 struct mgmt_cp_set_dev_class *cp;
734 u16 dev_id;
735 int err;
736
737 cp = (void *) data;
738 dev_id = get_unaligned_le16(&cp->index);
739
740 BT_DBG("request for hci%u", dev_id);
741
742 hdev = hci_dev_get(dev_id);
743 if (!hdev)
744 return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV);
745
746 hci_dev_lock_bh(hdev);
747
748 hdev->major_class = cp->major;
749 hdev->minor_class = cp->minor;
750
751 err = update_class(hdev);
752
753 if (err == 0)
754 err = index_rsp(sk, MGMT_OP_SET_DEV_CLASS, dev_id);
755
756 hci_dev_unlock_bh(hdev);
757 hci_dev_put(hdev);
758
759 return err;
760}
761
762static int set_service_cache(struct sock *sk, unsigned char *data, u16 len)
763{
764 struct hci_dev *hdev;
765 struct mgmt_cp_set_service_cache *cp;
766 u16 dev_id;
767 int err;
768
769 cp = (void *) data;
770 dev_id = get_unaligned_le16(&cp->index);
771
772 hdev = hci_dev_get(dev_id);
773 if (!hdev)
774 return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
775
776 hci_dev_lock_bh(hdev);
777
778 BT_DBG("hci%u enable %d", dev_id, cp->enable);
779
780 if (cp->enable) {
781 set_bit(HCI_SERVICE_CACHE, &hdev->flags);
782 err = 0;
783 } else {
784 clear_bit(HCI_SERVICE_CACHE, &hdev->flags);
785 err = update_class(hdev);
786 }
787
788 if (err == 0)
789 err = index_rsp(sk, MGMT_OP_SET_SERVICE_CACHE, dev_id);
790
791 hci_dev_unlock_bh(hdev);
792 hci_dev_put(hdev);
793
794 return err;
795}
796
Johan Hedberg03811012010-12-08 00:21:06 +0200797int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
798{
799 unsigned char *buf;
800 struct mgmt_hdr *hdr;
801 u16 opcode, len;
802 int err;
803
804 BT_DBG("got %zu bytes", msglen);
805
806 if (msglen < sizeof(*hdr))
807 return -EINVAL;
808
809 buf = kmalloc(msglen, GFP_ATOMIC);
810 if (!buf)
811 return -ENOMEM;
812
813 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
814 err = -EFAULT;
815 goto done;
816 }
817
818 hdr = (struct mgmt_hdr *) buf;
819 opcode = get_unaligned_le16(&hdr->opcode);
820 len = get_unaligned_le16(&hdr->len);
821
822 if (len != msglen - sizeof(*hdr)) {
823 err = -EINVAL;
824 goto done;
825 }
826
827 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +0200828 case MGMT_OP_READ_VERSION:
829 err = read_version(sk);
830 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200831 case MGMT_OP_READ_INDEX_LIST:
832 err = read_index_list(sk);
833 break;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200834 case MGMT_OP_READ_INFO:
835 err = read_controller_info(sk, buf + sizeof(*hdr), len);
836 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200837 case MGMT_OP_SET_POWERED:
838 err = set_powered(sk, buf + sizeof(*hdr), len);
839 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200840 case MGMT_OP_SET_DISCOVERABLE:
841 err = set_discoverable(sk, buf + sizeof(*hdr), len);
842 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200843 case MGMT_OP_SET_CONNECTABLE:
844 err = set_connectable(sk, buf + sizeof(*hdr), len);
845 break;
Johan Hedbergc542a062011-01-26 13:11:03 +0200846 case MGMT_OP_SET_PAIRABLE:
847 err = set_pairable(sk, buf + sizeof(*hdr), len);
848 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200849 case MGMT_OP_ADD_UUID:
850 err = add_uuid(sk, buf + sizeof(*hdr), len);
851 break;
852 case MGMT_OP_REMOVE_UUID:
853 err = remove_uuid(sk, buf + sizeof(*hdr), len);
854 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200855 case MGMT_OP_SET_DEV_CLASS:
856 err = set_dev_class(sk, buf + sizeof(*hdr), len);
857 break;
858 case MGMT_OP_SET_SERVICE_CACHE:
859 err = set_service_cache(sk, buf + sizeof(*hdr), len);
860 break;
Johan Hedberg03811012010-12-08 00:21:06 +0200861 default:
862 BT_DBG("Unknown op %u", opcode);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200863 err = cmd_status(sk, opcode, 0x01);
Johan Hedberg03811012010-12-08 00:21:06 +0200864 break;
865 }
866
Johan Hedberge41d8b42010-12-13 21:07:03 +0200867 if (err < 0)
868 goto done;
869
Johan Hedberg03811012010-12-08 00:21:06 +0200870 err = msglen;
871
872done:
873 kfree(buf);
874 return err;
875}
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200876
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200877int mgmt_index_added(u16 index)
878{
879 struct mgmt_ev_index_added ev;
880
881 put_unaligned_le16(index, &ev.index);
882
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200883 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL);
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200884}
885
886int mgmt_index_removed(u16 index)
887{
888 struct mgmt_ev_index_added ev;
889
890 put_unaligned_le16(index, &ev.index);
891
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200892 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL);
893}
894
Johan Hedberg73f22f62010-12-29 16:00:25 +0200895struct cmd_lookup {
Johan Hedberg72a734e2010-12-30 00:38:22 +0200896 u8 val;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200897 struct sock *sk;
898};
899
Johan Hedberg72a734e2010-12-30 00:38:22 +0200900static void mode_rsp(struct pending_cmd *cmd, void *data)
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200901{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200902 struct mgmt_mode *cp = cmd->cmd;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200903 struct cmd_lookup *match = data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200904
Johan Hedberg72a734e2010-12-30 00:38:22 +0200905 if (cp->val != match->val)
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200906 return;
907
Johan Hedberg053f0212011-01-26 13:07:10 +0200908 send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200909
910 list_del(&cmd->list);
911
912 if (match->sk == NULL) {
913 match->sk = cmd->sk;
914 sock_hold(match->sk);
915 }
916
917 mgmt_pending_free(cmd);
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200918}
Johan Hedberg5add6af2010-12-16 10:00:37 +0200919
920int mgmt_powered(u16 index, u8 powered)
921{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200922 struct mgmt_mode ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200923 struct cmd_lookup match = { powered, NULL };
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200924 int ret;
Johan Hedberg5add6af2010-12-16 10:00:37 +0200925
Johan Hedberg72a734e2010-12-30 00:38:22 +0200926 mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
Johan Hedberg5add6af2010-12-16 10:00:37 +0200927
Johan Hedberg72a734e2010-12-30 00:38:22 +0200928 put_unaligned_le16(index, &ev.index);
929 ev.val = powered;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200930
931 ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk);
932
933 if (match.sk)
934 sock_put(match.sk);
935
936 return ret;
Johan Hedberg5add6af2010-12-16 10:00:37 +0200937}
Johan Hedberg73f22f62010-12-29 16:00:25 +0200938
Johan Hedberg73f22f62010-12-29 16:00:25 +0200939int mgmt_discoverable(u16 index, u8 discoverable)
940{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200941 struct mgmt_mode ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200942 struct cmd_lookup match = { discoverable, NULL };
943 int ret;
944
Johan Hedberg73f22f62010-12-29 16:00:25 +0200945 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index,
Johan Hedberg72a734e2010-12-30 00:38:22 +0200946 mode_rsp, &match);
947
948 put_unaligned_le16(index, &ev.index);
949 ev.val = discoverable;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200950
951 ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk);
952
953 if (match.sk)
954 sock_put(match.sk);
955
956 return ret;
957}
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200958
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200959int mgmt_connectable(u16 index, u8 connectable)
960{
Johan Hedberg72a734e2010-12-30 00:38:22 +0200961 struct mgmt_mode ev;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200962 struct cmd_lookup match = { connectable, NULL };
963 int ret;
964
Johan Hedberg72a734e2010-12-30 00:38:22 +0200965 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200966
Johan Hedberg72a734e2010-12-30 00:38:22 +0200967 put_unaligned_le16(index, &ev.index);
968 ev.val = connectable;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +0200969
970 ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk);
971
972 if (match.sk)
973 sock_put(match.sk);
974
975 return ret;
976}