aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/mgmt.c
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2011-01-13 14:56:52 -0500
committerGustavo F. Padovan <padovan@profusion.mobi>2011-02-07 22:40:06 -0500
commit1aff6f09491f454d4cd9f405c783fa5e9d3168a0 (patch)
tree3c51a386ada638e39bf6fa42dbc35413ede08ddd /net/bluetooth/mgmt.c
parentd5859e22cd40b73164b3e5d8d5d796f96edcc6af (diff)
Bluetooth: Add class of device control to the management interface
This patch adds the possibility for user space to fully control the Class of Device value of local adapters. To control the service class bits each UUID that's added comes with a service class "hint" which acts as a mask of bits that the UUID needs to have enabled. The set_service_cache management command is used to make sure we queue up all UUID changes as user space initializes its drivers and then send a single HCI_Write_Class_of_Device command when initialization is complete. 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.c121
1 files changed, 118 insertions, 3 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 0854c2f1073c..a08f4ce03182 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -571,7 +571,7 @@ failed:
571 return err; 571 return err;
572} 572}
573 573
574static int uuid_rsp(struct sock *sk, u16 opcode, u16 index) 574static int index_rsp(struct sock *sk, u16 opcode, u16 index)
575{ 575{
576 struct mgmt_hdr *hdr; 576 struct mgmt_hdr *hdr;
577 struct mgmt_ev_cmd_complete *ev; 577 struct mgmt_ev_cmd_complete *ev;
@@ -596,6 +596,39 @@ static int uuid_rsp(struct sock *sk, u16 opcode, u16 index)
596 return 0; 596 return 0;
597} 597}
598 598
599static u8 get_service_classes(struct hci_dev *hdev)
600{
601 struct list_head *p;
602 u8 val = 0;
603
604 list_for_each(p, &hdev->uuids) {
605 struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
606
607 val |= uuid->svc_hint;
608 }
609
610 return val;
611}
612
613static int update_class(struct hci_dev *hdev)
614{
615 u8 cod[3];
616
617 BT_DBG("%s", hdev->name);
618
619 if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
620 return 0;
621
622 cod[0] = hdev->minor_class;
623 cod[1] = hdev->major_class;
624 cod[2] = get_service_classes(hdev);
625
626 if (memcmp(cod, hdev->dev_class, 3) == 0)
627 return 0;
628
629 return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
630}
631
599static int add_uuid(struct sock *sk, unsigned char *data, u16 len) 632static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
600{ 633{
601 struct mgmt_cp_add_uuid *cp; 634 struct mgmt_cp_add_uuid *cp;
@@ -622,10 +655,15 @@ static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
622 } 655 }
623 656
624 memcpy(uuid->uuid, cp->uuid, 16); 657 memcpy(uuid->uuid, cp->uuid, 16);
658 uuid->svc_hint = cp->svc_hint;
625 659
626 list_add(&uuid->list, &hdev->uuids); 660 list_add(&uuid->list, &hdev->uuids);
627 661
628 err = uuid_rsp(sk, MGMT_OP_ADD_UUID, dev_id); 662 err = update_class(hdev);
663 if (err < 0)
664 goto failed;
665
666 err = index_rsp(sk, MGMT_OP_ADD_UUID, dev_id);
629 667
630failed: 668failed:
631 hci_dev_unlock_bh(hdev); 669 hci_dev_unlock_bh(hdev);
@@ -676,7 +714,11 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len)
676 goto unlock; 714 goto unlock;
677 } 715 }
678 716
679 err = uuid_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id); 717 err = update_class(hdev);
718 if (err < 0)
719 goto unlock;
720
721 err = index_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id);
680 722
681unlock: 723unlock:
682 hci_dev_unlock_bh(hdev); 724 hci_dev_unlock_bh(hdev);
@@ -685,6 +727,73 @@ unlock:
685 return err; 727 return err;
686} 728}
687 729
730static int set_dev_class(struct sock *sk, unsigned char *data, u16 len)
731{
732 struct hci_dev *hdev;
733 struct mgmt_cp_set_dev_class *cp;
734 u16 dev_id;
735 int err;
736
737 cp = (void *) data;
738 dev_id = get_unaligned_le16(&cp->index);
739
740 BT_DBG("request for hci%u", dev_id);
741
742 hdev = hci_dev_get(dev_id);
743 if (!hdev)
744 return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV);
745
746 hci_dev_lock_bh(hdev);
747
748 hdev->major_class = cp->major;
749 hdev->minor_class = cp->minor;
750
751 err = update_class(hdev);
752
753 if (err == 0)
754 err = index_rsp(sk, MGMT_OP_SET_DEV_CLASS, dev_id);
755
756 hci_dev_unlock_bh(hdev);
757 hci_dev_put(hdev);
758
759 return err;
760}
761
762static int set_service_cache(struct sock *sk, unsigned char *data, u16 len)
763{
764 struct hci_dev *hdev;
765 struct mgmt_cp_set_service_cache *cp;
766 u16 dev_id;
767 int err;
768
769 cp = (void *) data;
770 dev_id = get_unaligned_le16(&cp->index);
771
772 hdev = hci_dev_get(dev_id);
773 if (!hdev)
774 return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
775
776 hci_dev_lock_bh(hdev);
777
778 BT_DBG("hci%u enable %d", dev_id, cp->enable);
779
780 if (cp->enable) {
781 set_bit(HCI_SERVICE_CACHE, &hdev->flags);
782 err = 0;
783 } else {
784 clear_bit(HCI_SERVICE_CACHE, &hdev->flags);
785 err = update_class(hdev);
786 }
787
788 if (err == 0)
789 err = index_rsp(sk, MGMT_OP_SET_SERVICE_CACHE, dev_id);
790
791 hci_dev_unlock_bh(hdev);
792 hci_dev_put(hdev);
793
794 return err;
795}
796
688int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) 797int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
689{ 798{
690 unsigned char *buf; 799 unsigned char *buf;
@@ -743,6 +852,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
743 case MGMT_OP_REMOVE_UUID: 852 case MGMT_OP_REMOVE_UUID:
744 err = remove_uuid(sk, buf + sizeof(*hdr), len); 853 err = remove_uuid(sk, buf + sizeof(*hdr), len);
745 break; 854 break;
855 case MGMT_OP_SET_DEV_CLASS:
856 err = set_dev_class(sk, buf + sizeof(*hdr), len);
857 break;
858 case MGMT_OP_SET_SERVICE_CACHE:
859 err = set_service_cache(sk, buf + sizeof(*hdr), len);
860 break;
746 default: 861 default:
747 BT_DBG("Unknown op %u", opcode); 862 BT_DBG("Unknown op %u", opcode);
748 err = cmd_status(sk, opcode, 0x01); 863 err = cmd_status(sk, opcode, 0x01);