Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 1
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 27 Oct 2021 23:58:39 +0000 (16:58 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Fri, 29 Oct 2021 14:51:58 +0000 (16:51 +0200)
This make use of hci_cmd_sync_queue for the following MGMT commands:

Set Device Class
Set Device ID
Add UUID
Remove UUID

tools/mgmt-tester -s "Set Device Class"

Test Summary
------------
Set Device Class - Success 1                         Passed
Set Device Class - Success 2                         Passed
Set Device Class - Invalid parameters 1              Passed
Total: 3, Passed: 3 (100.0%), Failed: 0, Not Run: 0
Overall execution time: 0.0599 seconds

tools/mgmt-tester -s "Set Device ID"

Test Summary
------------
Set Device ID - Success 1                            Passed
Set Device ID - Success 2                            Passed
Set Device ID - Disable                              Passed
Set Device ID - Power off and Power on               Passed
Set Device ID - SSP off and Power on                 Passed
Set Device ID - Invalid Parameter                    Passed
Total: 6, Passed: 6 (100.0%), Failed: 0, Not Run: 0
Overall execution time: 0.107 seconds

tools/mgmt-tester -s "Add UUID"

Test Summary
------------
Add UUID - UUID-16 1                                 Passed
Add UUID - UUID-16 multiple 1                        Passed
Add UUID - UUID-16 partial 1                         Passed
Add UUID - UUID-32 1                                 Passed
Add UUID - UUID-32 multiple 1                        Passed
Add UUID - UUID-32 partial 1                         Passed
Add UUID - UUID-128 1                                Passed
Add UUID - UUID-128 multiple 1                       Passed
Add UUID - UUID-128 partial 1                        Passed
Add UUID - UUID mix                                  Passed
Total: 10, Passed: 10 (100.0%), Failed: 0, Not Run: 0
Overall execution time: 0.198 seconds

tools/mgmt-tester -s "Remove UUID"

Test Summary
------------
Remove UUID - Success 1                              Passed
Remove UUID - All UUID - Success 2                   Passed
Remove UUID - Power Off - Success 3                  Passed
Remove UUID - Power Off and On - Success 4           Passed
Remove UUID - Not Exist - Invalid Params 1           Passed
Total: 5, Passed: 5 (100.0%), Failed: 0, Not Run: 0
Overall execution time: 0.0908 seconds

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
include/net/bluetooth/hci_sync.h
net/bluetooth/hci_request.c
net/bluetooth/hci_request.h
net/bluetooth/hci_sync.c
net/bluetooth/mgmt.c
net/bluetooth/mgmt_util.c
net/bluetooth/mgmt_util.h

index fcfdeb3..fe77ff9 100644 (file)
@@ -40,3 +40,6 @@ void hci_cmd_sync_clear(struct hci_dev *hdev);
 
 int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
                       void *data, hci_cmd_sync_work_destroy_t destroy);
+
+int hci_update_eir_sync(struct hci_dev *hdev);
+int hci_update_class_sync(struct hci_dev *hdev);
index c98340b..b1b3343 100644 (file)
@@ -97,8 +97,8 @@ int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
        return req_run(req, NULL, complete);
 }
 
-static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
-                                 struct sk_buff *skb)
+void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+                          struct sk_buff *skb)
 {
        bt_dev_dbg(hdev, "result 0x%2.2x", result);
 
index 74c5de5..ba75c2d 100644 (file)
 
 #include <asm/unaligned.h>
 
+#define HCI_REQ_DONE     0
+#define HCI_REQ_PEND     1
+#define HCI_REQ_CANCELED  2
+
 #define hci_req_sync_lock(hdev)   mutex_lock(&hdev->req_lock)
 #define hci_req_sync_unlock(hdev) mutex_unlock(&hdev->req_lock)
 
@@ -44,6 +48,8 @@ void hci_req_purge(struct hci_request *req);
 bool hci_req_status_pend(struct hci_dev *hdev);
 int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
 int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete);
+void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+                          struct sk_buff *skb);
 void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
                 const void *param);
 void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
index b204828..8546cbc 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "hci_request.h"
 #include "smp.h"
+#include "eir.h"
 
 static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
                                  struct sk_buff *skb)
@@ -328,3 +329,74 @@ int hci_cmd_sync_queue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
        return 0;
 }
 EXPORT_SYMBOL(hci_cmd_sync_queue);
+
+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));
+
+       eir_create(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);
+}
index 4468344..ec6aa7a 100644 (file)
@@ -276,10 +276,39 @@ static const u8 mgmt_status_table[] = {
        MGMT_STATUS_CONNECT_FAILED,     /* MAC Connection Failed */
 };
 
-static u8 mgmt_status(u8 hci_status)
+static u8 mgmt_errno_status(int err)
 {
-       if (hci_status < ARRAY_SIZE(mgmt_status_table))
-               return mgmt_status_table[hci_status];
+       switch (err) {
+       case 0:
+               return MGMT_STATUS_SUCCESS;
+       case -EPERM:
+               return MGMT_STATUS_REJECTED;
+       case -EINVAL:
+               return MGMT_STATUS_INVALID_PARAMS;
+       case -EOPNOTSUPP:
+               return MGMT_STATUS_NOT_SUPPORTED;
+       case -EBUSY:
+               return MGMT_STATUS_BUSY;
+       case -ETIMEDOUT:
+               return MGMT_STATUS_AUTH_FAILED;
+       case -ENOMEM:
+               return MGMT_STATUS_NO_RESOURCES;
+       case -EISCONN:
+               return MGMT_STATUS_ALREADY_CONNECTED;
+       case -ENOTCONN:
+               return MGMT_STATUS_DISCONNECTED;
+       }
+
+       return MGMT_STATUS_FAILED;
+}
+
+static u8 mgmt_status(int err)
+{
+       if (err < 0)
+               return mgmt_errno_status(err);
+
+       if (err < ARRAY_SIZE(mgmt_status_table))
+               return mgmt_status_table[err];
 
        return MGMT_STATUS_FAILED;
 }
@@ -951,25 +980,23 @@ bool mgmt_get_connectable(struct hci_dev *hdev)
        return hci_dev_test_flag(hdev, HCI_CONNECTABLE);
 }
 
+static int service_cache_sync(struct hci_dev *hdev, void *data)
+{
+       hci_update_eir_sync(hdev);
+       hci_update_class_sync(hdev);
+
+       return 0;
+}
+
 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, NULL);
 }
 
 static void rpa_expired(struct work_struct *work)
@@ -2075,37 +2102,33 @@ static u8 get_uuid_size(const u8 *uuid)
        return 16;
 }
 
-static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status)
+static void mgmt_class_complete(struct hci_dev *hdev, void *data, int err)
 {
-       struct mgmt_pending_cmd *cmd;
-
-       hci_dev_lock(hdev);
+       struct mgmt_pending_cmd *cmd = data;
 
-       cmd = pending_find(mgmt_op, hdev);
-       if (!cmd)
-               goto unlock;
+       bt_dev_dbg(hdev, "err %d", err);
 
        mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
-                         mgmt_status(status), hdev->dev_class, 3);
+                         mgmt_status(err), hdev->dev_class, 3);
 
-       mgmt_pending_remove(cmd);
-
-unlock:
-       hci_dev_unlock(hdev);
+       mgmt_pending_free(cmd);
 }
 
-static void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+static int add_uuid_sync(struct hci_dev *hdev, void *data)
 {
-       bt_dev_dbg(hdev, "status 0x%02x", status);
+       int err;
 
-       mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status);
+       err = hci_update_class_sync(hdev);
+       if (err)
+               return err;
+
+       return hci_update_eir_sync(hdev);
 }
 
 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;
 
@@ -2131,28 +2154,17 @@ 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);
-               goto failed;
-       }
-
-       cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len);
+       cmd = mgmt_pending_new(sk, MGMT_OP_ADD_UUID, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto failed;
        }
 
-       err = 0;
+       err = hci_cmd_sync_queue(hdev, add_uuid_sync, cmd, mgmt_class_complete);
+       if (err < 0) {
+               mgmt_pending_free(cmd);
+               goto failed;
+       }
 
 failed:
        hci_dev_unlock(hdev);
@@ -2173,11 +2185,15 @@ 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 int remove_uuid_sync(struct hci_dev *hdev, void *data)
 {
-       bt_dev_dbg(hdev, "status 0x%02x", status);
+       int err;
 
-       mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status);
+       err = hci_update_class_sync(hdev);
+       if (err)
+               return err;
+
+       return hci_update_eir_sync(hdev);
 }
 
 static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -2187,7 +2203,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);
@@ -2231,39 +2246,35 @@ 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);
-               goto unlock;
-       }
-
-       cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len);
+       cmd = mgmt_pending_new(sk, MGMT_OP_REMOVE_UUID, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto unlock;
        }
 
-       err = 0;
+       err = hci_cmd_sync_queue(hdev, remove_uuid_sync, cmd,
+                                mgmt_class_complete);
+       if (err < 0)
+               mgmt_pending_free(cmd);
 
 unlock:
        hci_dev_unlock(hdev);
        return err;
 }
 
-static void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+static int set_class_sync(struct hci_dev *hdev, void *data)
 {
-       bt_dev_dbg(hdev, "status 0x%02x", status);
+       int err = 0;
+
+       if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) {
+               cancel_delayed_work_sync(&hdev->service_cache);
+               err = hci_update_eir_sync(hdev);
+       }
+
+       if (err)
+               return err;
 
-       mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status);
+       return hci_update_class_sync(hdev);
 }
 
 static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -2271,7 +2282,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);
@@ -2303,34 +2313,16 @@ 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);
-               goto unlock;
-       }
-
-       cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len);
+       cmd = mgmt_pending_new(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len);
        if (!cmd) {
                err = -ENOMEM;
                goto unlock;
        }
 
-       err = 0;
+       err = hci_cmd_sync_queue(hdev, set_class_sync, cmd,
+                                mgmt_class_complete);
+       if (err < 0)
+               mgmt_pending_free(cmd);
 
 unlock:
        hci_dev_unlock(hdev);
@@ -5494,11 +5486,15 @@ done:
        return err;
 }
 
+static int set_device_id_sync(struct hci_dev *hdev, void *data)
+{
+       return 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;
 
@@ -5520,9 +5516,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, NULL);
 
        hci_dev_unlock(hdev);
 
index 0d0a6d7..83875f2 100644 (file)
@@ -227,7 +227,7 @@ void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
        }
 }
 
-struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
+struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
                                          struct hci_dev *hdev,
                                          void *data, u16 len)
 {
@@ -251,6 +251,19 @@ struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
        cmd->sk = sk;
        sock_hold(sk);
 
+       return cmd;
+}
+
+struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
+                                         struct hci_dev *hdev,
+                                         void *data, u16 len)
+{
+       struct mgmt_pending_cmd *cmd;
+
+       cmd = mgmt_pending_new(sk, opcode, hdev, data, len);
+       if (!cmd)
+               return NULL;
+
        list_add(&cmd->list, &hdev->mgmt_pending);
 
        return cmd;
index 6559f18..9dc24ba 100644 (file)
@@ -49,5 +49,8 @@ void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
 struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
                                          struct hci_dev *hdev,
                                          void *data, u16 len);
+struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
+                                         struct hci_dev *hdev,
+                                         void *data, u16 len);
 void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
 void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);