]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - net/bluetooth/mgmt.c
Bluetooth: Add support for set_discoverable management command
[mirror_ubuntu-artful-kernel.git] / net / bluetooth / mgmt.c
CommitLineData
0381101f
JH
1/*
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
02d98129
JH
32#define MGMT_VERSION 0
33#define MGMT_REVISION 1
34
eec8d2bc
JH
35struct 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
f7b64e69
JH
45static 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
02d98129
JH
72static 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
faba42eb
JH
102static 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);
b2c60d42
JJ
124 if (!skb) {
125 read_unlock(&hci_dev_list_lock);
faba42eb 126 return -ENOMEM;
b2c60d42 127 }
faba42eb
JH
128
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);
ab81cbf9
JH
142
143 hci_del_off_timer(d);
144
145 if (test_bit(HCI_SETUP, &d->flags))
146 continue;
147
faba42eb
JH
148 put_unaligned_le16(d->id, &rp->index[i++]);
149 BT_DBG("Added hci%u", d->id);
150 }
151
152 read_unlock(&hci_dev_list_lock);
153
154 if (sock_queue_rcv_skb(sk, skb) < 0)
155 kfree_skb(skb);
156
157 return 0;
158}
159
f7b64e69 160static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
0381101f
JH
161{
162 struct sk_buff *skb;
163 struct mgmt_hdr *hdr;
f7b64e69
JH
164 struct mgmt_ev_cmd_complete *ev;
165 struct mgmt_rp_read_info *rp;
166 struct mgmt_cp_read_info *cp;
167 struct hci_dev *hdev;
168 u16 dev_id;
0381101f
JH
169
170 BT_DBG("sock %p", sk);
171
f7b64e69
JH
172 if (len != 2)
173 return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
174
175 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
0381101f 176 if (!skb)
e41d8b4e 177 return -ENOMEM;
0381101f
JH
178
179 hdr = (void *) skb_put(skb, sizeof(*hdr));
f7b64e69
JH
180 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
181 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
0381101f
JH
182
183 ev = (void *) skb_put(skb, sizeof(*ev));
f7b64e69
JH
184 put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
185
186 rp = (void *) skb_put(skb, sizeof(*rp));
187
188 cp = (void *) data;
189 dev_id = get_unaligned_le16(&cp->index);
190
191 BT_DBG("request for hci%u", dev_id);
192
193 hdev = hci_dev_get(dev_id);
194 if (!hdev) {
195 kfree_skb(skb);
196 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
197 }
198
ab81cbf9
JH
199 hci_del_off_timer(hdev);
200
f7b64e69
JH
201 hci_dev_lock_bh(hdev);
202
203 put_unaligned_le16(hdev->id, &rp->index);
204 rp->type = hdev->dev_type;
205
206 rp->powered = test_bit(HCI_UP, &hdev->flags);
207 rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags);
208 rp->pairable = test_bit(HCI_PSCAN, &hdev->flags);
209
210 if (test_bit(HCI_AUTH, &hdev->flags))
211 rp->sec_mode = 3;
212 else if (hdev->ssp_mode > 0)
213 rp->sec_mode = 4;
214 else
215 rp->sec_mode = 2;
216
217 bacpy(&rp->bdaddr, &hdev->bdaddr);
218 memcpy(rp->features, hdev->features, 8);
219 memcpy(rp->dev_class, hdev->dev_class, 3);
220 put_unaligned_le16(hdev->manufacturer, &rp->manufacturer);
221 rp->hci_ver = hdev->hci_ver;
222 put_unaligned_le16(hdev->hci_rev, &rp->hci_rev);
223
224 hci_dev_unlock_bh(hdev);
225 hci_dev_put(hdev);
0381101f
JH
226
227 if (sock_queue_rcv_skb(sk, skb) < 0)
228 kfree_skb(skb);
e41d8b4e
JH
229
230 return 0;
0381101f
JH
231}
232
eec8d2bc
JH
233static void mgmt_pending_free(struct pending_cmd *cmd)
234{
235 sock_put(cmd->sk);
236 kfree(cmd->cmd);
237 kfree(cmd);
238}
239
240static int mgmt_pending_add(struct sock *sk, u16 opcode, int index,
241 void *data, u16 len)
242{
243 struct pending_cmd *cmd;
244
245 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
246 if (!cmd)
247 return -ENOMEM;
248
249 cmd->opcode = opcode;
250 cmd->index = index;
251
252 cmd->cmd = kmalloc(len, GFP_ATOMIC);
253 if (!cmd->cmd) {
254 kfree(cmd);
255 return -ENOMEM;
256 }
257
258 memcpy(cmd->cmd, data, len);
259
260 cmd->sk = sk;
261 sock_hold(sk);
262
263 list_add(&cmd->list, &cmd_list);
264
265 return 0;
266}
267
268static void mgmt_pending_foreach(u16 opcode, int index,
269 void (*cb)(struct pending_cmd *cmd, void *data),
270 void *data)
271{
272 struct list_head *p, *n;
273
274 list_for_each_safe(p, n, &cmd_list) {
275 struct pending_cmd *cmd;
276
277 cmd = list_entry(p, struct pending_cmd, list);
278
279 if (cmd->opcode != opcode)
280 continue;
281
282 if (index >= 0 && cmd->index != index)
283 continue;
284
285 cb(cmd, data);
286 }
287}
288
289static struct pending_cmd *mgmt_pending_find(u16 opcode, int index)
290{
291 struct list_head *p;
292
293 list_for_each(p, &cmd_list) {
294 struct pending_cmd *cmd;
295
296 cmd = list_entry(p, struct pending_cmd, list);
297
298 if (cmd->opcode != opcode)
299 continue;
300
301 if (index >= 0 && cmd->index != index)
302 continue;
303
304 return cmd;
305 }
306
307 return NULL;
308}
309
73f22f62
JH
310static void mgmt_pending_remove(u16 opcode, int index)
311{
312 struct pending_cmd *cmd;
313
314 cmd = mgmt_pending_find(opcode, index);
315 if (cmd == NULL)
316 return;
317
318 list_del(&cmd->list);
319 mgmt_pending_free(cmd);
320}
321
eec8d2bc
JH
322static int set_powered(struct sock *sk, unsigned char *data, u16 len)
323{
324 struct mgmt_cp_set_powered *cp;
325 struct hci_dev *hdev;
326 u16 dev_id;
327 int ret, up;
328
329 cp = (void *) data;
330 dev_id = get_unaligned_le16(&cp->index);
331
332 BT_DBG("request for hci%u", dev_id);
333
334 hdev = hci_dev_get(dev_id);
335 if (!hdev)
336 return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV);
337
338 hci_dev_lock_bh(hdev);
339
340 up = test_bit(HCI_UP, &hdev->flags);
341 if ((cp->powered && up) || (!cp->powered && !up)) {
342 ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY);
343 goto failed;
344 }
345
346 if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) {
347 ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY);
348 goto failed;
349 }
350
351 ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len);
352 if (ret < 0)
353 goto failed;
354
355 if (cp->powered)
356 queue_work(hdev->workqueue, &hdev->power_on);
357 else
358 queue_work(hdev->workqueue, &hdev->power_off);
359
360 ret = 0;
361
362failed:
363 hci_dev_unlock_bh(hdev);
364 hci_dev_put(hdev);
365 return ret;
366}
367
73f22f62
JH
368static int set_discoverable(struct sock *sk, unsigned char *data, u16 len)
369{
370 struct mgmt_cp_set_discoverable *cp;
371 struct hci_dev *hdev;
372 u16 dev_id;
373 u8 scan;
374 int err;
375
376 cp = (void *) data;
377 dev_id = get_unaligned_le16(&cp->index);
378
379 BT_DBG("request for hci%u", dev_id);
380
381 hdev = hci_dev_get(dev_id);
382 if (!hdev)
383 return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV);
384
385 hci_dev_lock_bh(hdev);
386
387 if (!test_bit(HCI_UP, &hdev->flags)) {
388 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
389 goto failed;
390 }
391
392 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) ||
393 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id) ||
394 hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE)) {
395 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY);
396 goto failed;
397 }
398
399 if (cp->discoverable == test_bit(HCI_ISCAN, &hdev->flags) &&
400 test_bit(HCI_PSCAN, &hdev->flags)) {
401 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY);
402 goto failed;
403 }
404
405 err = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len);
406 if (err < 0)
407 goto failed;
408
409 scan = SCAN_PAGE;
410
411 if (cp->discoverable)
412 scan |= SCAN_INQUIRY;
413
414 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
415 if (err < 0)
416 mgmt_pending_remove(MGMT_OP_SET_DISCOVERABLE, dev_id);
417
418failed:
419 hci_dev_unlock_bh(hdev);
420 hci_dev_put(hdev);
421
422 return err;
423}
424
0381101f
JH
425int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
426{
427 unsigned char *buf;
428 struct mgmt_hdr *hdr;
429 u16 opcode, len;
430 int err;
431
432 BT_DBG("got %zu bytes", msglen);
433
434 if (msglen < sizeof(*hdr))
435 return -EINVAL;
436
437 buf = kmalloc(msglen, GFP_ATOMIC);
438 if (!buf)
439 return -ENOMEM;
440
441 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
442 err = -EFAULT;
443 goto done;
444 }
445
446 hdr = (struct mgmt_hdr *) buf;
447 opcode = get_unaligned_le16(&hdr->opcode);
448 len = get_unaligned_le16(&hdr->len);
449
450 if (len != msglen - sizeof(*hdr)) {
451 err = -EINVAL;
452 goto done;
453 }
454
455 switch (opcode) {
02d98129
JH
456 case MGMT_OP_READ_VERSION:
457 err = read_version(sk);
458 break;
faba42eb
JH
459 case MGMT_OP_READ_INDEX_LIST:
460 err = read_index_list(sk);
461 break;
f7b64e69
JH
462 case MGMT_OP_READ_INFO:
463 err = read_controller_info(sk, buf + sizeof(*hdr), len);
464 break;
eec8d2bc
JH
465 case MGMT_OP_SET_POWERED:
466 err = set_powered(sk, buf + sizeof(*hdr), len);
467 break;
73f22f62
JH
468 case MGMT_OP_SET_DISCOVERABLE:
469 err = set_discoverable(sk, buf + sizeof(*hdr), len);
470 break;
0381101f
JH
471 default:
472 BT_DBG("Unknown op %u", opcode);
e41d8b4e 473 err = cmd_status(sk, opcode, 0x01);
0381101f
JH
474 break;
475 }
476
e41d8b4e
JH
477 if (err < 0)
478 goto done;
479
0381101f
JH
480 err = msglen;
481
482done:
483 kfree(buf);
484 return err;
485}
c71e97bf 486
eec8d2bc 487static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
c71e97bf
JH
488{
489 struct sk_buff *skb;
490 struct mgmt_hdr *hdr;
491
492 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
493 if (!skb)
494 return -ENOMEM;
495
496 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
497
498 hdr = (void *) skb_put(skb, sizeof(*hdr));
499 hdr->opcode = cpu_to_le16(event);
500 hdr->len = cpu_to_le16(data_len);
501
502 memcpy(skb_put(skb, data_len), data, data_len);
503
eec8d2bc 504 hci_send_to_sock(NULL, skb, skip_sk);
c71e97bf
JH
505 kfree_skb(skb);
506
507 return 0;
508}
509
510int mgmt_index_added(u16 index)
511{
512 struct mgmt_ev_index_added ev;
513
514 put_unaligned_le16(index, &ev.index);
515
eec8d2bc 516 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL);
c71e97bf
JH
517}
518
519int mgmt_index_removed(u16 index)
520{
521 struct mgmt_ev_index_added ev;
522
523 put_unaligned_le16(index, &ev.index);
524
eec8d2bc
JH
525 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL);
526}
527
73f22f62
JH
528struct cmd_lookup {
529 u8 value;
eec8d2bc
JH
530 struct sock *sk;
531};
532
533static void power_rsp(struct pending_cmd *cmd, void *data)
534{
535 struct mgmt_hdr *hdr;
536 struct mgmt_ev_cmd_complete *ev;
537 struct mgmt_rp_set_powered *rp;
538 struct mgmt_cp_set_powered *cp = cmd->cmd;
539 struct sk_buff *skb;
73f22f62 540 struct cmd_lookup *match = data;
eec8d2bc 541
73f22f62 542 if (cp->powered != match->value)
eec8d2bc
JH
543 return;
544
545 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
546 if (!skb)
547 return;
548
549 hdr = (void *) skb_put(skb, sizeof(*hdr));
550 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
551 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
552
553 ev = (void *) skb_put(skb, sizeof(*ev));
554 put_unaligned_le16(cmd->opcode, &ev->opcode);
555
556 rp = (void *) skb_put(skb, sizeof(*rp));
557 put_unaligned_le16(cmd->index, &rp->index);
558 rp->powered = cp->powered;
559
560 if (sock_queue_rcv_skb(cmd->sk, skb) < 0)
561 kfree_skb(skb);
562
563 list_del(&cmd->list);
564
565 if (match->sk == NULL) {
566 match->sk = cmd->sk;
567 sock_hold(match->sk);
568 }
569
570 mgmt_pending_free(cmd);
c71e97bf 571}
5add6af8
JH
572
573int mgmt_powered(u16 index, u8 powered)
574{
575 struct mgmt_ev_powered ev;
73f22f62 576 struct cmd_lookup match = { powered, NULL };
eec8d2bc 577 int ret;
5add6af8
JH
578
579 put_unaligned_le16(index, &ev.index);
580 ev.powered = powered;
581
eec8d2bc
JH
582 mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, power_rsp, &match);
583
584 ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk);
585
586 if (match.sk)
587 sock_put(match.sk);
588
589 return ret;
5add6af8 590}
73f22f62
JH
591
592static void discoverable_rsp(struct pending_cmd *cmd, void *data)
593{
594 struct mgmt_cp_set_discoverable *cp = cmd->cmd;
595 struct cmd_lookup *match = data;
596 struct sk_buff *skb;
597 struct mgmt_hdr *hdr;
598 struct mgmt_ev_cmd_complete *ev;
599 struct mgmt_rp_set_discoverable *rp;
600
601 if (cp->discoverable != match->value)
602 return;
603
604 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
605 if (!skb)
606 return;
607
608 hdr = (void *) skb_put(skb, sizeof(*hdr));
609 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
610 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
611
612 ev = (void *) skb_put(skb, sizeof(*ev));
613 put_unaligned_le16(MGMT_OP_SET_DISCOVERABLE, &ev->opcode);
614
615 rp = (void *) skb_put(skb, sizeof(*rp));
616 put_unaligned_le16(cmd->index, &rp->index);
617 rp->discoverable = cp->discoverable;
618
619 if (sock_queue_rcv_skb(cmd->sk, skb) < 0)
620 kfree_skb(skb);
621
622 list_del(&cmd->list);
623
624 if (match->sk == NULL) {
625 match->sk = cmd->sk;
626 sock_hold(match->sk);
627 }
628
629 mgmt_pending_free(cmd);
630}
631
632int mgmt_discoverable(u16 index, u8 discoverable)
633{
634 struct mgmt_ev_discoverable ev;
635 struct cmd_lookup match = { discoverable, NULL };
636 int ret;
637
638 put_unaligned_le16(index, &ev.index);
639 ev.discoverable = discoverable;
640
641 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index,
642 discoverable_rsp, &match);
643
644 ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk);
645
646 if (match.sk)
647 sock_put(match.sk);
648
649 return ret;
650}