]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - net/bluetooth/mgmt.c
Bluetooth: Add send_mode_rsp convenience function for mgmt.c
[mirror_ubuntu-bionic-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
ebc99feb
JH
145 set_bit(HCI_MGMT, &d->flags);
146
ab81cbf9
JH
147 if (test_bit(HCI_SETUP, &d->flags))
148 continue;
149
faba42eb
JH
150 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
f7b64e69 162static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
0381101f
JH
163{
164 struct sk_buff *skb;
165 struct mgmt_hdr *hdr;
f7b64e69
JH
166 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;
0381101f
JH
171
172 BT_DBG("sock %p", sk);
173
f7b64e69
JH
174 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);
0381101f 178 if (!skb)
e41d8b4e 179 return -ENOMEM;
0381101f
JH
180
181 hdr = (void *) skb_put(skb, sizeof(*hdr));
f7b64e69
JH
182 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
183 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
0381101f
JH
184
185 ev = (void *) skb_put(skb, sizeof(*ev));
f7b64e69
JH
186 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
ab81cbf9
JH
201 hci_del_off_timer(hdev);
202
f7b64e69
JH
203 hci_dev_lock_bh(hdev);
204
ebc99feb
JH
205 set_bit(HCI_MGMT, &hdev->flags);
206
f7b64e69
JH
207 put_unaligned_le16(hdev->id, &rp->index);
208 rp->type = hdev->dev_type;
209
210 rp->powered = test_bit(HCI_UP, &hdev->flags);
9fbcbb45 211 rp->connectable = test_bit(HCI_PSCAN, &hdev->flags);
f7b64e69
JH
212 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);
0381101f
JH
231
232 if (sock_queue_rcv_skb(sk, skb) < 0)
233 kfree_skb(skb);
e41d8b4e
JH
234
235 return 0;
0381101f
JH
236}
237
eec8d2bc
JH
238static 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
73f22f62
JH
315static 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
eec8d2bc
JH
327static int set_powered(struct sock *sk, unsigned char *data, u16 len)
328{
72a734ec 329 struct mgmt_mode *cp;
eec8d2bc
JH
330 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);
72a734ec 346 if ((cp->val && up) || (!cp->val && !up)) {
eec8d2bc
JH
347 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
72a734ec 360 if (cp->val)
eec8d2bc
JH
361 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
73f22f62
JH
373static int set_discoverable(struct sock *sk, unsigned char *data, u16 len)
374{
72a734ec 375 struct mgmt_mode *cp;
73f22f62
JH
376 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) ||
9fbcbb45 398 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) {
73f22f62
JH
399 err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY);
400 goto failed;
401 }
402
72a734ec 403 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
73f22f62
JH
404 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
72a734ec 415 if (cp->val)
73f22f62
JH
416 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
9fbcbb45
JH
429static int set_connectable(struct sock *sk, unsigned char *data, u16 len)
430{
72a734ec 431 struct mgmt_mode *cp;
9fbcbb45
JH
432 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
72a734ec 459 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
9fbcbb45
JH
460 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
72a734ec 468 if (cp->val)
9fbcbb45
JH
469 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
053f0211
JH
484static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
485{
486 struct mgmt_hdr *hdr;
487 struct mgmt_ev_cmd_complete *ev;
488 struct mgmt_mode *rp;
489 struct sk_buff *skb;
490
491 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
492 if (!skb)
493 return -ENOMEM;
494
495 hdr = (void *) skb_put(skb, sizeof(*hdr));
496 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
497 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
498
499 ev = (void *) skb_put(skb, sizeof(*ev));
500 put_unaligned_le16(opcode, &ev->opcode);
501
502 rp = (void *) skb_put(skb, sizeof(*rp));
503 put_unaligned_le16(index, &rp->index);
504 rp->val = val;
505
506 if (sock_queue_rcv_skb(sk, skb) < 0)
507 kfree_skb(skb);
508
509 return 0;
510}
511
0381101f
JH
512int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
513{
514 unsigned char *buf;
515 struct mgmt_hdr *hdr;
516 u16 opcode, len;
517 int err;
518
519 BT_DBG("got %zu bytes", msglen);
520
521 if (msglen < sizeof(*hdr))
522 return -EINVAL;
523
524 buf = kmalloc(msglen, GFP_ATOMIC);
525 if (!buf)
526 return -ENOMEM;
527
528 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
529 err = -EFAULT;
530 goto done;
531 }
532
533 hdr = (struct mgmt_hdr *) buf;
534 opcode = get_unaligned_le16(&hdr->opcode);
535 len = get_unaligned_le16(&hdr->len);
536
537 if (len != msglen - sizeof(*hdr)) {
538 err = -EINVAL;
539 goto done;
540 }
541
542 switch (opcode) {
02d98129
JH
543 case MGMT_OP_READ_VERSION:
544 err = read_version(sk);
545 break;
faba42eb
JH
546 case MGMT_OP_READ_INDEX_LIST:
547 err = read_index_list(sk);
548 break;
f7b64e69
JH
549 case MGMT_OP_READ_INFO:
550 err = read_controller_info(sk, buf + sizeof(*hdr), len);
551 break;
eec8d2bc
JH
552 case MGMT_OP_SET_POWERED:
553 err = set_powered(sk, buf + sizeof(*hdr), len);
554 break;
73f22f62
JH
555 case MGMT_OP_SET_DISCOVERABLE:
556 err = set_discoverable(sk, buf + sizeof(*hdr), len);
557 break;
9fbcbb45
JH
558 case MGMT_OP_SET_CONNECTABLE:
559 err = set_connectable(sk, buf + sizeof(*hdr), len);
560 break;
0381101f
JH
561 default:
562 BT_DBG("Unknown op %u", opcode);
e41d8b4e 563 err = cmd_status(sk, opcode, 0x01);
0381101f
JH
564 break;
565 }
566
e41d8b4e
JH
567 if (err < 0)
568 goto done;
569
0381101f
JH
570 err = msglen;
571
572done:
573 kfree(buf);
574 return err;
575}
c71e97bf 576
eec8d2bc 577static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
c71e97bf
JH
578{
579 struct sk_buff *skb;
580 struct mgmt_hdr *hdr;
581
582 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
583 if (!skb)
584 return -ENOMEM;
585
586 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
587
588 hdr = (void *) skb_put(skb, sizeof(*hdr));
589 hdr->opcode = cpu_to_le16(event);
590 hdr->len = cpu_to_le16(data_len);
591
592 memcpy(skb_put(skb, data_len), data, data_len);
593
eec8d2bc 594 hci_send_to_sock(NULL, skb, skip_sk);
c71e97bf
JH
595 kfree_skb(skb);
596
597 return 0;
598}
599
600int mgmt_index_added(u16 index)
601{
602 struct mgmt_ev_index_added ev;
603
604 put_unaligned_le16(index, &ev.index);
605
eec8d2bc 606 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL);
c71e97bf
JH
607}
608
609int mgmt_index_removed(u16 index)
610{
611 struct mgmt_ev_index_added ev;
612
613 put_unaligned_le16(index, &ev.index);
614
eec8d2bc
JH
615 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL);
616}
617
73f22f62 618struct cmd_lookup {
72a734ec 619 u8 val;
eec8d2bc
JH
620 struct sock *sk;
621};
622
72a734ec 623static void mode_rsp(struct pending_cmd *cmd, void *data)
eec8d2bc 624{
72a734ec 625 struct mgmt_mode *cp = cmd->cmd;
73f22f62 626 struct cmd_lookup *match = data;
eec8d2bc 627
72a734ec 628 if (cp->val != match->val)
eec8d2bc
JH
629 return;
630
053f0211 631 send_mode_rsp(cmd->sk, cmd->opcode, cmd->index, cp->val);
eec8d2bc
JH
632
633 list_del(&cmd->list);
634
635 if (match->sk == NULL) {
636 match->sk = cmd->sk;
637 sock_hold(match->sk);
638 }
639
640 mgmt_pending_free(cmd);
c71e97bf 641}
5add6af8
JH
642
643int mgmt_powered(u16 index, u8 powered)
644{
72a734ec 645 struct mgmt_mode ev;
73f22f62 646 struct cmd_lookup match = { powered, NULL };
eec8d2bc 647 int ret;
5add6af8 648
72a734ec 649 mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match);
5add6af8 650
72a734ec
JH
651 put_unaligned_le16(index, &ev.index);
652 ev.val = powered;
eec8d2bc
JH
653
654 ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk);
655
656 if (match.sk)
657 sock_put(match.sk);
658
659 return ret;
5add6af8 660}
73f22f62 661
73f22f62
JH
662int mgmt_discoverable(u16 index, u8 discoverable)
663{
72a734ec 664 struct mgmt_mode ev;
73f22f62
JH
665 struct cmd_lookup match = { discoverable, NULL };
666 int ret;
667
73f22f62 668 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index,
72a734ec
JH
669 mode_rsp, &match);
670
671 put_unaligned_le16(index, &ev.index);
672 ev.val = discoverable;
73f22f62
JH
673
674 ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk);
675
676 if (match.sk)
677 sock_put(match.sk);
678
679 return ret;
680}
9fbcbb45 681
9fbcbb45
JH
682int mgmt_connectable(u16 index, u8 connectable)
683{
72a734ec 684 struct mgmt_mode ev;
9fbcbb45
JH
685 struct cmd_lookup match = { connectable, NULL };
686 int ret;
687
72a734ec 688 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match);
9fbcbb45 689
72a734ec
JH
690 put_unaligned_le16(index, &ev.index);
691 ev.val = connectable;
9fbcbb45
JH
692
693 ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk);
694
695 if (match.sk)
696 sock_put(match.sk);
697
698 return ret;
699}