mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
be58267a34
commit
3669487308
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
4557
drivers/bluetooth/btusb_qcom.c
Normal file
4557
drivers/bluetooth/btusb_qcom.c
Normal file
File diff suppressed because it is too large
Load Diff
99
drivers/bluetooth/btusb_qcom.h
Normal file
99
drivers/bluetooth/btusb_qcom.h
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user