aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/net/bluetooth/hci.h1
-rw-r--r--include/net/bluetooth/hci_core.h3
-rw-r--r--include/net/bluetooth/mgmt.h14
-rw-r--r--net/bluetooth/mgmt.c121
4 files changed, 136 insertions, 3 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 99ac3516fe9d..9ce46cd00ba2 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -81,6 +81,7 @@ enum {
81 HCI_AUTO_OFF, 81 HCI_AUTO_OFF,
82 HCI_MGMT, 82 HCI_MGMT,
83 HCI_PAIRABLE, 83 HCI_PAIRABLE,
84 HCI_SERVICE_CACHE,
84}; 85};
85 86
86/* HCI ioctl defines */ 87/* HCI ioctl defines */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 71a3fbf1e785..e62da084e01d 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -70,6 +70,7 @@ struct bdaddr_list {
70struct bt_uuid { 70struct bt_uuid {
71 struct list_head list; 71 struct list_head list;
72 u8 uuid[16]; 72 u8 uuid[16];
73 u8 svc_hint;
73}; 74};
74 75
75#define NUM_REASSEMBLY 4 76#define NUM_REASSEMBLY 4
@@ -86,6 +87,8 @@ struct hci_dev {
86 bdaddr_t bdaddr; 87 bdaddr_t bdaddr;
87 __u8 dev_name[248]; 88 __u8 dev_name[248];
88 __u8 dev_class[3]; 89 __u8 dev_class[3];
90 __u8 major_class;
91 __u8 minor_class;
89 __u8 features[8]; 92 __u8 features[8];
90 __u8 commands[64]; 93 __u8 commands[64];
91 __u8 ssp_mode; 94 __u8 ssp_mode;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index c118ad3af332..b092c4c014eb 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -76,6 +76,7 @@ struct mgmt_mode {
76struct mgmt_cp_add_uuid { 76struct mgmt_cp_add_uuid {
77 __le16 index; 77 __le16 index;
78 __u8 uuid[16]; 78 __u8 uuid[16];
79 __u8 svc_hint;
79} __packed; 80} __packed;
80 81
81#define MGMT_OP_REMOVE_UUID 0x000A 82#define MGMT_OP_REMOVE_UUID 0x000A
@@ -84,6 +85,19 @@ struct mgmt_cp_remove_uuid {
84 __u8 uuid[16]; 85 __u8 uuid[16];
85} __packed; 86} __packed;
86 87
88#define MGMT_OP_SET_DEV_CLASS 0x000B
89struct mgmt_cp_set_dev_class {
90 __le16 index;
91 __u8 major;
92 __u8 minor;
93} __packed;
94
95#define MGMT_OP_SET_SERVICE_CACHE 0x000C
96struct mgmt_cp_set_service_cache {
97 __le16 index;
98 __u8 enable;
99} __packed;
100
87#define MGMT_EV_CMD_COMPLETE 0x0001 101#define MGMT_EV_CMD_COMPLETE 0x0001
88struct mgmt_ev_cmd_complete { 102struct mgmt_ev_cmd_complete {
89 __le16 opcode; 103 __le16 opcode;
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);