summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 99ac3516fe9..9ce46cd00ba 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -81,6 +81,7 @@ enum {
HCI_AUTO_OFF,
HCI_MGMT,
HCI_PAIRABLE,
+ HCI_SERVICE_CACHE,
};
/* HCI ioctl defines */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 71a3fbf1e78..e62da084e01 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -70,6 +70,7 @@ struct bdaddr_list {
struct bt_uuid {
struct list_head list;
u8 uuid[16];
+ u8 svc_hint;
};
#define NUM_REASSEMBLY 4
@@ -86,6 +87,8 @@ struct hci_dev {
bdaddr_t bdaddr;
__u8 dev_name[248];
__u8 dev_class[3];
+ __u8 major_class;
+ __u8 minor_class;
__u8 features[8];
__u8 commands[64];
__u8 ssp_mode;
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index c118ad3af33..b092c4c014e 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -76,6 +76,7 @@ struct mgmt_mode {
struct mgmt_cp_add_uuid {
__le16 index;
__u8 uuid[16];
+ __u8 svc_hint;
} __packed;
#define MGMT_OP_REMOVE_UUID 0x000A
@@ -84,6 +85,19 @@ struct mgmt_cp_remove_uuid {
__u8 uuid[16];
} __packed;
+#define MGMT_OP_SET_DEV_CLASS 0x000B
+struct mgmt_cp_set_dev_class {
+ __le16 index;
+ __u8 major;
+ __u8 minor;
+} __packed;
+
+#define MGMT_OP_SET_SERVICE_CACHE 0x000C
+struct mgmt_cp_set_service_cache {
+ __le16 index;
+ __u8 enable;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 0854c2f1073..a08f4ce0318 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -571,7 +571,7 @@ failed:
return err;
}
-static int uuid_rsp(struct sock *sk, u16 opcode, u16 index)
+static int index_rsp(struct sock *sk, u16 opcode, u16 index)
{
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
@@ -596,6 +596,39 @@ static int uuid_rsp(struct sock *sk, u16 opcode, u16 index)
return 0;
}
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+ struct list_head *p;
+ u8 val = 0;
+
+ list_for_each(p, &hdev->uuids) {
+ struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+
+ val |= uuid->svc_hint;
+ }
+
+ return val;
+}
+
+static int update_class(struct hci_dev *hdev)
+{
+ u8 cod[3];
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+ return 0;
+
+ cod[0] = hdev->minor_class;
+ cod[1] = hdev->major_class;
+ cod[2] = get_service_classes(hdev);
+
+ if (memcmp(cod, hdev->dev_class, 3) == 0)
+ return 0;
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
+
static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
{
struct mgmt_cp_add_uuid *cp;
@@ -622,10 +655,15 @@ static int add_uuid(struct sock *sk, unsigned char *data, u16 len)
}
memcpy(uuid->uuid, cp->uuid, 16);
+ uuid->svc_hint = cp->svc_hint;
list_add(&uuid->list, &hdev->uuids);
- err = uuid_rsp(sk, MGMT_OP_ADD_UUID, dev_id);
+ err = update_class(hdev);
+ if (err < 0)
+ goto failed;
+
+ err = index_rsp(sk, MGMT_OP_ADD_UUID, dev_id);
failed:
hci_dev_unlock_bh(hdev);
@@ -676,7 +714,11 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len)
goto unlock;
}
- err = uuid_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id);
+ err = update_class(hdev);
+ if (err < 0)
+ goto unlock;
+
+ err = index_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id);
unlock:
hci_dev_unlock_bh(hdev);
@@ -685,6 +727,73 @@ unlock:
return err;
}
+static int set_dev_class(struct sock *sk, unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_set_dev_class *cp;
+ u16 dev_id;
+ int err;
+
+ cp = (void *) data;
+ dev_id = get_unaligned_le16(&cp->index);
+
+ BT_DBG("request for hci%u", dev_id);
+
+ hdev = hci_dev_get(dev_id);
+ if (!hdev)
+ return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ hdev->major_class = cp->major;
+ hdev->minor_class = cp->minor;
+
+ err = update_class(hdev);
+
+ if (err == 0)
+ err = index_rsp(sk, MGMT_OP_SET_DEV_CLASS, dev_id);
+
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int set_service_cache(struct sock *sk, unsigned char *data, u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_set_service_cache *cp;
+ u16 dev_id;
+ int err;
+
+ cp = (void *) data;
+ dev_id = get_unaligned_le16(&cp->index);
+
+ hdev = hci_dev_get(dev_id);
+ if (!hdev)
+ return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
+
+ hci_dev_lock_bh(hdev);
+
+ BT_DBG("hci%u enable %d", dev_id, cp->enable);
+
+ if (cp->enable) {
+ set_bit(HCI_SERVICE_CACHE, &hdev->flags);
+ err = 0;
+ } else {
+ clear_bit(HCI_SERVICE_CACHE, &hdev->flags);
+ err = update_class(hdev);
+ }
+
+ if (err == 0)
+ err = index_rsp(sk, MGMT_OP_SET_SERVICE_CACHE, dev_id);
+
+ hci_dev_unlock_bh(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
unsigned char *buf;
@@ -743,6 +852,12 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
case MGMT_OP_REMOVE_UUID:
err = remove_uuid(sk, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_SET_DEV_CLASS:
+ err = set_dev_class(sk, buf + sizeof(*hdr), len);
+ break;
+ case MGMT_OP_SET_SERVICE_CACHE:
+ err = set_service_cache(sk, buf + sizeof(*hdr), len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, opcode, 0x01);