diff options
author | Johan Hedberg <johan.hedberg@nokia.com> | 2010-12-29 17:18:33 -0500 |
---|---|---|
committer | Gustavo F. Padovan <padovan@profusion.mobi> | 2011-02-07 22:40:05 -0500 |
commit | 9fbcbb455dd01abfad4f314b618ac51d566114cb (patch) | |
tree | d82d36ff143611df2a41f4249432420d649d3576 /net/bluetooth | |
parent | 73f22f62388795c0f6b4f3f97bda7a64f9681aac (diff) |
Bluetooth: Add set_connectable management command
This patch adds a set_connectable command as well as a corresponding
event to the management interface. It's mainly useful for setting an
adapter as connectable from a non-initialized state as well as setting
an already initialized adapter as non-connectable (mostly useful for
qualification purposes).
Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/hci_event.c | 16 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 122 |
2 files changed, 131 insertions, 7 deletions
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f55004af0558..a8a38f17ef78 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c | |||
@@ -274,18 +274,24 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) | |||
274 | 274 | ||
275 | if (!status) { | 275 | if (!status) { |
276 | __u8 param = *((__u8 *) sent); | 276 | __u8 param = *((__u8 *) sent); |
277 | int old_pscan, old_iscan; | ||
277 | 278 | ||
278 | clear_bit(HCI_PSCAN, &hdev->flags); | 279 | old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); |
279 | clear_bit(HCI_ISCAN, &hdev->flags); | 280 | old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); |
280 | 281 | ||
281 | if (param & SCAN_INQUIRY) { | 282 | if (param & SCAN_INQUIRY) { |
282 | set_bit(HCI_ISCAN, &hdev->flags); | 283 | set_bit(HCI_ISCAN, &hdev->flags); |
283 | mgmt_discoverable(hdev->id, 1); | 284 | if (!old_iscan) |
284 | } else | 285 | mgmt_discoverable(hdev->id, 1); |
286 | } else if (old_iscan) | ||
285 | mgmt_discoverable(hdev->id, 0); | 287 | mgmt_discoverable(hdev->id, 0); |
286 | 288 | ||
287 | if (param & SCAN_PAGE) | 289 | if (param & SCAN_PAGE) { |
288 | set_bit(HCI_PSCAN, &hdev->flags); | 290 | set_bit(HCI_PSCAN, &hdev->flags); |
291 | if (!old_pscan) | ||
292 | mgmt_connectable(hdev->id, 1); | ||
293 | } else if (old_pscan) | ||
294 | mgmt_connectable(hdev->id, 0); | ||
289 | } | 295 | } |
290 | 296 | ||
291 | hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); | 297 | hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5fa3034fe79f..fc41cfc3f162 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c | |||
@@ -204,6 +204,7 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) | |||
204 | rp->type = hdev->dev_type; | 204 | rp->type = hdev->dev_type; |
205 | 205 | ||
206 | rp->powered = test_bit(HCI_UP, &hdev->flags); | 206 | rp->powered = test_bit(HCI_UP, &hdev->flags); |
207 | rp->connectable = test_bit(HCI_PSCAN, &hdev->flags); | ||
207 | rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); | 208 | rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); |
208 | rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); | 209 | rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); |
209 | 210 | ||
@@ -390,8 +391,7 @@ static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) | |||
390 | } | 391 | } |
391 | 392 | ||
392 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || | 393 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || |
393 | mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id) || | 394 | 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); | 395 | err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); |
396 | goto failed; | 396 | goto failed; |
397 | } | 397 | } |
@@ -422,6 +422,61 @@ failed: | |||
422 | return err; | 422 | return err; |
423 | } | 423 | } |
424 | 424 | ||
425 | static int set_connectable(struct sock *sk, unsigned char *data, u16 len) | ||
426 | { | ||
427 | struct mgmt_cp_set_connectable *cp; | ||
428 | struct hci_dev *hdev; | ||
429 | u16 dev_id; | ||
430 | u8 scan; | ||
431 | int err; | ||
432 | |||
433 | cp = (void *) data; | ||
434 | dev_id = get_unaligned_le16(&cp->index); | ||
435 | |||
436 | BT_DBG("request for hci%u", dev_id); | ||
437 | |||
438 | hdev = hci_dev_get(dev_id); | ||
439 | if (!hdev) | ||
440 | return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV); | ||
441 | |||
442 | hci_dev_lock_bh(hdev); | ||
443 | |||
444 | if (!test_bit(HCI_UP, &hdev->flags)) { | ||
445 | err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN); | ||
446 | goto failed; | ||
447 | } | ||
448 | |||
449 | if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || | ||
450 | mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { | ||
451 | err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY); | ||
452 | goto failed; | ||
453 | } | ||
454 | |||
455 | if (cp->connectable == test_bit(HCI_PSCAN, &hdev->flags)) { | ||
456 | err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); | ||
457 | goto failed; | ||
458 | } | ||
459 | |||
460 | err = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len); | ||
461 | if (err < 0) | ||
462 | goto failed; | ||
463 | |||
464 | if (cp->connectable) | ||
465 | scan = SCAN_PAGE; | ||
466 | else | ||
467 | scan = 0; | ||
468 | |||
469 | err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); | ||
470 | if (err < 0) | ||
471 | mgmt_pending_remove(MGMT_OP_SET_CONNECTABLE, dev_id); | ||
472 | |||
473 | failed: | ||
474 | hci_dev_unlock_bh(hdev); | ||
475 | hci_dev_put(hdev); | ||
476 | |||
477 | return err; | ||
478 | } | ||
479 | |||
425 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | 480 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) |
426 | { | 481 | { |
427 | unsigned char *buf; | 482 | unsigned char *buf; |
@@ -468,6 +523,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | |||
468 | case MGMT_OP_SET_DISCOVERABLE: | 523 | case MGMT_OP_SET_DISCOVERABLE: |
469 | err = set_discoverable(sk, buf + sizeof(*hdr), len); | 524 | err = set_discoverable(sk, buf + sizeof(*hdr), len); |
470 | break; | 525 | break; |
526 | case MGMT_OP_SET_CONNECTABLE: | ||
527 | err = set_connectable(sk, buf + sizeof(*hdr), len); | ||
528 | break; | ||
471 | default: | 529 | default: |
472 | BT_DBG("Unknown op %u", opcode); | 530 | BT_DBG("Unknown op %u", opcode); |
473 | err = cmd_status(sk, opcode, 0x01); | 531 | err = cmd_status(sk, opcode, 0x01); |
@@ -648,3 +706,63 @@ int mgmt_discoverable(u16 index, u8 discoverable) | |||
648 | 706 | ||
649 | return ret; | 707 | return ret; |
650 | } | 708 | } |
709 | |||
710 | static void connectable_rsp(struct pending_cmd *cmd, void *data) | ||
711 | { | ||
712 | struct mgmt_cp_set_connectable *cp = cmd->cmd; | ||
713 | struct cmd_lookup *match = data; | ||
714 | struct sk_buff *skb; | ||
715 | struct mgmt_hdr *hdr; | ||
716 | struct mgmt_ev_cmd_complete *ev; | ||
717 | struct mgmt_rp_set_connectable *rp; | ||
718 | |||
719 | if (cp->connectable != match->value) | ||
720 | return; | ||
721 | |||
722 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); | ||
723 | if (!skb) | ||
724 | return; | ||
725 | |||
726 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
727 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
728 | hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); | ||
729 | |||
730 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
731 | put_unaligned_le16(MGMT_OP_SET_CONNECTABLE, &ev->opcode); | ||
732 | |||
733 | rp = (void *) skb_put(skb, sizeof(*rp)); | ||
734 | put_unaligned_le16(cmd->index, &rp->index); | ||
735 | rp->connectable = cp->connectable; | ||
736 | |||
737 | if (sock_queue_rcv_skb(cmd->sk, skb) < 0) | ||
738 | kfree_skb(skb); | ||
739 | |||
740 | list_del(&cmd->list); | ||
741 | |||
742 | if (match->sk == NULL) { | ||
743 | match->sk = cmd->sk; | ||
744 | sock_hold(match->sk); | ||
745 | } | ||
746 | |||
747 | mgmt_pending_free(cmd); | ||
748 | } | ||
749 | |||
750 | int mgmt_connectable(u16 index, u8 connectable) | ||
751 | { | ||
752 | struct mgmt_ev_connectable ev; | ||
753 | struct cmd_lookup match = { connectable, NULL }; | ||
754 | int ret; | ||
755 | |||
756 | put_unaligned_le16(index, &ev.index); | ||
757 | ev.connectable = connectable; | ||
758 | |||
759 | mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, | ||
760 | connectable_rsp, &match); | ||
761 | |||
762 | ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); | ||
763 | |||
764 | if (match.sk) | ||
765 | sock_put(match.sk); | ||
766 | |||
767 | return ret; | ||
768 | } | ||