]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_SYNC
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 23 Oct 2024 20:55:57 +0000 (16:55 -0400)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 14 Nov 2024 20:41:31 +0000 (15:41 -0500)
This adds the initial implementation of MGMT_OP_HCI_CMD_SYNC as
documented in mgmt-api (BlueZ tree):

Send HCI command and wait for event Command
===========================================

Command Code: 0x005B
Controller Index: <controller id>
Command Parameters: Opcode (2 Octets)
Event (1 Octet)
Timeout (1 Octet)
Parameter Length (2 Octets)
Parameter (variable)
Return Parameters: Response (1-variable Octets)

This command may be used to send a HCI command and wait for an
(optional) event.

The HCI command is specified by the Opcode, any arbitrary is supported
including vendor commands, but contrary to the like of
Raw/User channel it is run as an HCI command send by the kernel
since it uses its command synchronization thus it is possible to wait
for a specific event as a response.

Setting event to 0x00 will cause the command to wait for either
HCI Command Status or HCI Command Complete.

Timeout is specified in seconds, setting it to 0 will cause the
default timeout to be used.

Possible errors: Failed
Invalid Parameters

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/mgmt.h
net/bluetooth/mgmt.c

index d382679efd2b1c148a46e59d1f5e4cade063b5e6..affac861efdc5841eb97fd0ccc1f3fc2dbc3e6fb 100644 (file)
@@ -878,6 +878,16 @@ struct mgmt_cp_mesh_send_cancel {
 } __packed;
 #define MGMT_MESH_SEND_CANCEL_SIZE     1
 
+#define MGMT_OP_HCI_CMD_SYNC           0x005B
+struct mgmt_cp_hci_cmd_sync {
+       __le16 opcode;
+       __u8   event;
+       __u8   timeout;
+       __le16 params_len;
+       __u8   params[];
+} __packed;
+#define MGMT_HCI_CMD_SYNC_SIZE         6
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
index a429661b676a83ec2d34ed7e228195f39a153f9f..1f6d083682b8e242eb4d8436f910d3ee91917123 100644 (file)
@@ -132,6 +132,7 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_MESH_READ_FEATURES,
        MGMT_OP_MESH_SEND,
        MGMT_OP_MESH_SEND_CANCEL,
+       MGMT_OP_HCI_CMD_SYNC,
 };
 
 static const u16 mgmt_events[] = {
@@ -2515,6 +2516,64 @@ unlock:
        return err;
 }
 
+static int send_hci_cmd_sync(struct hci_dev *hdev, void *data)
+{
+       struct mgmt_pending_cmd *cmd = data;
+       struct mgmt_cp_hci_cmd_sync *cp = cmd->param;
+       struct sk_buff *skb;
+
+       skb = __hci_cmd_sync_ev(hdev, le16_to_cpu(cp->opcode),
+                               le16_to_cpu(cp->params_len), cp->params,
+                               cp->event, cp->timeout ?
+                               msecs_to_jiffies(cp->timeout * 1000) :
+                               HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
+                               mgmt_status(PTR_ERR(skb)));
+               goto done;
+       }
+
+       mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, 0,
+                         skb->data, skb->len);
+
+       kfree_skb(skb);
+
+done:
+       mgmt_pending_free(cmd);
+
+       return 0;
+}
+
+static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
+                            void *data, u16 len)
+{
+       struct mgmt_cp_hci_cmd_sync *cp = data;
+       struct mgmt_pending_cmd *cmd;
+       int err;
+
+       if (len < sizeof(*cp))
+               return mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
+                                      MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+       cmd = mgmt_pending_new(sk, MGMT_OP_HCI_CMD_SYNC, hdev, data, len);
+       if (!cmd)
+               err = -ENOMEM;
+       else
+               err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd, NULL);
+
+       if (err < 0) {
+               err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
+                                     MGMT_STATUS_FAILED);
+
+               if (cmd)
+                       mgmt_pending_free(cmd);
+       }
+
+       hci_dev_unlock(hdev);
+       return err;
+}
+
 /* This is a helper function to test for pending mgmt commands that can
  * cause CoD or EIR HCI commands. We can only allow one such pending
  * mgmt command at a time since otherwise we cannot easily track what
@@ -9371,6 +9430,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
        { mesh_send,               MGMT_MESH_SEND_SIZE,
                                                HCI_MGMT_VAR_LEN },
        { mesh_send_cancel,        MGMT_MESH_SEND_CANCEL_SIZE },
+       { mgmt_hci_cmd_sync,       MGMT_HCI_CMD_SYNC_SIZE, HCI_MGMT_VAR_LEN },
 };
 
 void mgmt_index_added(struct hci_dev *hdev)