blob: 5fa3034fe79ff2f6cfc74821ccbad7997f38386a [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 Hedbergf7b64e62010-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
145 if (test_bit(HCI_SETUP, &d->flags))
146 continue;
147
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200148 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
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200160static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200161{
162 struct sk_buff *skb;
163 struct mgmt_hdr *hdr;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200164 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;
Johan Hedberg03811012010-12-08 00:21:06 +0200169
170 BT_DBG("sock %p", sk);
171
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200172 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);
Johan Hedberg03811012010-12-08 00:21:06 +0200176 if (!skb)
Johan Hedberge41d8b42010-12-13 21:07:03 +0200177 return -ENOMEM;
Johan Hedberg03811012010-12-08 00:21:06 +0200178
179 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200180 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
181 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200182
183 ev = (void *) skb_put(skb, sizeof(*ev));
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200184 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
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200199 hci_del_off_timer(hdev);
200
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200201 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);
Johan Hedberg03811012010-12-08 00:21:06 +0200226
227 if (sock_queue_rcv_skb(sk, skb) < 0)
228 kfree_skb(skb);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200229
230 return 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200231}
232
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200233static 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
Johan Hedberg73f22f62010-12-29 16:00:25 +0200310static 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
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200322static 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
Johan Hedberg73f22f62010-12-29 16:00:25 +0200368static 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
Johan Hedberg03811012010-12-08 00:21:06 +0200425int 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) {
Johan Hedberg02d98122010-12-13 21:07:04 +0200456 case MGMT_OP_READ_VERSION:
457 err = read_version(sk);
458 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200459 case MGMT_OP_READ_INDEX_LIST:
460 err = read_index_list(sk);
461 break;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200462 case MGMT_OP_READ_INFO:
463 err = read_controller_info(sk, buf + sizeof(*hdr), len);
464 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200465 case MGMT_OP_SET_POWERED:
466 err = set_powered(sk, buf + sizeof(*hdr), len);
467 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200468 case MGMT_OP_SET_DISCOVERABLE:
469 err = set_discoverable(sk, buf + sizeof(*hdr), len);
470 break;
Johan Hedberg03811012010-12-08 00:21:06 +0200471 default:
472 BT_DBG("Unknown op %u", opcode);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200473 err = cmd_status(sk, opcode, 0x01);
Johan Hedberg03811012010-12-08 00:21:06 +0200474 break;
475 }
476
Johan Hedberge41d8b42010-12-13 21:07:03 +0200477 if (err < 0)
478 goto done;
479
Johan Hedberg03811012010-12-08 00:21:06 +0200480 err = msglen;
481
482done:
483 kfree(buf);
484 return err;
485}
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200486
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200487static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200488{
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
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200504 hci_send_to_sock(NULL, skb, skip_sk);
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200505 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
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200516 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL);
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200517}
518
519int mgmt_index_removed(u16 index)
520{
521 struct mgmt_ev_index_added ev;
522
523 put_unaligned_le16(index, &ev.index);
524
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200525 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL);
526}
527
Johan Hedberg73f22f62010-12-29 16:00:25 +0200528struct cmd_lookup {
529 u8 value;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200530 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;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200540 struct cmd_lookup *match = data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200541
Johan Hedberg73f22f62010-12-29 16:00:25 +0200542 if (cp->powered != match->value)
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200543 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);
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200571}
Johan Hedberg5add6af2010-12-16 10:00:37 +0200572
573int mgmt_powered(u16 index, u8 powered)
574{
575 struct mgmt_ev_powered ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200576 struct cmd_lookup match = { powered, NULL };
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200577 int ret;
Johan Hedberg5add6af2010-12-16 10:00:37 +0200578
579 put_unaligned_le16(index, &ev.index);
580 ev.powered = powered;
581
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200582 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;
Johan Hedberg5add6af2010-12-16 10:00:37 +0200590}
Johan Hedberg73f22f62010-12-29 16:00:25 +0200591
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}