diff options
author | Johan Hedberg <johan.hedberg@nokia.com> | 2010-12-29 09:00:25 -0500 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-02-07 22:40:04 -0500 |
commit | 73f22f62388795c0f6b4f3f97bda7a64f9681aac (patch) | |
tree | 5e85471cd9411a9f21cd2f7bb722be196a23f7c7 | |
parent | eec8d2bcc841ae44edcde9660ff21144a2016053 (diff) |
Bluetooth: Add support for set_discoverable management command
This patch adds a set_discoverable command to the management interface
as well as the corresponding event. The command is used to control the
discoverable state of adapters.
Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r-- | include/net/bluetooth/hci_core.h | 1 | ||||
-rw-r--r-- | include/net/bluetooth/mgmt.h | 16 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 5 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 142 |
4 files changed, 158 insertions, 6 deletions
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2d046e07a586..ee5ec4f17a15 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h | |||
@@ -675,6 +675,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); | |||
675 | int mgmt_index_added(u16 index); | 675 | int mgmt_index_added(u16 index); |
676 | int mgmt_index_removed(u16 index); | 676 | int mgmt_index_removed(u16 index); |
677 | int mgmt_powered(u16 index, u8 powered); | 677 | int mgmt_powered(u16 index, u8 powered); |
678 | int mgmt_discoverable(u16 index, u8 discoverable); | ||
678 | 679 | ||
679 | /* HCI info for socket */ | 680 | /* HCI info for socket */ |
680 | #define hci_pi(sk) ((struct hci_pinfo *) sk) | 681 | #define hci_pi(sk) ((struct hci_pinfo *) sk) |
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 81ef78918b66..434dbcf28b6e 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h | |||
@@ -68,6 +68,16 @@ struct mgmt_rp_set_powered { | |||
68 | __u8 powered; | 68 | __u8 powered; |
69 | } __packed; | 69 | } __packed; |
70 | 70 | ||
71 | #define MGMT_OP_SET_DISCOVERABLE 0x0006 | ||
72 | struct mgmt_cp_set_discoverable { | ||
73 | __le16 index; | ||
74 | __u8 discoverable; | ||
75 | } __packed; | ||
76 | struct mgmt_rp_set_discoverable { | ||
77 | __le16 index; | ||
78 | __u8 discoverable; | ||
79 | } __packed; | ||
80 | |||
71 | #define MGMT_EV_CMD_COMPLETE 0x0001 | 81 | #define MGMT_EV_CMD_COMPLETE 0x0001 |
72 | struct mgmt_ev_cmd_complete { | 82 | struct mgmt_ev_cmd_complete { |
73 | __le16 opcode; | 83 | __le16 opcode; |
@@ -101,3 +111,9 @@ struct mgmt_ev_powered { | |||
101 | __le16 index; | 111 | __le16 index; |
102 | __u8 powered; | 112 | __u8 powered; |
103 | } __packed; | 113 | } __packed; |
114 | |||
115 | #define MGMT_EV_DISCOVERABLE 0x0007 | ||
116 | struct mgmt_ev_discoverable { | ||
117 | __le16 index; | ||
118 | __u8 discoverable; | ||
119 | } __packed; | ||
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d42fb35309b5..f55004af0558 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c | |||
@@ -278,8 +278,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) | |||
278 | clear_bit(HCI_PSCAN, &hdev->flags); | 278 | clear_bit(HCI_PSCAN, &hdev->flags); |
279 | clear_bit(HCI_ISCAN, &hdev->flags); | 279 | clear_bit(HCI_ISCAN, &hdev->flags); |
280 | 280 | ||
281 | if (param & SCAN_INQUIRY) | 281 | if (param & SCAN_INQUIRY) { |
282 | set_bit(HCI_ISCAN, &hdev->flags); | 282 | set_bit(HCI_ISCAN, &hdev->flags); |
283 | mgmt_discoverable(hdev->id, 1); | ||
284 | } else | ||
285 | mgmt_discoverable(hdev->id, 0); | ||
283 | 286 | ||
284 | if (param & SCAN_PAGE) | 287 | if (param & SCAN_PAGE) |
285 | set_bit(HCI_PSCAN, &hdev->flags); | 288 | set_bit(HCI_PSCAN, &hdev->flags); |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b65b6ca08463..5fa3034fe79f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -307,6 +307,18 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) | |||
307 | return NULL; | 307 | return NULL; |
308 | } | 308 | } |
309 | 309 | ||
310 | static 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 | |||
310 | static int set_powered(struct sock *sk, unsigned char *data, u16 len) | 322 | static int set_powered(struct sock *sk, unsigned char *data, u16 len) |
311 | { | 323 | { |
312 | struct mgmt_cp_set_powered *cp; | 324 | struct mgmt_cp_set_powered *cp; |
@@ -353,6 +365,63 @@ failed: | |||
353 | return ret; | 365 | return ret; |
354 | } | 366 | } |
355 | 367 | ||
368 | static 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 | |||
418 | failed: | ||
419 | hci_dev_unlock_bh(hdev); | ||
420 | hci_dev_put(hdev); | ||
421 | |||
422 | return err; | ||
423 | } | ||
424 | |||
356 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 425 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) |
357 | { | 426 | { |
358 | unsigned char *buf; | 427 | unsigned char *buf; |
@@ -396,6 +465,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
396 | case MGMT_OP_SET_POWERED: | 465 | case MGMT_OP_SET_POWERED: |
397 | err = set_powered(sk, buf + sizeof(*hdr), len); | 466 | err = set_powered(sk, buf + sizeof(*hdr), len); |
398 | break; | 467 | break; |
468 | case MGMT_OP_SET_DISCOVERABLE: | ||
469 | err = set_discoverable(sk, buf + sizeof(*hdr), len); | ||
470 | break; | ||
399 | default: | 471 | default: |
400 | BT_DBG("Unknown op %u", opcode); | 472 | BT_DBG("Unknown op %u", opcode); |
401 | err = cmd_status(sk, opcode, 0x01); | 473 | err = cmd_status(sk, opcode, 0x01); |
@@ -453,8 +525,8 @@ int mgmt_index_removed(u16 index) | |||
453 | return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); | 525 | return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); |
454 | } | 526 | } |
455 | 527 | ||
456 | struct powered_lookup { | 528 | struct cmd_lookup { |
457 | u8 powered; | 529 | u8 value; |
458 | struct sock *sk; | 530 | struct sock *sk; |
459 | }; | 531 | }; |
460 | 532 | ||
@@ -465,9 +537,9 @@ static void power_rsp(struct pending_cmd *cmd, void *data) | |||
465 | struct mgmt_rp_set_powered *rp; | 537 | struct mgmt_rp_set_powered *rp; |
466 | struct mgmt_cp_set_powered *cp = cmd->cmd; | 538 | struct mgmt_cp_set_powered *cp = cmd->cmd; |
467 | struct sk_buff *skb; | 539 | struct sk_buff *skb; |
468 | struct powered_lookup *match = data; | 540 | struct cmd_lookup *match = data; |
469 | 541 | ||
470 | if (cp->powered != match->powered) | 542 | if (cp->powered != match->value) |
471 | return; | 543 | return; |
472 | 544 | ||
473 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); | 545 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); |
@@ -501,7 +573,7 @@ static void power_rsp(struct pending_cmd *cmd, void *data) | |||
501 | int mgmt_powered(u16 index, u8 powered) | 573 | int mgmt_powered(u16 index, u8 powered) |
502 | { | 574 | { |
503 | struct mgmt_ev_powered ev; | 575 | struct mgmt_ev_powered ev; |
504 | struct powered_lookup match = { powered, NULL }; | 576 | struct cmd_lookup match = { powered, NULL }; |
505 | int ret; | 577 | int ret; |
506 | 578 | ||
507 | put_unaligned_le16(index, &ev.index); | 579 | put_unaligned_le16(index, &ev.index); |
@@ -516,3 +588,63 @@ int mgmt_powered(u16 index, u8 powered) | |||
516 | 588 | ||
517 | return ret; | 589 | return ret; |
518 | } | 590 | } |
591 | |||
592 | static 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 | |||
632 | int 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 | } | ||