|
@@ -35,7 +35,7 @@
|
|
|
bool enable_hs;
|
|
|
|
|
|
#define MGMT_VERSION 1
|
|
|
-#define MGMT_REVISION 2
|
|
|
+#define MGMT_REVISION 3
|
|
|
|
|
|
static const u16 mgmt_commands[] = {
|
|
|
MGMT_OP_READ_INDEX_LIST,
|
|
@@ -435,35 +435,117 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
|
|
|
|
|
#define PNP_INFO_SVCLASS_ID 0x1200
|
|
|
|
|
|
-static u8 bluetooth_base_uuid[] = {
|
|
|
- 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
|
|
|
- 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
-};
|
|
|
+static u8 *create_uuid16_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
|
|
|
+{
|
|
|
+ u8 *ptr = data, *uuids_start = NULL;
|
|
|
+ struct bt_uuid *uuid;
|
|
|
+
|
|
|
+ if (len < 4)
|
|
|
+ return ptr;
|
|
|
+
|
|
|
+ list_for_each_entry(uuid, &hdev->uuids, list) {
|
|
|
+ u16 uuid16;
|
|
|
+
|
|
|
+ if (uuid->size != 16)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ uuid16 = get_unaligned_le16(&uuid->uuid[12]);
|
|
|
+ if (uuid16 < 0x1100)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (uuid16 == PNP_INFO_SVCLASS_ID)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!uuids_start) {
|
|
|
+ uuids_start = ptr;
|
|
|
+ uuids_start[0] = 1;
|
|
|
+ uuids_start[1] = EIR_UUID16_ALL;
|
|
|
+ ptr += 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Stop if not enough space to put next UUID */
|
|
|
+ if ((ptr - data) + sizeof(u16) > len) {
|
|
|
+ uuids_start[1] = EIR_UUID16_SOME;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ *ptr++ = (uuid16 & 0x00ff);
|
|
|
+ *ptr++ = (uuid16 & 0xff00) >> 8;
|
|
|
+ uuids_start[0] += sizeof(uuid16);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
|
|
|
-static u16 get_uuid16(u8 *uuid128)
|
|
|
+static u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
|
|
|
{
|
|
|
- u32 val;
|
|
|
- int i;
|
|
|
+ u8 *ptr = data, *uuids_start = NULL;
|
|
|
+ struct bt_uuid *uuid;
|
|
|
+
|
|
|
+ if (len < 6)
|
|
|
+ return ptr;
|
|
|
+
|
|
|
+ list_for_each_entry(uuid, &hdev->uuids, list) {
|
|
|
+ if (uuid->size != 32)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!uuids_start) {
|
|
|
+ uuids_start = ptr;
|
|
|
+ uuids_start[0] = 1;
|
|
|
+ uuids_start[1] = EIR_UUID32_ALL;
|
|
|
+ ptr += 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Stop if not enough space to put next UUID */
|
|
|
+ if ((ptr - data) + sizeof(u32) > len) {
|
|
|
+ uuids_start[1] = EIR_UUID32_SOME;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- for (i = 0; i < 12; i++) {
|
|
|
- if (bluetooth_base_uuid[i] != uuid128[i])
|
|
|
- return 0;
|
|
|
+ memcpy(ptr, &uuid->uuid[12], sizeof(u32));
|
|
|
+ ptr += sizeof(u32);
|
|
|
+ uuids_start[0] += sizeof(u32);
|
|
|
}
|
|
|
|
|
|
- val = get_unaligned_le32(&uuid128[12]);
|
|
|
- if (val > 0xffff)
|
|
|
- return 0;
|
|
|
+ return ptr;
|
|
|
+}
|
|
|
+
|
|
|
+static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len)
|
|
|
+{
|
|
|
+ u8 *ptr = data, *uuids_start = NULL;
|
|
|
+ struct bt_uuid *uuid;
|
|
|
+
|
|
|
+ if (len < 18)
|
|
|
+ return ptr;
|
|
|
+
|
|
|
+ list_for_each_entry(uuid, &hdev->uuids, list) {
|
|
|
+ if (uuid->size != 128)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!uuids_start) {
|
|
|
+ uuids_start = ptr;
|
|
|
+ uuids_start[0] = 1;
|
|
|
+ uuids_start[1] = EIR_UUID128_ALL;
|
|
|
+ ptr += 2;
|
|
|
+ }
|
|
|
|
|
|
- return (u16) val;
|
|
|
+ /* Stop if not enough space to put next UUID */
|
|
|
+ if ((ptr - data) + 16 > len) {
|
|
|
+ uuids_start[1] = EIR_UUID128_SOME;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(ptr, uuid->uuid, 16);
|
|
|
+ ptr += 16;
|
|
|
+ uuids_start[0] += 16;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ptr;
|
|
|
}
|
|
|
|
|
|
static void create_eir(struct hci_dev *hdev, u8 *data)
|
|
|
{
|
|
|
u8 *ptr = data;
|
|
|
- u16 eir_len = 0;
|
|
|
- u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
|
|
|
- int i, truncated = 0;
|
|
|
- struct bt_uuid *uuid;
|
|
|
size_t name_len;
|
|
|
|
|
|
name_len = strlen(hdev->dev_name);
|
|
@@ -481,7 +563,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
|
|
|
|
|
|
memcpy(ptr + 2, hdev->dev_name, name_len);
|
|
|
|
|
|
- eir_len += (name_len + 2);
|
|
|
ptr += (name_len + 2);
|
|
|
}
|
|
|
|
|
@@ -490,7 +571,6 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
|
|
|
ptr[1] = EIR_TX_POWER;
|
|
|
ptr[2] = (u8) hdev->inq_tx_power;
|
|
|
|
|
|
- eir_len += 3;
|
|
|
ptr += 3;
|
|
|
}
|
|
|
|
|
@@ -503,60 +583,12 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
|
|
|
put_unaligned_le16(hdev->devid_product, ptr + 6);
|
|
|
put_unaligned_le16(hdev->devid_version, ptr + 8);
|
|
|
|
|
|
- eir_len += 10;
|
|
|
ptr += 10;
|
|
|
}
|
|
|
|
|
|
- memset(uuid16_list, 0, sizeof(uuid16_list));
|
|
|
-
|
|
|
- /* Group all UUID16 types */
|
|
|
- list_for_each_entry(uuid, &hdev->uuids, list) {
|
|
|
- u16 uuid16;
|
|
|
-
|
|
|
- uuid16 = get_uuid16(uuid->uuid);
|
|
|
- if (uuid16 == 0)
|
|
|
- return;
|
|
|
-
|
|
|
- if (uuid16 < 0x1100)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (uuid16 == PNP_INFO_SVCLASS_ID)
|
|
|
- continue;
|
|
|
-
|
|
|
- /* Stop if not enough space to put next UUID */
|
|
|
- if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
|
|
|
- truncated = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check for duplicates */
|
|
|
- for (i = 0; uuid16_list[i] != 0; i++)
|
|
|
- if (uuid16_list[i] == uuid16)
|
|
|
- break;
|
|
|
-
|
|
|
- if (uuid16_list[i] == 0) {
|
|
|
- uuid16_list[i] = uuid16;
|
|
|
- eir_len += sizeof(u16);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (uuid16_list[0] != 0) {
|
|
|
- u8 *length = ptr;
|
|
|
-
|
|
|
- /* EIR Data type */
|
|
|
- ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
|
|
|
-
|
|
|
- ptr += 2;
|
|
|
- eir_len += 2;
|
|
|
-
|
|
|
- for (i = 0; uuid16_list[i] != 0; i++) {
|
|
|
- *ptr++ = (uuid16_list[i] & 0x00ff);
|
|
|
- *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
|
|
|
- }
|
|
|
-
|
|
|
- /* EIR Data length */
|
|
|
- *length = (i * sizeof(u16)) + 1;
|
|
|
- }
|
|
|
+ ptr = create_uuid16_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
|
|
+ ptr = create_uuid32_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
|
|
+ ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
|
|
}
|
|
|
|
|
|
static int update_eir(struct hci_dev *hdev)
|
|
@@ -728,13 +760,9 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
|
|
|
void *data),
|
|
|
void *data)
|
|
|
{
|
|
|
- struct list_head *p, *n;
|
|
|
-
|
|
|
- list_for_each_safe(p, n, &hdev->mgmt_pending) {
|
|
|
- struct pending_cmd *cmd;
|
|
|
-
|
|
|
- cmd = list_entry(p, struct pending_cmd, list);
|
|
|
+ struct pending_cmd *cmd, *tmp;
|
|
|
|
|
|
+ list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
|
|
|
if (opcode > 0 && cmd->opcode != opcode)
|
|
|
continue;
|
|
|
|
|
@@ -1304,6 +1332,25 @@ unlock:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static const u8 bluetooth_base_uuid[] = {
|
|
|
+ 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
|
|
|
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
+};
|
|
|
+
|
|
|
+static u8 get_uuid_size(const u8 *uuid)
|
|
|
+{
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (memcmp(uuid, bluetooth_base_uuid, 12))
|
|
|
+ return 128;
|
|
|
+
|
|
|
+ val = get_unaligned_le32(&uuid[12]);
|
|
|
+ if (val > 0xffff)
|
|
|
+ return 32;
|
|
|
+
|
|
|
+ return 16;
|
|
|
+}
|
|
|
+
|
|
|
static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|
|
{
|
|
|
struct mgmt_cp_add_uuid *cp = data;
|
|
@@ -1329,8 +1376,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|
|
|
|
|
memcpy(uuid->uuid, cp->uuid, 16);
|
|
|
uuid->svc_hint = cp->svc_hint;
|
|
|
+ uuid->size = get_uuid_size(cp->uuid);
|
|
|
|
|
|
- list_add(&uuid->list, &hdev->uuids);
|
|
|
+ list_add_tail(&uuid->list, &hdev->uuids);
|
|
|
|
|
|
err = update_class(hdev);
|
|
|
if (err < 0)
|
|
@@ -1374,7 +1422,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
|
|
|
{
|
|
|
struct mgmt_cp_remove_uuid *cp = data;
|
|
|
struct pending_cmd *cmd;
|
|
|
- struct list_head *p, *n;
|
|
|
+ struct bt_uuid *match, *tmp;
|
|
|
u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
int err, found;
|
|
|
|
|
@@ -1402,9 +1450,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
|
|
|
|
|
|
found = 0;
|
|
|
|
|
|
- list_for_each_safe(p, n, &hdev->uuids) {
|
|
|
- struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
|
|
|
-
|
|
|
+ list_for_each_entry_safe(match, tmp, &hdev->uuids, list) {
|
|
|
if (memcmp(match->uuid, cp->uuid, 16) != 0)
|
|
|
continue;
|
|
|
|
|
@@ -3023,6 +3069,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|
|
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
|
|
|
|
|
|
if (powered) {
|
|
|
+ u8 link_sec;
|
|
|
+
|
|
|
if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
|
|
|
!lmp_host_ssp_capable(hdev)) {
|
|
|
u8 ssp = 1;
|
|
@@ -3046,6 +3094,11 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
|
|
|
sizeof(cp), &cp);
|
|
|
}
|
|
|
|
|
|
+ link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
|
|
|
+ if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
|
|
|
+ hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE,
|
|
|
+ sizeof(link_sec), &link_sec);
|
|
|
+
|
|
|
if (lmp_bredr_capable(hdev)) {
|
|
|
set_bredr_scan(hdev);
|
|
|
update_class(hdev);
|