Skip to content

Commit 1aff6f0

Browse files
Johan HedbergGustavo F. Padovan
authored andcommitted
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 <[email protected]> Signed-off-by: Gustavo F. Padovan <[email protected]>
1 parent d5859e2 commit 1aff6f0

File tree

4 files changed

+136
-3
lines changed

4 files changed

+136
-3
lines changed

include/net/bluetooth/hci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ enum {
8181
HCI_AUTO_OFF,
8282
HCI_MGMT,
8383
HCI_PAIRABLE,
84+
HCI_SERVICE_CACHE,
8485
};
8586

8687
/* HCI ioctl defines */

include/net/bluetooth/hci_core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct bdaddr_list {
7070
struct bt_uuid {
7171
struct list_head list;
7272
u8 uuid[16];
73+
u8 svc_hint;
7374
};
7475

7576
#define NUM_REASSEMBLY 4
@@ -86,6 +87,8 @@ struct hci_dev {
8687
bdaddr_t bdaddr;
8788
__u8 dev_name[248];
8889
__u8 dev_class[3];
90+
__u8 major_class;
91+
__u8 minor_class;
8992
__u8 features[8];
9093
__u8 commands[64];
9194
__u8 ssp_mode;

include/net/bluetooth/mgmt.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct mgmt_mode {
7676
struct mgmt_cp_add_uuid {
7777
__le16 index;
7878
__u8 uuid[16];
79+
__u8 svc_hint;
7980
} __packed;
8081

8182
#define MGMT_OP_REMOVE_UUID 0x000A
@@ -84,6 +85,19 @@ struct mgmt_cp_remove_uuid {
8485
__u8 uuid[16];
8586
} __packed;
8687

88+
#define MGMT_OP_SET_DEV_CLASS 0x000B
89+
struct 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
96+
struct mgmt_cp_set_service_cache {
97+
__le16 index;
98+
__u8 enable;
99+
} __packed;
100+
87101
#define MGMT_EV_CMD_COMPLETE 0x0001
88102
struct mgmt_ev_cmd_complete {
89103
__le16 opcode;

net/bluetooth/mgmt.c

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ static int set_pairable(struct sock *sk, unsigned char *data, u16 len)
571571
return err;
572572
}
573573

574-
static int uuid_rsp(struct sock *sk, u16 opcode, u16 index)
574+
static int index_rsp(struct sock *sk, u16 opcode, u16 index)
575575
{
576576
struct mgmt_hdr *hdr;
577577
struct mgmt_ev_cmd_complete *ev;
@@ -596,6 +596,39 @@ static int uuid_rsp(struct sock *sk, u16 opcode, u16 index)
596596
return 0;
597597
}
598598

599+
static 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+
613+
static 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+
599632
static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
600633
{
601634
struct mgmt_cp_add_uuid *cp;
@@ -622,10 +655,15 @@ static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
622655
}
623656

624657
memcpy(uuid->uuid, cp->uuid, 16);
658+
uuid->svc_hint = cp->svc_hint;
625659

626660
list_add(&uuid->list, &hdev->uuids);
627661

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);
629667

630668
failed:
631669
hci_dev_unlock_bh(hdev);
@@ -676,7 +714,11 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len)
676714
goto unlock;
677715
}
678716

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);
680722

681723
unlock:
682724
hci_dev_unlock_bh(hdev);
@@ -685,6 +727,73 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len)
685727
return err;
686728
}
687729

730+
static 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+
762+
static 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+
688797
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
689798
{
690799
unsigned char *buf;
@@ -743,6 +852,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
743852
case MGMT_OP_REMOVE_UUID:
744853
err = remove_uuid(sk, buf + sizeof(*hdr), len);
745854
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;
746861
default:
747862
BT_DBG("Unknown op %u", opcode);
748863
err = cmd_status(sk, opcode, 0x01);

0 commit comments

Comments
 (0)