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 /net/bluetooth/mgmt.c | |
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>
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r-- | net/bluetooth/mgmt.c | 142 |
1 files changed, 137 insertions, 5 deletions
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 | } | ||