Commit 04425292 authored by Hsin-chen Chuang's avatar Hsin-chen Chuang Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: Introduce HCI Driver protocol



Although commit 75ddcd5a ("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: b16b327e ("Bluetooth: btusb: add sysfs attribute to control USB alt setting")
Signed-off-by: default avatarHsin-chen Chuang <chharry@chromium.org>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent c50b5666
Loading
Loading
Loading
Loading
+63 −6
Original line number Diff line number Diff line
@@ -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)
{
@@ -3945,6 +4001,7 @@ static int btusb_probe(struct usb_interface *intf,
	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);
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+3 −0
Original line number Diff line number Diff line
@@ -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
+76 −0
Original line number Diff line number Diff line
/* 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 */
+2 −0
Original line number Diff line number Diff line
@@ -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;
Loading