Message ID | 20210520164213.1381518-2-luiz.dentz@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [1/2] Bluetooth: Add helper for serialized HCI command execution | expand |
Hi Luiz, > This make use of hci_cmd_sync_queue for the following MGMT commands: > > Set Device Class > Set Device ID > Add UUID > Remove UUID > > Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> > --- > include/net/bluetooth/hci_core.h | 2 + > net/bluetooth/Makefile | 2 +- > net/bluetooth/hci_request.c | 18 +++ > net/bluetooth/hci_sync.c | 241 +++++++++++++++++++++++++++++++ > net/bluetooth/hci_sync.h | 9 ++ > net/bluetooth/mgmt.c | 123 ++++++++-------- > 6 files changed, 328 insertions(+), 67 deletions(-) > create mode 100644 net/bluetooth/hci_sync.c > create mode 100644 net/bluetooth/hci_sync.h I was thinking to put things into hci_bredr.c and hci_le.c. > > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h > index aecbdf99c216..2494c547a622 100644 > --- a/include/net/bluetooth/hci_core.h > +++ b/include/net/bluetooth/hci_core.h > @@ -1699,6 +1699,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, > const void *param, u32 timeout); > struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, > const void *param, u8 event, u32 timeout); > +int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, > + const void *param, u32 timeout); > int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen, > const void *param); > > diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile > index cc0995301f93..ab46b2b4f3cb 100644 > --- a/net/bluetooth/Makefile > +++ b/net/bluetooth/Makefile > @@ -14,7 +14,7 @@ bluetooth_6lowpan-y := 6lowpan.o > > bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ > hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \ > - ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o > + ecdh_helper.o hci_request.o hci_sync.o mgmt_util.o mgmt_config.o > > bluetooth-$(CONFIG_BT_BREDR) += sco.o > bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o > diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c > index fa9125b782f8..f390ac33ced9 100644 > --- a/net/bluetooth/hci_request.c > +++ b/net/bluetooth/hci_request.c > @@ -189,6 +189,24 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, > } > EXPORT_SYMBOL(__hci_cmd_sync); > > +int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, > + const void *param, u32 timeout) > +{ > + struct sk_buff *skb; > + uint8_t status; Use u8 here since that is not user space. And drop the timeout parameter. We always use the same anyway. > + > + skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout); > + if (IS_ERR(skb)) > + return PTR_ERR(skb); > + > + status = skb->data[0]; > + > + kfree_skb(skb); > + > + return status; > +} > +EXPORT_SYMBOL(__hci_cmd_sync_status); Either we use u8 as return value or we convert it to the matching errno value. > + > /* Execute request and wait for completion. */ > int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req, > unsigned long opt), > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c > new file mode 100644 > index 000000000000..5b73fcf09c18 > --- /dev/null > +++ b/net/bluetooth/hci_sync.c > @@ -0,0 +1,241 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2021 Intel Corporation > + */ > + > +#include <net/bluetooth/bluetooth.h> > +#include <net/bluetooth/hci_core.h> > + > +#include "hci_request.h" > +#include "hci_sync.h" > + > +#define PNP_INFO_SVCLASS_ID 0x1200 > + > +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 u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) > +{ > + 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; > + } > + > + memcpy(ptr, &uuid->uuid[12], sizeof(u32)); > + ptr += sizeof(u32); > + uuids_start[0] += sizeof(u32); > + } > + > + 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; > + } > + > + /* 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; > + size_t name_len; > + > + name_len = strlen(hdev->dev_name); > + > + if (name_len > 0) { > + /* EIR Data type */ > + if (name_len > 48) { > + name_len = 48; > + ptr[1] = EIR_NAME_SHORT; > + } else > + ptr[1] = EIR_NAME_COMPLETE; > + > + /* EIR Data length */ > + ptr[0] = name_len + 1; > + > + memcpy(ptr + 2, hdev->dev_name, name_len); > + > + ptr += (name_len + 2); > + } > + > + if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) { > + ptr[0] = 2; > + ptr[1] = EIR_TX_POWER; > + ptr[2] = (u8) hdev->inq_tx_power; > + > + ptr += 3; > + } > + > + if (hdev->devid_source > 0) { > + ptr[0] = 9; > + ptr[1] = EIR_DEVICE_ID; > + > + put_unaligned_le16(hdev->devid_source, ptr + 2); > + put_unaligned_le16(hdev->devid_vendor, ptr + 4); > + put_unaligned_le16(hdev->devid_product, ptr + 6); > + put_unaligned_le16(hdev->devid_version, ptr + 8); > + > + ptr += 10; > + } > + > + 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)); > +} > + > +int __hci_update_eir_sync(struct hci_dev *hdev) > +{ > + struct hci_cp_write_eir cp; > + > + bt_dev_dbg(hdev, ""); > + > + if (!hdev_is_powered(hdev)) > + return 0; > + > + if (!lmp_ext_inq_capable(hdev)) > + return 0; > + > + if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) > + return 0; > + > + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) > + return 0; > + > + memset(&cp, 0, sizeof(cp)); > + > + create_eir(hdev, cp.data); > + > + if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) > + return 0; > + > + memcpy(hdev->eir, cp.data, sizeof(cp.data)); > + > + return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp, > + HCI_CMD_TIMEOUT); > +} > + > +static u8 get_service_classes(struct hci_dev *hdev) > +{ > + struct bt_uuid *uuid; > + u8 val = 0; > + > + list_for_each_entry(uuid, &hdev->uuids, list) > + val |= uuid->svc_hint; > + > + return val; > +} > + > +int __hci_update_class_sync(struct hci_dev *hdev) > +{ > + u8 cod[3]; > + > + bt_dev_dbg(hdev, ""); > + > + if (!hdev_is_powered(hdev)) > + return 0; > + > + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) > + return 0; > + > + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) > + return 0; > + > + cod[0] = hdev->minor_class; > + cod[1] = hdev->major_class; > + cod[2] = get_service_classes(hdev); > + > + if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) > + cod[1] |= 0x20; > + > + if (memcmp(cod, hdev->dev_class, 3) == 0) > + return 0; > + > + return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV, > + sizeof(cod), cod, HCI_CMD_TIMEOUT); > +} > diff --git a/net/bluetooth/hci_sync.h b/net/bluetooth/hci_sync.h > new file mode 100644 > index 000000000000..735ff4b46e86 > --- /dev/null > +++ b/net/bluetooth/hci_sync.h > @@ -0,0 +1,9 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2021 Intel Corporation > + */ > + > +int __hci_update_eir_sync(struct hci_dev *hdev); > +int __hci_update_class_sync(struct hci_dev *hdev); > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c > index b44e19c69c44..96759c166678 100644 > --- a/net/bluetooth/mgmt.c > +++ b/net/bluetooth/mgmt.c > @@ -34,6 +34,7 @@ > #include <net/bluetooth/mgmt.h> > > #include "hci_request.h" > +#include "hci_sync.h" > #include "smp.h" > #include "mgmt_util.h" > #include "mgmt_config.h" > @@ -950,25 +951,21 @@ bool mgmt_get_connectable(struct hci_dev *hdev) > return hci_dev_test_flag(hdev, HCI_CONNECTABLE); > } > > +static void __service_cache_sync(struct hci_dev *hdev, void *data) > +{ > + __hci_update_eir_sync(hdev); > + __hci_update_class_sync(hdev); > +} > + > static void service_cache_off(struct work_struct *work) > { > struct hci_dev *hdev = container_of(work, struct hci_dev, > service_cache.work); > - struct hci_request req; > > if (!hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) > return; > > - hci_req_init(&req, hdev); > - > - hci_dev_lock(hdev); > - > - __hci_req_update_eir(&req); > - __hci_req_update_class(&req); > - > - hci_dev_unlock(hdev); > - > - hci_req_run(&req, NULL); > + hci_cmd_sync_queue(hdev, __service_cache_sync, NULL); > } > > static void rpa_expired(struct work_struct *work) > @@ -2093,8 +2090,17 @@ static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status) > hci_dev_unlock(hdev); > } > > -static void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) > +static void __add_uuid_sync(struct hci_dev *hdev, void *data) > { > + uint8_t status; > + > + status = __hci_update_class_sync(hdev); > + if (status) > + goto done; > + > + status = __hci_update_eir_sync(hdev); > + > +done: > bt_dev_dbg(hdev, "status 0x%02x", status); > > mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status); > @@ -2104,7 +2110,6 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) > { > struct mgmt_cp_add_uuid *cp = data; > struct mgmt_pending_cmd *cmd; > - struct hci_request req; > struct bt_uuid *uuid; > int err; > > @@ -2130,20 +2135,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) > > list_add_tail(&uuid->list, &hdev->uuids); > > - hci_req_init(&req, hdev); > - > - __hci_req_update_class(&req); > - __hci_req_update_eir(&req); > - > - err = hci_req_run(&req, add_uuid_complete); > - if (err < 0) { > - if (err != -ENODATA) > - goto failed; > - > - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, > - hdev->dev_class, 3); > + err = hci_cmd_sync_queue(hdev, __add_uuid_sync, NULL); > + if (err < 0) > goto failed; > - } > > cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); > if (!cmd) { > @@ -2172,8 +2166,17 @@ static bool enable_service_cache(struct hci_dev *hdev) > return false; > } > > -static void remove_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) > +static void __remove_uuid_sync(struct hci_dev *hdev, void *data) > { > + uint8_t status; > + > + status = __hci_update_class_sync(hdev); > + if (status) > + goto done; > + > + status = __hci_update_eir_sync(hdev); > + > +done: > bt_dev_dbg(hdev, "status 0x%02x", status); > > mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status); > @@ -2186,7 +2189,6 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, > struct mgmt_pending_cmd *cmd; > 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 }; > - struct hci_request req; > int err, found; > > bt_dev_dbg(hdev, "sock %p", sk); > @@ -2230,20 +2232,9 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, > } > > update_class: > - hci_req_init(&req, hdev); > - > - __hci_req_update_class(&req); > - __hci_req_update_eir(&req); > - > - err = hci_req_run(&req, remove_uuid_complete); > - if (err < 0) { > - if (err != -ENODATA) > - goto unlock; > - > - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, > - hdev->dev_class, 3); > + err = hci_cmd_sync_queue(hdev, __remove_uuid_sync, NULL); > + if (err < 0) > goto unlock; > - } > > cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); > if (!cmd) { > @@ -2258,8 +2249,24 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, > return err; > } > > -static void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode) > +static void __set_class_sync(struct hci_dev *hdev, void *data) > { > + uint8_t status = 0; > + > + hci_dev_lock(hdev); > + > + if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { > + hci_dev_unlock(hdev); > + cancel_delayed_work_sync(&hdev->service_cache); > + hci_dev_lock(hdev); > + status = __hci_update_eir_sync(hdev); > + } > + > + hci_dev_unlock(hdev); > + > + if (!status) > + status = __hci_update_class_sync(hdev); > + > bt_dev_dbg(hdev, "status 0x%02x", status); > > mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status); > @@ -2270,7 +2277,6 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, > { > struct mgmt_cp_set_dev_class *cp = data; > struct mgmt_pending_cmd *cmd; > - struct hci_request req; > int err; > > bt_dev_dbg(hdev, "sock %p", sk); > @@ -2302,26 +2308,9 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, > goto unlock; > } > > - hci_req_init(&req, hdev); > - > - if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { > - hci_dev_unlock(hdev); > - cancel_delayed_work_sync(&hdev->service_cache); > - hci_dev_lock(hdev); > - __hci_req_update_eir(&req); > - } > - > - __hci_req_update_class(&req); > - > - err = hci_req_run(&req, set_class_complete); > - if (err < 0) { > - if (err != -ENODATA) > - goto unlock; > - > - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, > - hdev->dev_class, 3); > + err = hci_cmd_sync_queue(hdev, __set_class_sync, NULL); > + if (err < 0) > goto unlock; > - } > > cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); > if (!cmd) { > @@ -5263,11 +5252,15 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, > return err; > } > > +static void __set_device_id_sync(struct hci_dev *hdev, void *data) > +{ > + __hci_update_eir_sync(hdev); > +} > + > static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, > u16 len) > { > struct mgmt_cp_set_device_id *cp = data; > - struct hci_request req; > int err; > __u16 source; > > @@ -5289,9 +5282,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, > err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, > NULL, 0); > > - hci_req_init(&req, hdev); > - __hci_req_update_eir(&req); > - hci_req_run(&req, NULL); > + hci_cmd_sync_queue(hdev, __set_device_id_sync, NULL); > > hci_dev_unlock(hdev); I dislike the prefix __sync. They are in a separate anyway. So keep is simple and also scrap the __ prefix. Regards Marcel
Hi Marcel, On Thu, May 20, 2021 at 1:17 PM Marcel Holtmann <marcel@holtmann.org> wrote: > > Hi Luiz, > > > This make use of hci_cmd_sync_queue for the following MGMT commands: > > > > Set Device Class > > Set Device ID > > Add UUID > > Remove UUID > > > > Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> > > --- > > include/net/bluetooth/hci_core.h | 2 + > > net/bluetooth/Makefile | 2 +- > > net/bluetooth/hci_request.c | 18 +++ > > net/bluetooth/hci_sync.c | 241 +++++++++++++++++++++++++++++++ > > net/bluetooth/hci_sync.h | 9 ++ > > net/bluetooth/mgmt.c | 123 ++++++++-------- > > 6 files changed, 328 insertions(+), 67 deletions(-) > > create mode 100644 net/bluetooth/hci_sync.c > > create mode 100644 net/bluetooth/hci_sync.h > > I was thinking to put things into hci_bredr.c and hci_le.c. Initially I thought about that but I was afraid of things that could affect states in both bearers so I went with what I considered to be simpler. > > > > diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h > > index aecbdf99c216..2494c547a622 100644 > > --- a/include/net/bluetooth/hci_core.h > > +++ b/include/net/bluetooth/hci_core.h > > @@ -1699,6 +1699,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, > > const void *param, u32 timeout); > > struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, > > const void *param, u8 event, u32 timeout); > > +int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, > > + const void *param, u32 timeout); > > int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen, > > const void *param); > > > > diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile > > index cc0995301f93..ab46b2b4f3cb 100644 > > --- a/net/bluetooth/Makefile > > +++ b/net/bluetooth/Makefile > > @@ -14,7 +14,7 @@ bluetooth_6lowpan-y := 6lowpan.o > > > > bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ > > hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \ > > - ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o > > + ecdh_helper.o hci_request.o hci_sync.o mgmt_util.o mgmt_config.o > > > > bluetooth-$(CONFIG_BT_BREDR) += sco.o > > bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o > > diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c > > index fa9125b782f8..f390ac33ced9 100644 > > --- a/net/bluetooth/hci_request.c > > +++ b/net/bluetooth/hci_request.c > > @@ -189,6 +189,24 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, > > } > > EXPORT_SYMBOL(__hci_cmd_sync); > > > > +int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, > > + const void *param, u32 timeout) > > +{ > > + struct sk_buff *skb; > > + uint8_t status; > > Use u8 here since that is not user space. And drop the timeout parameter. We always use the same anyway. Yep, I fixed that already. > > + > > + skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout); > > + if (IS_ERR(skb)) > > + return PTR_ERR(skb); > > + > > + status = skb->data[0]; > > + > > + kfree_skb(skb); > > + > > + return status; > > +} > > +EXPORT_SYMBOL(__hci_cmd_sync_status); > > Either we use u8 as return value or we convert it to the matching errno value. Ok, I was thinking of converting but it looks like the mgmt can actually report the status, that said we may need to convert the PTR_ERR then. > > + > > /* Execute request and wait for completion. */ > > int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req, > > unsigned long opt), > > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c > > new file mode 100644 > > index 000000000000..5b73fcf09c18 > > --- /dev/null > > +++ b/net/bluetooth/hci_sync.c > > @@ -0,0 +1,241 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * BlueZ - Bluetooth protocol stack for Linux > > + * > > + * Copyright (C) 2021 Intel Corporation > > + */ > > + > > +#include <net/bluetooth/bluetooth.h> > > +#include <net/bluetooth/hci_core.h> > > + > > +#include "hci_request.h" > > +#include "hci_sync.h" > > + > > +#define PNP_INFO_SVCLASS_ID 0x1200 > > + > > +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 u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) > > +{ > > + 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; > > + } > > + > > + memcpy(ptr, &uuid->uuid[12], sizeof(u32)); > > + ptr += sizeof(u32); > > + uuids_start[0] += sizeof(u32); > > + } > > + > > + 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; > > + } > > + > > + /* 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; > > + size_t name_len; > > + > > + name_len = strlen(hdev->dev_name); > > + > > + if (name_len > 0) { > > + /* EIR Data type */ > > + if (name_len > 48) { > > + name_len = 48; > > + ptr[1] = EIR_NAME_SHORT; > > + } else > > + ptr[1] = EIR_NAME_COMPLETE; > > + > > + /* EIR Data length */ > > + ptr[0] = name_len + 1; > > + > > + memcpy(ptr + 2, hdev->dev_name, name_len); > > + > > + ptr += (name_len + 2); > > + } > > + > > + if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) { > > + ptr[0] = 2; > > + ptr[1] = EIR_TX_POWER; > > + ptr[2] = (u8) hdev->inq_tx_power; > > + > > + ptr += 3; > > + } > > + > > + if (hdev->devid_source > 0) { > > + ptr[0] = 9; > > + ptr[1] = EIR_DEVICE_ID; > > + > > + put_unaligned_le16(hdev->devid_source, ptr + 2); > > + put_unaligned_le16(hdev->devid_vendor, ptr + 4); > > + put_unaligned_le16(hdev->devid_product, ptr + 6); > > + put_unaligned_le16(hdev->devid_version, ptr + 8); > > + > > + ptr += 10; > > + } > > + > > + 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)); > > +} > > + > > +int __hci_update_eir_sync(struct hci_dev *hdev) > > +{ > > + struct hci_cp_write_eir cp; > > + > > + bt_dev_dbg(hdev, ""); > > + > > + if (!hdev_is_powered(hdev)) > > + return 0; > > + > > + if (!lmp_ext_inq_capable(hdev)) > > + return 0; > > + > > + if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) > > + return 0; > > + > > + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) > > + return 0; > > + > > + memset(&cp, 0, sizeof(cp)); > > + > > + create_eir(hdev, cp.data); > > + > > + if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) > > + return 0; > > + > > + memcpy(hdev->eir, cp.data, sizeof(cp.data)); > > + > > + return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp, > > + HCI_CMD_TIMEOUT); > > +} > > + > > +static u8 get_service_classes(struct hci_dev *hdev) > > +{ > > + struct bt_uuid *uuid; > > + u8 val = 0; > > + > > + list_for_each_entry(uuid, &hdev->uuids, list) > > + val |= uuid->svc_hint; > > + > > + return val; > > +} > > + > > +int __hci_update_class_sync(struct hci_dev *hdev) > > +{ > > + u8 cod[3]; > > + > > + bt_dev_dbg(hdev, ""); > > + > > + if (!hdev_is_powered(hdev)) > > + return 0; > > + > > + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) > > + return 0; > > + > > + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) > > + return 0; > > + > > + cod[0] = hdev->minor_class; > > + cod[1] = hdev->major_class; > > + cod[2] = get_service_classes(hdev); > > + > > + if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) > > + cod[1] |= 0x20; > > + > > + if (memcmp(cod, hdev->dev_class, 3) == 0) > > + return 0; > > + > > + return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV, > > + sizeof(cod), cod, HCI_CMD_TIMEOUT); > > +} > > diff --git a/net/bluetooth/hci_sync.h b/net/bluetooth/hci_sync.h > > new file mode 100644 > > index 000000000000..735ff4b46e86 > > --- /dev/null > > +++ b/net/bluetooth/hci_sync.h > > @@ -0,0 +1,9 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * BlueZ - Bluetooth protocol stack for Linux > > + * > > + * Copyright (C) 2021 Intel Corporation > > + */ > > + > > +int __hci_update_eir_sync(struct hci_dev *hdev); > > +int __hci_update_class_sync(struct hci_dev *hdev); > > diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c > > index b44e19c69c44..96759c166678 100644 > > --- a/net/bluetooth/mgmt.c > > +++ b/net/bluetooth/mgmt.c > > @@ -34,6 +34,7 @@ > > #include <net/bluetooth/mgmt.h> > > > > #include "hci_request.h" > > +#include "hci_sync.h" > > #include "smp.h" > > #include "mgmt_util.h" > > #include "mgmt_config.h" > > @@ -950,25 +951,21 @@ bool mgmt_get_connectable(struct hci_dev *hdev) > > return hci_dev_test_flag(hdev, HCI_CONNECTABLE); > > } > > > > +static void __service_cache_sync(struct hci_dev *hdev, void *data) > > +{ > > + __hci_update_eir_sync(hdev); > > + __hci_update_class_sync(hdev); > > +} > > + > > static void service_cache_off(struct work_struct *work) > > { > > struct hci_dev *hdev = container_of(work, struct hci_dev, > > service_cache.work); > > - struct hci_request req; > > > > if (!hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) > > return; > > > > - hci_req_init(&req, hdev); > > - > > - hci_dev_lock(hdev); > > - > > - __hci_req_update_eir(&req); > > - __hci_req_update_class(&req); > > - > > - hci_dev_unlock(hdev); > > - > > - hci_req_run(&req, NULL); > > + hci_cmd_sync_queue(hdev, __service_cache_sync, NULL); > > } > > > > static void rpa_expired(struct work_struct *work) > > @@ -2093,8 +2090,17 @@ static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status) > > hci_dev_unlock(hdev); > > } > > > > -static void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) > > +static void __add_uuid_sync(struct hci_dev *hdev, void *data) > > { > > + uint8_t status; > > + > > + status = __hci_update_class_sync(hdev); > > + if (status) > > + goto done; > > + > > + status = __hci_update_eir_sync(hdev); > > + > > +done: > > bt_dev_dbg(hdev, "status 0x%02x", status); > > > > mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status); > > @@ -2104,7 +2110,6 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) > > { > > struct mgmt_cp_add_uuid *cp = data; > > struct mgmt_pending_cmd *cmd; > > - struct hci_request req; > > struct bt_uuid *uuid; > > int err; > > > > @@ -2130,20 +2135,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) > > > > list_add_tail(&uuid->list, &hdev->uuids); > > > > - hci_req_init(&req, hdev); > > - > > - __hci_req_update_class(&req); > > - __hci_req_update_eir(&req); > > - > > - err = hci_req_run(&req, add_uuid_complete); > > - if (err < 0) { > > - if (err != -ENODATA) > > - goto failed; > > - > > - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, > > - hdev->dev_class, 3); > > + err = hci_cmd_sync_queue(hdev, __add_uuid_sync, NULL); > > + if (err < 0) > > goto failed; > > - } > > > > cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); > > if (!cmd) { > > @@ -2172,8 +2166,17 @@ static bool enable_service_cache(struct hci_dev *hdev) > > return false; > > } > > > > -static void remove_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) > > +static void __remove_uuid_sync(struct hci_dev *hdev, void *data) > > { > > + uint8_t status; > > + > > + status = __hci_update_class_sync(hdev); > > + if (status) > > + goto done; > > + > > + status = __hci_update_eir_sync(hdev); > > + > > +done: > > bt_dev_dbg(hdev, "status 0x%02x", status); > > > > mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status); > > @@ -2186,7 +2189,6 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, > > struct mgmt_pending_cmd *cmd; > > 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 }; > > - struct hci_request req; > > int err, found; > > > > bt_dev_dbg(hdev, "sock %p", sk); > > @@ -2230,20 +2232,9 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, > > } > > > > update_class: > > - hci_req_init(&req, hdev); > > - > > - __hci_req_update_class(&req); > > - __hci_req_update_eir(&req); > > - > > - err = hci_req_run(&req, remove_uuid_complete); > > - if (err < 0) { > > - if (err != -ENODATA) > > - goto unlock; > > - > > - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, > > - hdev->dev_class, 3); > > + err = hci_cmd_sync_queue(hdev, __remove_uuid_sync, NULL); > > + if (err < 0) > > goto unlock; > > - } > > > > cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); > > if (!cmd) { > > @@ -2258,8 +2249,24 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, > > return err; > > } > > > > -static void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode) > > +static void __set_class_sync(struct hci_dev *hdev, void *data) > > { > > + uint8_t status = 0; > > + > > + hci_dev_lock(hdev); > > + > > + if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { > > + hci_dev_unlock(hdev); > > + cancel_delayed_work_sync(&hdev->service_cache); > > + hci_dev_lock(hdev); > > + status = __hci_update_eir_sync(hdev); > > + } > > + > > + hci_dev_unlock(hdev); > > + > > + if (!status) > > + status = __hci_update_class_sync(hdev); > > + > > bt_dev_dbg(hdev, "status 0x%02x", status); > > > > mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status); > > @@ -2270,7 +2277,6 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, > > { > > struct mgmt_cp_set_dev_class *cp = data; > > struct mgmt_pending_cmd *cmd; > > - struct hci_request req; > > int err; > > > > bt_dev_dbg(hdev, "sock %p", sk); > > @@ -2302,26 +2308,9 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, > > goto unlock; > > } > > > > - hci_req_init(&req, hdev); > > - > > - if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { > > - hci_dev_unlock(hdev); > > - cancel_delayed_work_sync(&hdev->service_cache); > > - hci_dev_lock(hdev); > > - __hci_req_update_eir(&req); > > - } > > - > > - __hci_req_update_class(&req); > > - > > - err = hci_req_run(&req, set_class_complete); > > - if (err < 0) { > > - if (err != -ENODATA) > > - goto unlock; > > - > > - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, > > - hdev->dev_class, 3); > > + err = hci_cmd_sync_queue(hdev, __set_class_sync, NULL); > > + if (err < 0) > > goto unlock; > > - } > > > > cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); > > if (!cmd) { > > @@ -5263,11 +5252,15 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, > > return err; > > } > > > > +static void __set_device_id_sync(struct hci_dev *hdev, void *data) > > +{ > > + __hci_update_eir_sync(hdev); > > +} > > + > > static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, > > u16 len) > > { > > struct mgmt_cp_set_device_id *cp = data; > > - struct hci_request req; > > int err; > > __u16 source; > > > > @@ -5289,9 +5282,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, > > err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, > > NULL, 0); > > > > - hci_req_init(&req, hdev); > > - __hci_req_update_eir(&req); > > - hci_req_run(&req, NULL); > > + hci_cmd_sync_queue(hdev, __set_device_id_sync, NULL); > > > > hci_dev_unlock(hdev); > > I dislike the prefix __sync. They are in a separate anyway. So keep is simple and also scrap the __ prefix. Sure, do you want to perhaps have it as hci_sync given those should indicate that they require hci_req_sync_lock to be held, or you think that given the we will be converting everything to use the cmd_sync work that is not really necessary? Btw, as part of the cleanup Im also planning to send a patch moving eir/adv data related code to eir.{c,h}, do you have anything against it? > > Regards > > Marcel >
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index aecbdf99c216..2494c547a622 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1699,6 +1699,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u8 event, u32 timeout); +int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param, u32 timeout); int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param); diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index cc0995301f93..ab46b2b4f3cb 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -14,7 +14,7 @@ bluetooth_6lowpan-y := 6lowpan.o bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \ - ecdh_helper.o hci_request.o mgmt_util.o mgmt_config.o + ecdh_helper.o hci_request.o hci_sync.o mgmt_util.o mgmt_config.o bluetooth-$(CONFIG_BT_BREDR) += sco.o bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index fa9125b782f8..f390ac33ced9 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -189,6 +189,24 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, } EXPORT_SYMBOL(__hci_cmd_sync); +int __hci_cmd_sync_status(struct hci_dev *hdev, u16 opcode, u32 plen, + const void *param, u32 timeout) +{ + struct sk_buff *skb; + uint8_t status; + + skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + status = skb->data[0]; + + kfree_skb(skb); + + return status; +} +EXPORT_SYMBOL(__hci_cmd_sync_status); + /* Execute request and wait for completion. */ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req, unsigned long opt), diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c new file mode 100644 index 000000000000..5b73fcf09c18 --- /dev/null +++ b/net/bluetooth/hci_sync.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2021 Intel Corporation + */ + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_request.h" +#include "hci_sync.h" + +#define PNP_INFO_SVCLASS_ID 0x1200 + +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 u8 *create_uuid32_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) +{ + 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; + } + + memcpy(ptr, &uuid->uuid[12], sizeof(u32)); + ptr += sizeof(u32); + uuids_start[0] += sizeof(u32); + } + + 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; + } + + /* 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; + size_t name_len; + + name_len = strlen(hdev->dev_name); + + if (name_len > 0) { + /* EIR Data type */ + if (name_len > 48) { + name_len = 48; + ptr[1] = EIR_NAME_SHORT; + } else + ptr[1] = EIR_NAME_COMPLETE; + + /* EIR Data length */ + ptr[0] = name_len + 1; + + memcpy(ptr + 2, hdev->dev_name, name_len); + + ptr += (name_len + 2); + } + + if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) { + ptr[0] = 2; + ptr[1] = EIR_TX_POWER; + ptr[2] = (u8) hdev->inq_tx_power; + + ptr += 3; + } + + if (hdev->devid_source > 0) { + ptr[0] = 9; + ptr[1] = EIR_DEVICE_ID; + + put_unaligned_le16(hdev->devid_source, ptr + 2); + put_unaligned_le16(hdev->devid_vendor, ptr + 4); + put_unaligned_le16(hdev->devid_product, ptr + 6); + put_unaligned_le16(hdev->devid_version, ptr + 8); + + ptr += 10; + } + + 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)); +} + +int __hci_update_eir_sync(struct hci_dev *hdev) +{ + struct hci_cp_write_eir cp; + + bt_dev_dbg(hdev, ""); + + if (!hdev_is_powered(hdev)) + return 0; + + if (!lmp_ext_inq_capable(hdev)) + return 0; + + if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) + return 0; + + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) + return 0; + + memset(&cp, 0, sizeof(cp)); + + create_eir(hdev, cp.data); + + if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) + return 0; + + memcpy(hdev->eir, cp.data, sizeof(cp.data)); + + return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp, + HCI_CMD_TIMEOUT); +} + +static u8 get_service_classes(struct hci_dev *hdev) +{ + struct bt_uuid *uuid; + u8 val = 0; + + list_for_each_entry(uuid, &hdev->uuids, list) + val |= uuid->svc_hint; + + return val; +} + +int __hci_update_class_sync(struct hci_dev *hdev) +{ + u8 cod[3]; + + bt_dev_dbg(hdev, ""); + + if (!hdev_is_powered(hdev)) + return 0; + + if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) + return 0; + + if (hci_dev_test_flag(hdev, HCI_SERVICE_CACHE)) + return 0; + + cod[0] = hdev->minor_class; + cod[1] = hdev->major_class; + cod[2] = get_service_classes(hdev); + + if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) + cod[1] |= 0x20; + + if (memcmp(cod, hdev->dev_class, 3) == 0) + return 0; + + return __hci_cmd_sync_status(hdev, HCI_OP_WRITE_CLASS_OF_DEV, + sizeof(cod), cod, HCI_CMD_TIMEOUT); +} diff --git a/net/bluetooth/hci_sync.h b/net/bluetooth/hci_sync.h new file mode 100644 index 000000000000..735ff4b46e86 --- /dev/null +++ b/net/bluetooth/hci_sync.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2021 Intel Corporation + */ + +int __hci_update_eir_sync(struct hci_dev *hdev); +int __hci_update_class_sync(struct hci_dev *hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b44e19c69c44..96759c166678 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -34,6 +34,7 @@ #include <net/bluetooth/mgmt.h> #include "hci_request.h" +#include "hci_sync.h" #include "smp.h" #include "mgmt_util.h" #include "mgmt_config.h" @@ -950,25 +951,21 @@ bool mgmt_get_connectable(struct hci_dev *hdev) return hci_dev_test_flag(hdev, HCI_CONNECTABLE); } +static void __service_cache_sync(struct hci_dev *hdev, void *data) +{ + __hci_update_eir_sync(hdev); + __hci_update_class_sync(hdev); +} + static void service_cache_off(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, service_cache.work); - struct hci_request req; if (!hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) return; - hci_req_init(&req, hdev); - - hci_dev_lock(hdev); - - __hci_req_update_eir(&req); - __hci_req_update_class(&req); - - hci_dev_unlock(hdev); - - hci_req_run(&req, NULL); + hci_cmd_sync_queue(hdev, __service_cache_sync, NULL); } static void rpa_expired(struct work_struct *work) @@ -2093,8 +2090,17 @@ static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status) hci_dev_unlock(hdev); } -static void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) +static void __add_uuid_sync(struct hci_dev *hdev, void *data) { + uint8_t status; + + status = __hci_update_class_sync(hdev); + if (status) + goto done; + + status = __hci_update_eir_sync(hdev); + +done: bt_dev_dbg(hdev, "status 0x%02x", status); mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status); @@ -2104,7 +2110,6 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; struct mgmt_pending_cmd *cmd; - struct hci_request req; struct bt_uuid *uuid; int err; @@ -2130,20 +2135,9 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) list_add_tail(&uuid->list, &hdev->uuids); - hci_req_init(&req, hdev); - - __hci_req_update_class(&req); - __hci_req_update_eir(&req); - - err = hci_req_run(&req, add_uuid_complete); - if (err < 0) { - if (err != -ENODATA) - goto failed; - - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, - hdev->dev_class, 3); + err = hci_cmd_sync_queue(hdev, __add_uuid_sync, NULL); + if (err < 0) goto failed; - } cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); if (!cmd) { @@ -2172,8 +2166,17 @@ static bool enable_service_cache(struct hci_dev *hdev) return false; } -static void remove_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) +static void __remove_uuid_sync(struct hci_dev *hdev, void *data) { + uint8_t status; + + status = __hci_update_class_sync(hdev); + if (status) + goto done; + + status = __hci_update_eir_sync(hdev); + +done: bt_dev_dbg(hdev, "status 0x%02x", status); mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status); @@ -2186,7 +2189,6 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, struct mgmt_pending_cmd *cmd; 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 }; - struct hci_request req; int err, found; bt_dev_dbg(hdev, "sock %p", sk); @@ -2230,20 +2232,9 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, } update_class: - hci_req_init(&req, hdev); - - __hci_req_update_class(&req); - __hci_req_update_eir(&req); - - err = hci_req_run(&req, remove_uuid_complete); - if (err < 0) { - if (err != -ENODATA) - goto unlock; - - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, - hdev->dev_class, 3); + err = hci_cmd_sync_queue(hdev, __remove_uuid_sync, NULL); + if (err < 0) goto unlock; - } cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); if (!cmd) { @@ -2258,8 +2249,24 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, return err; } -static void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode) +static void __set_class_sync(struct hci_dev *hdev, void *data) { + uint8_t status = 0; + + hci_dev_lock(hdev); + + if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { + hci_dev_unlock(hdev); + cancel_delayed_work_sync(&hdev->service_cache); + hci_dev_lock(hdev); + status = __hci_update_eir_sync(hdev); + } + + hci_dev_unlock(hdev); + + if (!status) + status = __hci_update_class_sync(hdev); + bt_dev_dbg(hdev, "status 0x%02x", status); mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status); @@ -2270,7 +2277,6 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_cp_set_dev_class *cp = data; struct mgmt_pending_cmd *cmd; - struct hci_request req; int err; bt_dev_dbg(hdev, "sock %p", sk); @@ -2302,26 +2308,9 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - hci_req_init(&req, hdev); - - if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { - hci_dev_unlock(hdev); - cancel_delayed_work_sync(&hdev->service_cache); - hci_dev_lock(hdev); - __hci_req_update_eir(&req); - } - - __hci_req_update_class(&req); - - err = hci_req_run(&req, set_class_complete); - if (err < 0) { - if (err != -ENODATA) - goto unlock; - - err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); + err = hci_cmd_sync_queue(hdev, __set_class_sync, NULL); + if (err < 0) goto unlock; - } cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); if (!cmd) { @@ -5263,11 +5252,15 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, return err; } +static void __set_device_id_sync(struct hci_dev *hdev, void *data) +{ + __hci_update_eir_sync(hdev); +} + static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_device_id *cp = data; - struct hci_request req; int err; __u16 source; @@ -5289,9 +5282,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, NULL, 0); - hci_req_init(&req, hdev); - __hci_req_update_eir(&req); - hci_req_run(&req, NULL); + hci_cmd_sync_queue(hdev, __set_device_id_sync, NULL); hci_dev_unlock(hdev);