Bluetooth: btusb: Add support for Qualcomm multi-subsystem QCC2072

QCC2072 has a Peripheral (PERI) subsystem to support the BT function
unit, the transport wire is owned by PERI and carries both BT-HCI and
PERI-HCI frames, as shown below:

                 _______________________________
       USB I/F	|	   internal link	|
BTHOST --------	|---- PERI -------------- BT	|
		|_______________________________|
                (other on-chip subsystems omitted)

PERI has its own command/event/ACL traffic, memdump, and firmware
(PATCH and NVM), all different from BT's own, and BTHOST needs to
interact with PERI directly.

To support multi-subsystem controllers, add btusb_qcom.c/.h as a new
transport-specific design:

- Multi-subsystem memdump collection.
- Generic PERI event handling, and sending a PERI frame then
  syncing an event sequence.
- Downloading PERI firmware (rampatch and NVM).
- Recovery on command timeout or hardware error event.

BTW, it also supports BT-only chips.

/sys/kernel/debug/usb/devices:
T:  Bus=03 Lev=02 Prnt=02 Port=02 Cnt=01 Dev#=  5 Spd=12   MxCh= 0
D:  Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=0cf3 ProdID=ea00 Rev= 0.01
C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=1ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   0 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   0 Ivl=1ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=   9 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=   9 Ivl=1ms
I:  If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  17 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  17 Ivl=1ms
I:  If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  25 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  25 Ivl=1ms
I:  If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  33 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  33 Ivl=1ms
I:  If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  49 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  49 Ivl=1ms
I:  If#= 1 Alt= 6 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  63 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  63 Ivl=1ms
I:  If#= 1 Alt= 7 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=83(I) Atr=01(Isoc) MxPS=  65 Ivl=1ms
E:  Ad=03(O) Atr=01(Isoc) MxPS=  65 Ivl=1ms

Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Zijun Hu 2026-09-13 19:46:46 -07:00 committed by Luiz Augusto von Dentz
parent be58267a34
commit 3669487308
5 changed files with 4792 additions and 0 deletions

View File

@ -90,6 +90,20 @@ config BT_HCIBTUSB_RTL
Say Y here to compile support for Realtek protocol.
config BT_HCIBTUSB_QCOM
bool "Qualcomm protocol support"
depends on BT_HCIBTUSB
select FW_LOADER
select GPIOLIB
select WANT_DEV_COREDUMP
default y
help
Enables various required support, such as configuration, firmware
download, and error recovery, for Qualcomm Bluetooth controllers.
Say Y here to compile support for Qualcomm protocol.
If unsure, say Y.
config BT_HCIBTSDIO
tristate "HCI SDIO driver"
depends on MMC

View File

@ -52,5 +52,6 @@ hci_uart-$(CONFIG_BT_HCIUART_AML) += hci_aml.o
hci_uart-objs := $(hci_uart-y)
btusb-y := btusb_main.o
btusb-$(CONFIG_BT_HCIBTUSB_QCOM) += btusb_qcom.o
CONTEXT_ANALYSIS := y

View File

@ -29,6 +29,7 @@
#include "btrtl.h"
#include "btmtk.h"
#include "hci_uart.h"
#include "btusb_qcom.h"
#define VERSION "1.0"
@ -70,6 +71,7 @@ static struct usb_driver btusb_driver;
#define BTUSB_ACTIONS_SEMI BIT(27)
#define BTUSB_BARROT BIT(28)
#define BTUSB_BROKEN_EXT_SCAN BIT(29)
#define BTUSB_QUALCOMM BIT(30)
static const struct usb_device_id btusb_table[] = {
/*
@ -426,6 +428,10 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x2c7c, 0x0132), .driver_info = BTUSB_QCA_WCN6855 |
BTUSB_WIDEBAND_SPEECH },
/* Qualcomm multi-subsystem chipset QCC2072 */
{ USB_DEVICE(0x0cf3, 0xea00), .driver_info = BTUSB_QUALCOMM |
BTUSB_WIDEBAND_SPEECH },
/* Broadcom BCM2035 */
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
@ -1396,6 +1402,31 @@ static int btusb_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
static int btusb_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
u8 pkt_type = hci_skb_pkt_type(skb);
int ret;
switch (pkt_type) {
case HCI_EVENT_PKT:
ret = btusb_recv_event(hdev, skb);
break;
case HCI_ACLDATA_PKT:
ret = btusb_recv_acl(hdev, skb);
break;
case HCI_SCODATA_PKT:
case HCI_ISODATA_PKT:
ret = hci_recv_frame(hdev, skb);
break;
default:
dev_kfree_skb_irq(skb);
ret = -EINVAL;
break;
}
return ret;
}
/* Dispatch through the btusb_recv_* wrappers so that vendor specific
* handling (data->recv_event, data->recv_acl) is preserved in H:4 mode.
*/
@ -4284,6 +4315,68 @@ static struct hci_drv btusb_hci_drv = {
.specific_handlers = btusb_hci_drv_specific_handlers,
};
/*
* ============================================================================
* Qualcomm support
* ============================================================================
*/
static int btusb_recv_intr_qcom(struct btusb_data *data, void *buffer, int count)
{
unsigned long flags;
int err = 0;
spin_lock_irqsave(&data->rxlock, flags);
data->evt_skb = btusb_qcom_recv_intr(data->hdev, data->evt_skb, buffer, count,
&err);
spin_unlock_irqrestore(&data->rxlock, flags);
return err;
}
static int btusb_recv_bulk_qcom(struct btusb_data *data, void *buffer, int count)
{
unsigned long flags;
int err = 0;
spin_lock_irqsave(&data->rxlock, flags);
data->acl_skb = btusb_qcom_recv_bulk(data->hdev, data->acl_skb, buffer, count,
&err);
spin_unlock_irqrestore(&data->rxlock, flags);
return err;
}
static int btusb_send_vendor_frame_qcom(struct hci_dev *hdev, struct sk_buff *skb)
{
u8 pkt_type = hci_skb_pkt_type(skb);
struct urb *urb;
switch (pkt_type) {
case QPERI_COMMAND_PKT:
urb = alloc_ctrl_urb(hdev, skb);
break;
case QPERI_ACLDATA_PKT:
urb = alloc_bulk_urb(hdev, skb);
break;
default:
return -EILSEQ;
}
if (IS_ERR(urb))
return PTR_ERR(urb);
return submit_or_queue_tx_urb(hdev, urb);
}
/*
* ============================================================================
* Qualcomm support end
* ============================================================================
*/
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@ -4414,6 +4507,9 @@ static int btusb_probe(struct usb_interface *intf,
} else if (id->driver_info & BTUSB_QCA_WCN6855) {
/* Allocate extra space for QCA WCN6855 device */
priv_size += sizeof(struct btqca_data);
} else if (id->driver_info & BTUSB_QUALCOMM) {
/* Allocate extra space for Qualcomm device */
priv_size += btusb_qcom_hdev_priv_size();
}
data->recv_acl = hci_recv_frame;
@ -4572,6 +4668,31 @@ static int btusb_probe(struct usb_interface *intf,
hci_set_msft_opcode(hdev, 0xFD70);
}
if (id->driver_info & BTUSB_QUALCOMM) {
struct btusb_qcom *xport_data;
if (!IS_ENABLED(CONFIG_BT_HCIBTUSB_QCOM)) {
err = -ENODEV;
bt_dev_err(hdev, "CONFIG_BT_HCIBTUSB_QCOM not enabled");
goto err_kill_tx_urbs;
}
xport_data = btusb_qcom_xport_data(hdev);
xport_data->reset_gpio = data->reset_gpio;
xport_data->prepare_reset = btusb_prepare_reset;
xport_data->recv_bt_frame = btusb_recv_frame;
xport_data->send_bt_frame = btusb_send_frame;
xport_data->send_vendor_frame = btusb_send_vendor_frame_qcom;
data->recv_intr = btusb_recv_intr_qcom;
data->recv_bulk = btusb_recv_bulk_qcom;
data->disconnect = btusb_qcom_disconnect;
hdev->manufacturer = 0x1d;
hdev->send = btusb_qcom_send_frame;
hdev->setup = btusb_qcom_setup;
}
if (id->driver_info & BTUSB_AMP) {
/* AMP controllers do not support SCO packets */
data->isoc = NULL;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Qualcomm Bluetooth USB transport-specific support
*
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef __BTUSB_QCOM_H
#define __BTUSB_QCOM_H
#include <linux/types.h>
#include <linux/mutex.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <net/bluetooth/hci_core.h>
#define QPERI_COMMAND_PKT 0x31
#define QPERI_ACLDATA_PKT 0x32
#define QPERI_EVENT_PKT 0x34
struct btusb_qcom {
__u16 idVendor;
__u16 idProduct;
struct usb_device *udev;
struct usb_interface *intf;
struct gpio_desc *reset_gpio;
/* serializes sending BT and vendor frames to the transport */
struct mutex tx_mutex;
void (*prepare_reset)(struct hci_dev *hdev);
int (*recv_bt_frame)(struct hci_dev *hdev, struct sk_buff *skb);
int (*send_bt_frame)(struct hci_dev *hdev, struct sk_buff *skb);
int (*send_vendor_frame)(struct hci_dev *hdev, struct sk_buff *skb);
};
#if IS_ENABLED(CONFIG_BT_HCIBTUSB_QCOM)
int btusb_qcom_hdev_priv_size(void);
struct btusb_qcom *btusb_qcom_xport_data(struct hci_dev *hdev);
int btusb_qcom_setup(struct hci_dev *hdev);
int btusb_qcom_disconnect(struct hci_dev *hdev);
int btusb_qcom_send_frame(struct hci_dev *hdev, struct sk_buff *skb);
struct sk_buff *btusb_qcom_recv_intr(struct hci_dev *hdev, struct sk_buff *skb,
void *buffer, int count, int *err);
struct sk_buff *btusb_qcom_recv_bulk(struct hci_dev *hdev, struct sk_buff *skb,
void *buffer, int count, int *err);
#else
static inline int btusb_qcom_hdev_priv_size(void)
{
return 0;
}
static inline struct btusb_qcom *btusb_qcom_xport_data(struct hci_dev *hdev)
{
return NULL;
}
static inline int btusb_qcom_setup(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btusb_qcom_disconnect(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btusb_qcom_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
static inline struct sk_buff *btusb_qcom_recv_intr(struct hci_dev *hdev,
struct sk_buff *skb,
void *buffer, int count,
int *err)
{
*err = -EOPNOTSUPP;
return NULL;
}
static inline struct sk_buff *btusb_qcom_recv_bulk(struct hci_dev *hdev,
struct sk_buff *skb,
void *buffer, int count,
int *err)
{
*err = -EOPNOTSUPP;
return NULL;
}
#endif
#endif