]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
Bluetooth: Introduce HCI Driver protocol
authorHsin-chen Chuang <chharry@chromium.org>
Wed, 16 Apr 2025 09:53:35 +0000 (09:53 +0000)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 21 May 2025 14:28:07 +0000 (10:28 -0400)
Although commit 75ddcd5ad40e ("Bluetooth: btusb: Configure altsetting
for HCI_USER_CHANNEL") has enabled the HCI_USER_CHANNEL user to send out
SCO data through USB Bluetooth chips, it's observed that with the patch
HFP is flaky on most of the existing USB Bluetooth controllers: Intel
chips sometimes send out no packet for Transparent codec; MTK chips may
generate SCO data with a wrong handle for CVSD codec; RTK could split
the data with a wrong packet size for Transparent codec; ... etc.

To address the issue above one needs to reset the altsetting back to
zero when there is no active SCO connection, which is the same as the
BlueZ behavior, and another benefit is the bus doesn't need to reserve
bandwidth when no SCO connection.

This patch adds the infrastructure that allow the user space program to
talk to Bluetooth drivers directly:
- Define the new packet type HCI_DRV_PKT which is specifically used for
  communication between the user space program and the Bluetooth drviers
- hci_send_frame intercepts the packets and invokes drivers' HCI Drv
  callbacks (so far only defined for btusb)
- 2 kinds of events to user space: Command Status and Command Complete,
  the former simply returns the status while the later may contain
  additional response data.

Cc: chromeos-bluetooth-upstreaming@chromium.org
Fixes: b16b327edb4d ("Bluetooth: btusb: add sysfs attribute to control USB alt setting")
Signed-off-by: Hsin-chen Chuang <chharry@chromium.org>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
drivers/bluetooth/btusb.c
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/hci_drv.h [new file with mode: 0644]
include/net/bluetooth/hci_mon.h
net/bluetooth/Makefile
net/bluetooth/hci_core.c
net/bluetooth/hci_drv.c [new file with mode: 0644]
net/bluetooth/hci_sock.c

index 47e08fa7db8f0ac5ad8aa83c8e9a91d11bb44f3e..953b88449923e970e14dc8c166504cd32e4d8d9f 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_drv.h>
 
 #include "btintel.h"
 #include "btbcm.h"
@@ -3800,6 +3801,61 @@ static ssize_t isoc_alt_store(struct device *dev,
 
 static DEVICE_ATTR_RW(isoc_alt);
 
+static const struct {
+       u16 opcode;
+       const char *desc;
+} btusb_hci_drv_supported_commands[] = {
+       /* Common commands */
+       { HCI_DRV_OP_READ_INFO, "Read Info" },
+};
+static int btusb_hci_drv_read_info(struct hci_dev *hdev, void *data,
+                                  u16 data_len)
+{
+       struct hci_drv_rp_read_info *rp;
+       size_t rp_size;
+       int err, i;
+       u16 opcode, num_supported_commands =
+               ARRAY_SIZE(btusb_hci_drv_supported_commands);
+
+       rp_size = sizeof(*rp) + num_supported_commands * 2;
+
+       rp = kmalloc(rp_size, GFP_KERNEL);
+       if (!rp)
+               return -ENOMEM;
+
+       strscpy_pad(rp->driver_name, btusb_driver.name);
+
+       rp->num_supported_commands = cpu_to_le16(num_supported_commands);
+       for (i = 0; i < num_supported_commands; i++) {
+               opcode = btusb_hci_drv_supported_commands[i].opcode;
+               bt_dev_info(hdev,
+                           "Supported HCI Drv command (0x%02x|0x%04x): %s",
+                           hci_opcode_ogf(opcode),
+                           hci_opcode_ocf(opcode),
+                           btusb_hci_drv_supported_commands[i].desc);
+               rp->supported_commands[i] = cpu_to_le16(opcode);
+       }
+
+       err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
+                                  HCI_DRV_STATUS_SUCCESS, rp, rp_size);
+
+       kfree(rp);
+       return err;
+}
+
+static const struct hci_drv_handler btusb_hci_drv_common_handlers[] = {
+       { btusb_hci_drv_read_info,      HCI_DRV_READ_INFO_SIZE },
+};
+
+static const struct hci_drv_handler btusb_hci_drv_specific_handlers[] = {};
+
+static struct hci_drv btusb_hci_drv = {
+       .common_handler_count   = ARRAY_SIZE(btusb_hci_drv_common_handlers),
+       .common_handlers        = btusb_hci_drv_common_handlers,
+       .specific_handler_count = ARRAY_SIZE(btusb_hci_drv_specific_handlers),
+       .specific_handlers      = btusb_hci_drv_specific_handlers,
+};
+
 static int btusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
 {
@@ -3939,12 +3995,13 @@ static int btusb_probe(struct usb_interface *intf,
                data->reset_gpio = reset_gpio;
        }
 
-       hdev->open   = btusb_open;
-       hdev->close  = btusb_close;
-       hdev->flush  = btusb_flush;
-       hdev->send   = btusb_send_frame;
-       hdev->notify = btusb_notify;
-       hdev->wakeup = btusb_wakeup;
+       hdev->open    = btusb_open;
+       hdev->close   = btusb_close;
+       hdev->flush   = btusb_flush;
+       hdev->send    = btusb_send_frame;
+       hdev->notify  = btusb_notify;
+       hdev->wakeup  = btusb_wakeup;
+       hdev->hci_drv = &btusb_hci_drv;
 
 #ifdef CONFIG_PM
        err = btusb_config_oob_wake(hdev);
index 797992019f9ee5ea22b13677ae1885158355b687..2502febf4da7a42f33c030006c00d40071189ad1 100644 (file)
@@ -494,6 +494,7 @@ enum {
 #define HCI_EVENT_PKT          0x04
 #define HCI_ISODATA_PKT                0x05
 #define HCI_DIAG_PKT           0xf0
+#define HCI_DRV_PKT            0xf1
 #define HCI_VENDOR_PKT         0xff
 
 /* HCI packet types */
index 54bfeeaa09959b48b4219010a27023cbcf045769..57f6175fd1cd86c0f3831a80675f1c48bad2d79a 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/rculist.h>
 
 #include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_drv.h>
 #include <net/bluetooth/hci_sync.h>
 #include <net/bluetooth/hci_sock.h>
 #include <net/bluetooth/coredump.h>
@@ -613,6 +614,8 @@ struct hci_dev {
        struct list_head        monitored_devices;
        bool                    advmon_pend_notify;
 
+       struct hci_drv          *hci_drv;
+
 #if IS_ENABLED(CONFIG_BT_LEDS)
        struct led_trigger      *power_led;
 #endif
diff --git a/include/net/bluetooth/hci_drv.h b/include/net/bluetooth/hci_drv.h
new file mode 100644 (file)
index 0000000..2f01c44
--- /dev/null
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Google Corporation
+ */
+
+#ifndef __HCI_DRV_H
+#define __HCI_DRV_H
+
+#include <linux/types.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+struct hci_drv_cmd_hdr {
+       __le16  opcode;
+       __le16  len;
+} __packed;
+
+struct hci_drv_ev_hdr {
+       __le16  opcode;
+       __le16  len;
+} __packed;
+
+#define HCI_DRV_EV_CMD_STATUS  0x0000
+struct hci_drv_ev_cmd_status {
+       __le16  opcode;
+       __u8    status;
+} __packed;
+
+#define HCI_DRV_EV_CMD_COMPLETE        0x0001
+struct hci_drv_ev_cmd_complete {
+       __le16  opcode;
+       __u8    status;
+       __u8    data[];
+} __packed;
+
+#define HCI_DRV_STATUS_SUCCESS                 0x00
+#define HCI_DRV_STATUS_UNSPECIFIED_ERROR       0x01
+#define HCI_DRV_STATUS_UNKNOWN_COMMAND         0x02
+#define HCI_DRV_STATUS_INVALID_PARAMETERS      0x03
+
+#define HCI_DRV_MAX_DRIVER_NAME_LENGTH 32
+
+/* Common commands that make sense on all drivers start from 0x0000 */
+#define HCI_DRV_OP_READ_INFO   0x0000
+#define HCI_DRV_READ_INFO_SIZE 0
+struct hci_drv_rp_read_info {
+       __u8    driver_name[HCI_DRV_MAX_DRIVER_NAME_LENGTH];
+       __le16  num_supported_commands;
+       __le16  supported_commands[];
+} __packed;
+
+/* Driver specific OGF (Opcode Group Field)
+ * Commands in this group may have different meanings across different drivers.
+ */
+#define HCI_DRV_OGF_DRIVER_SPECIFIC    0x01
+
+int hci_drv_cmd_status(struct hci_dev *hdev, u16 cmd, u8 status);
+int hci_drv_cmd_complete(struct hci_dev *hdev, u16 cmd, u8 status, void *rp,
+                        size_t rp_len);
+int hci_drv_process_cmd(struct hci_dev *hdev, struct sk_buff *cmd_skb);
+
+struct hci_drv_handler {
+       int (*func)(struct hci_dev *hdev, void *data, u16 data_len);
+       size_t data_len;
+};
+
+struct hci_drv {
+       size_t common_handler_count;
+       const struct hci_drv_handler *common_handlers;
+
+       size_t specific_handler_count;
+       const struct hci_drv_handler *specific_handlers;
+};
+
+#endif /* __HCI_DRV_H */
index 082f89531b8896e8fdcc618d9b63e2ab5d062c41..bbd752494ef9149d9280ee86b581b4cdd90f999c 100644 (file)
@@ -51,6 +51,8 @@ struct hci_mon_hdr {
 #define HCI_MON_CTRL_EVENT     17
 #define HCI_MON_ISO_TX_PKT     18
 #define HCI_MON_ISO_RX_PKT     19
+#define HCI_MON_DRV_TX_PKT     20
+#define HCI_MON_DRV_RX_PKT     21
 
 struct hci_mon_new_index {
        __u8            type;
index 5a3835b7dfcdcb1bf75a541cb64893bc558bba58..a7eede7616d85623f5f09dedde769f29f251580c 100644 (file)
@@ -14,7 +14,8 @@ 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 mgmt_util.o mgmt_config.o hci_codec.o eir.o hci_sync.o
+       ecdh_helper.o mgmt_util.o mgmt_config.o hci_codec.o eir.o hci_sync.o \
+       hci_drv.o
 
 bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o
 
index 5eb0600bbd03ccde9147ecbc55b046b6c86abf5b..df7d7bf32eb8e35f0fdcf4cd3722408fb1d7362f 100644 (file)
@@ -2911,6 +2911,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
                break;
        case HCI_ISODATA_PKT:
                break;
+       case HCI_DRV_PKT:
+               break;
        default:
                kfree_skb(skb);
                return -EINVAL;
@@ -3019,6 +3021,15 @@ static int hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
                return -EINVAL;
        }
 
+       if (hci_skb_pkt_type(skb) == HCI_DRV_PKT) {
+               /* Intercept HCI Drv packet here and don't go with hdev->send
+                * callback.
+                */
+               err = hci_drv_process_cmd(hdev, skb);
+               kfree_skb(skb);
+               return err;
+       }
+
        err = hdev->send(hdev, skb);
        if (err < 0) {
                bt_dev_err(hdev, "sending frame failed (%d)", err);
diff --git a/net/bluetooth/hci_drv.c b/net/bluetooth/hci_drv.c
new file mode 100644 (file)
index 0000000..3dd2d8a
--- /dev/null
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Google Corporation
+ */
+
+#include <linux/skbuff.h>
+#include <linux/types.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_drv.h>
+
+int hci_drv_cmd_status(struct hci_dev *hdev, u16 cmd, u8 status)
+{
+       struct hci_drv_ev_hdr *hdr;
+       struct hci_drv_ev_cmd_status *ev;
+       struct sk_buff *skb;
+
+       skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       hdr = skb_put(skb, sizeof(*hdr));
+       hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_STATUS);
+       hdr->len = __cpu_to_le16(sizeof(*ev));
+
+       ev = skb_put(skb, sizeof(*ev));
+       ev->opcode = __cpu_to_le16(cmd);
+       ev->status = status;
+
+       hci_skb_pkt_type(skb) = HCI_DRV_PKT;
+
+       return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL(hci_drv_cmd_status);
+
+int hci_drv_cmd_complete(struct hci_dev *hdev, u16 cmd, u8 status, void *rp,
+                        size_t rp_len)
+{
+       struct hci_drv_ev_hdr *hdr;
+       struct hci_drv_ev_cmd_complete *ev;
+       struct sk_buff *skb;
+
+       skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       hdr = skb_put(skb, sizeof(*hdr));
+       hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_COMPLETE);
+       hdr->len = __cpu_to_le16(sizeof(*ev) + rp_len);
+
+       ev = skb_put(skb, sizeof(*ev));
+       ev->opcode = __cpu_to_le16(cmd);
+       ev->status = status;
+
+       skb_put_data(skb, rp, rp_len);
+
+       hci_skb_pkt_type(skb) = HCI_DRV_PKT;
+
+       return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL(hci_drv_cmd_complete);
+
+int hci_drv_process_cmd(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_drv_cmd_hdr *hdr;
+       const struct hci_drv_handler *handler = NULL;
+       u16 opcode, len, ogf, ocf;
+
+       hdr = skb_pull_data(skb, sizeof(*hdr));
+       if (!hdr)
+               return -EILSEQ;
+
+       opcode = __le16_to_cpu(hdr->opcode);
+       len = __le16_to_cpu(hdr->len);
+       if (len != skb->len)
+               return -EILSEQ;
+
+       ogf = hci_opcode_ogf(opcode);
+       ocf = hci_opcode_ocf(opcode);
+
+       if (!hdev->hci_drv)
+               return hci_drv_cmd_status(hdev, opcode,
+                                         HCI_DRV_STATUS_UNKNOWN_COMMAND);
+
+       if (ogf != HCI_DRV_OGF_DRIVER_SPECIFIC) {
+               if (opcode < hdev->hci_drv->common_handler_count)
+                       handler = &hdev->hci_drv->common_handlers[opcode];
+       } else {
+               if (ocf < hdev->hci_drv->specific_handler_count)
+                       handler = &hdev->hci_drv->specific_handlers[ocf];
+       }
+
+       if (!handler || !handler->func)
+               return hci_drv_cmd_status(hdev, opcode,
+                                         HCI_DRV_STATUS_UNKNOWN_COMMAND);
+
+       if (len != handler->data_len)
+               return hci_drv_cmd_status(hdev, opcode,
+                                         HCI_DRV_STATUS_INVALID_PARAMETERS);
+
+       return handler->func(hdev, skb->data, len);
+}
+EXPORT_SYMBOL(hci_drv_process_cmd);
index 022b86797acdc56a6e9b85f24f4c98a0247831c9..428ee5c7de7ea32fea08a2ebcf57e30dee59e3fa 100644 (file)
@@ -234,7 +234,8 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
                        if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
                            hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
                            hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
-                           hci_skb_pkt_type(skb) != HCI_ISODATA_PKT)
+                           hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
+                           hci_skb_pkt_type(skb) != HCI_DRV_PKT)
                                continue;
                } else {
                        /* Don't send frame to other channel types */
@@ -391,6 +392,12 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
                else
                        opcode = cpu_to_le16(HCI_MON_ISO_TX_PKT);
                break;
+       case HCI_DRV_PKT:
+               if (bt_cb(skb)->incoming)
+                       opcode = cpu_to_le16(HCI_MON_DRV_RX_PKT);
+               else
+                       opcode = cpu_to_le16(HCI_MON_DRV_TX_PKT);
+               break;
        case HCI_DIAG_PKT:
                opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
                break;
@@ -1860,7 +1867,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
                    hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
                    hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
-                   hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
+                   hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
+                   hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
                        err = -EINVAL;
                        goto drop;
                }