rtk_btusb: Update driver version to 3.1.65ab490.20240531-141726

- Update driver to 3.1.65ab490.20240531-141726
- Add support for 8852CE

Bug 4915378

Change-Id: Ib3f695047ee306fd07db424239c985195619aad1
Signed-off-by: Shobek Attupurath <sattupurath@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3237077
(cherry picked from commit 3d15bb40b5)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3233099
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
This commit is contained in:
Shobek Attupurath
2024-10-18 17:54:13 +00:00
committed by Jon Hunter
parent 13859c153d
commit 1f6ba48285
7 changed files with 1930 additions and 607 deletions

View File

@@ -1,24 +1,15 @@
# SPDX-License-Identifier: GPL-2.0 ifneq ($(KERNELRELEASE),)
CONFIG_BTUSB_AUTOSUSPEND = n
CONFIG_BTUSB_WAKEUP_HOST = n
CONFIG_BTCOEX = y
ifeq ($(CONFIG_BTUSB_AUTOSUSPEND), y)
EXTRA_CFLAGS += -DCONFIG_BTUSB_AUTOSUSPEND
endif
ifeq ($(CONFIG_BTUSB_WAKEUP_HOST), y)
EXTRA_CFLAGS += -DCONFIG_BTUSB_WAKEUP_HOST
endif
ifeq ($(CONFIG_BTCOEX), y)
EXTRA_CFLAGS += -DCONFIG_BTCOEX
endif
ifndef CONFIG_TEGRA_SYSTEM_TYPE_ACK
obj-m := rtk_btusb.o obj-m := rtk_btusb.o
rtk_btusb-objs := rtk_coex.o \ rtk_btusb-y = rtk_coex.o rtk_misc.o rtk_bt.o
rtk_misc.o \ else
rtk_bt.o PWD := $(shell pwd)
KVER := $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order *.a
endif endif

View File

@@ -1,8 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* *
* Realtek Bluetooth USB driver * Realtek Bluetooth USB driver
* *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@@ -22,7 +36,7 @@
#include "rtk_bt.h" #include "rtk_bt.h"
#include "rtk_misc.h" #include "rtk_misc.h"
#define VERSION "3.1.6fd4e69.20220818-105856" #define VERSION "3.1.65ab490.20240531-141726"
#ifdef BTCOEX #ifdef BTCOEX
#include "rtk_coex.h" #include "rtk_coex.h"
@@ -35,95 +49,64 @@ static DEFINE_SEMAPHORE(switch_sem);
#endif #endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1) #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
static bool reset = 0; static bool reset = true;
#endif #endif
static struct usb_driver btusb_driver; static struct usb_driver btusb_driver;
static struct usb_device_id btusb_table[] = { #if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static u16 iso_min_conn_handle = 0x1b;
#endif
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
/* Generic Bluetooth USB interface */
{ USB_INTERFACE_INFO(0xe0, 0x01, 0x01) },
{}
};
static const struct usb_device_id blacklist_table[] = {
{ {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0bda, .idVendor = 0x0bda,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x13d3, .idVendor = 0x13d3,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0489, .idVendor = 0x0489,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x1358, .idVendor = 0x1358,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x04ca, .idVendor = 0x04ca,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x2ff8, .idVendor = 0x2ff8,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0b05, .idVendor = 0x0b05,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0930, .idVendor = 0x0930,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x10ec, .idVendor = 0x10ec,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x04c5, .idVendor = 0x04c5,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0cb5, .idVendor = 0x0cb5,
.bInterfaceClass = 0xe0,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01
}, { }, {
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0cb8, .idVendor = 0x0cb8,
.bInterfaceClass = 0xe0, }, {
.bInterfaceSubClass = 0x01, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,
.bInterfaceProtocol = 0x01 .idVendor = 0x04b8,
}, { } }, { }
}; };
@@ -148,20 +131,6 @@ static struct btusb_data *rtk_alloc(struct usb_interface *intf)
MODULE_DEVICE_TABLE(usb, btusb_table); MODULE_DEVICE_TABLE(usb, btusb_table);
static int inc_tx(struct btusb_data *data)
{
unsigned long flags;
int rv;
spin_lock_irqsave(&data->txlock, flags);
rv = test_bit(BTUSB_SUSPENDING, &data->flags);
if (!rv)
data->tx_in_flight++;
spin_unlock_irqrestore(&data->txlock, flags);
return rv;
}
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
static inline void btusb_free_frags(struct btusb_data *data) static inline void btusb_free_frags(struct btusb_data *data)
{ {
@@ -204,7 +173,11 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
} }
len = min_t(uint, bt_cb(skb)->expect, count); len = min_t(uint, bt_cb(skb)->expect, count);
#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
skb_put_data(skb, buffer, len);
#else
memcpy(skb_put(skb, len), buffer, len); memcpy(skb_put(skb, len), buffer, len);
#endif
count -= len; count -= len;
buffer += len; buffer += len;
@@ -259,15 +232,26 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
} }
len = min_t(uint, bt_cb(skb)->expect, count); len = min_t(uint, bt_cb(skb)->expect, count);
#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
skb_put_data(skb, buffer, len);
#else
memcpy(skb_put(skb, len), buffer, len); memcpy(skb_put(skb, len), buffer, len);
#endif
count -= len; count -= len;
buffer += len; buffer += len;
bt_cb(skb)->expect -= len; bt_cb(skb)->expect -= len;
if (skb->len == HCI_ACL_HDR_SIZE) { if (skb->len == HCI_ACL_HDR_SIZE) {
__le16 dlen = hci_acl_hdr(skb)->dlen; struct hci_acl_hdr *h = hci_acl_hdr(skb);
__le16 dlen = h->dlen;
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
__u16 handle = __le16_to_cpu(h->handle) & 0xfff;
if(handle >= iso_min_conn_handle) {
bt_cb(skb)->pkt_type = HCI_ISODATA_PKT;
}
#endif
/* Complete ACL header */ /* Complete ACL header */
bt_cb(skb)->expect = __le16_to_cpu(dlen); bt_cb(skb)->expect = __le16_to_cpu(dlen);
@@ -293,10 +277,42 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
return err; return err;
} }
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
static int btrtl_usb_recv_isoc(u16 pos, u8 *data, u8 *p, int len,
u16 wMaxPacketSize)
{
u8 *prev;
if (pos >= HCI_SCO_HDR_SIZE && pos >= wMaxPacketSize &&
len == wMaxPacketSize && !(pos % wMaxPacketSize) &&
wMaxPacketSize >= 10 && p[0] == data[0] && p[1] == data[1]) {
prev = data + (pos - wMaxPacketSize);
/* Detect the sco data of usb isoc pkt duplication. */
if (!memcmp(p + 2, prev + 2, 8))
return -EILSEQ;
if (wMaxPacketSize >= 12 &&
p[2] == prev[6] && p[3] == prev[7] &&
p[4] == prev[4] && p[5] == prev[5] &&
p[6] == prev[10] && p[7] == prev[11] &&
p[8] == prev[8] && p[9] == prev[9]) {
return -EILSEQ;
}
}
return 0;
}
#endif
static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
{ {
struct sk_buff *skb; struct sk_buff *skb;
int err = 0; int err = 0;
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
u16 wMaxPacketSize = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize);
#endif
spin_lock(&data->rxlock); spin_lock(&data->rxlock);
skb = data->sco_skb; skb = data->sco_skb;
@@ -316,7 +332,24 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
} }
len = min_t(uint, bt_cb(skb)->expect, count); len = min_t(uint, bt_cb(skb)->expect, count);
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
/* Gaps in audio could be heard while streaming WBS using USB
* alt settings 3 on some platforms.
* Add the function to detect it.
*/
if (test_bit(BTUSB_USE_ALT3_FOR_WBS, &data->flags)) {
err = btrtl_usb_recv_isoc(skb->len, skb->data, buffer,
len, wMaxPacketSize);
if (err)
break;
}
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
skb_put_data(skb, buffer, len);
#else
memcpy(skb_put(skb, len), buffer, len); memcpy(skb_put(skb, len), buffer, len);
#endif
count -= len; count -= len;
buffer += len; buffer += len;
@@ -347,6 +380,21 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
return err; return err;
} }
#else
static int inc_tx(struct btusb_data *data)
{
unsigned long flags;
int rv;
spin_lock_irqsave(&data->txlock, flags);
rv = test_bit(BTUSB_SUSPENDING, &data->flags);
if (!rv)
data->tx_in_flight++;
spin_unlock_irqrestore(&data->txlock, flags);
return rv;
}
#endif #endif
static void btusb_intr_complete(struct urb *urb) static void btusb_intr_complete(struct urb *urb)
@@ -626,6 +674,51 @@ retry:
} }
} }
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len,
int mtu, struct btusb_data *data)
{
int i = 0, offset = 0;
unsigned int interval;
BT_DBG("len %d mtu %d", len, mtu);
/* For mSBC ALT 6 settings some Realtek chips need to transmit the data
* continuously without the zero length of USB packets.
*/
if (btrealtek_test_flag(data->hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP))
goto ignore_usb_alt6_packet_flow;
/* For mSBC ALT 6 setting the host will send the packet at continuous
* flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting
* 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets.
* To maintain the rate we send 63bytes of usb packets alternatively for
* 7ms and 8ms to maintain the rate as 7.5ms.
*/
if (data->usb_alt6_packet_flow) {
interval = 7;
data->usb_alt6_packet_flow = false;
} else {
interval = 6;
data->usb_alt6_packet_flow = true;
}
for (i = 0; i < interval; i++) {
urb->iso_frame_desc[i].offset = offset;
urb->iso_frame_desc[i].length = offset;
}
ignore_usb_alt6_packet_flow:
if (len && i < BTUSB_MAX_ISOC_FRAMES) {
urb->iso_frame_desc[i].offset = offset;
urb->iso_frame_desc[i].length = len;
i++;
}
urb->number_of_packets = i;
}
#endif
static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu) static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
{ {
int i, offset = 0; int i, offset = 0;
@@ -675,6 +768,12 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress); pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 2, 14)
usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
hdev, data->isoc_rx_ep->bInterval);
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
#else
urb->dev = data->udev; urb->dev = data->udev;
urb->pipe = pipe; urb->pipe = pipe;
urb->context = hdev; urb->context = hdev;
@@ -684,6 +783,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP; urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
urb->transfer_buffer = buf; urb->transfer_buffer = buf;
urb->transfer_buffer_length = size; urb->transfer_buffer_length = size;
#endif
__fill_isoc_descriptor(urb, size, __fill_isoc_descriptor(urb, size,
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize)); le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
@@ -751,6 +851,49 @@ done:
kfree_skb(skb); kfree_skb(skb);
} }
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static int rtl_read_iso_handle_range(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct __rp {
u8 status;
u8 min_handle[2];
} *rp;
int ret = -EIO;
skb = __hci_cmd_sync(hdev, 0xfdab, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
return PTR_ERR(skb);
}
/* FIXME: if the return status is not zero, __hci_cmd_sync() would
* return an error and we would not reach here.
*/
if (skb->data[0]) {
RTKBT_ERR("%s: Read failed, status %0x", hdev->name,
skb->data[0]);
goto err;
}
if (skb->len < sizeof(*rp)) {
RTKBT_WARN("%s: The len %u of rp is too short", __func__,
skb->len);
goto err;
}
rp = (void *)skb->data;
iso_min_conn_handle = (u16)rp->min_handle[1] << 8 | rp->min_handle[0];
RTKBT_DBG("ISO handle range (handle >= %04x)", iso_min_conn_handle);
kfree_skb(skb);
return 0;
err:
kfree_skb(skb);
return ret;
}
#endif
static int btusb_open(struct hci_dev *hdev) static int btusb_open(struct hci_dev *hdev)
{ {
struct btusb_data *data = GET_DRV_DATA(hdev); struct btusb_data *data = GET_DRV_DATA(hdev);
@@ -775,6 +918,10 @@ static int btusb_open(struct hci_dev *hdev)
goto failed; goto failed;
/*******************************/ /*******************************/
err = setup_btrealtek_flag(data->intf, hdev);
if (err < 0)
RTKBT_WARN("setup_btrealtek_flag incorrect!");
RTKBT_INFO("%s set HCI UP RUNNING", __func__); RTKBT_INFO("%s set HCI UP RUNNING", __func__);
if (test_and_set_bit(HCI_UP, &hdev->flags)) if (test_and_set_bit(HCI_UP, &hdev->flags))
goto done; goto done;
@@ -817,6 +964,32 @@ failed:
return err; return err;
} }
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
static int btusb_setup(struct hci_dev *hdev)
{
rtl_read_iso_handle_range(hdev);
return 0;
}
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
static int btusb_shutdown(struct hci_dev *hdev)
{
struct sk_buff *skb;
int ret;
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
bt_dev_err(hdev, "HCI reset during shutdown failed");
return ret;
}
kfree_skb(skb);
return 0;
}
#endif
static void btusb_stop_traffic(struct btusb_data *data) static void btusb_stop_traffic(struct btusb_data *data)
{ {
mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek
@@ -920,8 +1093,152 @@ static const char pkt_ind[][8] = {
[HCI_COMMAND_PKT] = "cmd", [HCI_COMMAND_PKT] = "cmd",
[HCI_ACLDATA_PKT] = "acl", [HCI_ACLDATA_PKT] = "acl",
[HCI_SCODATA_PKT] = "sco", [HCI_SCODATA_PKT] = "sco",
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
[HCI_ISODATA_PKT] = "iso",
#endif
}; };
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct usb_ctrlrequest *dr;
struct urb *urb;
unsigned int pipe;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return ERR_PTR(-ENOMEM);
dr = kmalloc(sizeof(*dr), GFP_KERNEL);
if (!dr) {
usb_free_urb(urb);
return ERR_PTR(-ENOMEM);
}
dr->bRequestType = data->cmdreq_type;
dr->bRequest = 0;
dr->wIndex = 0;
dr->wValue = 0;
dr->wLength = __cpu_to_le16(skb->len);
pipe = usb_sndctrlpipe(data->udev, 0x00);
usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
skb->data, skb->len, btusb_tx_complete, skb);
skb->dev = (void *)hdev;
return urb;
}
static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct urb *urb;
unsigned int pipe;
if (!data->bulk_tx_ep)
return ERR_PTR(-ENODEV);
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return ERR_PTR(-ENOMEM);
pipe = usb_sndbulkpipe(data->udev, data->bulk_tx_ep->bEndpointAddress);
usb_fill_bulk_urb(urb, data->udev, pipe,
skb->data, skb->len, btusb_tx_complete, skb);
skb->dev = (void *)hdev;
return urb;
}
static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
struct urb *urb;
unsigned int pipe;
if (!data->isoc_tx_ep)
return ERR_PTR(-ENODEV);
urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
if (!urb)
return ERR_PTR(-ENOMEM);
pipe = usb_sndisocpipe(data->udev, data->isoc_tx_ep->bEndpointAddress);
usb_fill_int_urb(urb, data->udev, pipe,
skb->data, skb->len, btusb_isoc_tx_complete,
skb, data->isoc_tx_ep->bInterval);
urb->transfer_flags = URB_ISO_ASAP;
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
if (data->isoc_altsetting == 6)
__fill_isoc_descriptor_msbc(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize),
data);
else
__fill_isoc_descriptor(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
#else
__fill_isoc_descriptor(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
#endif
skb->dev = (void *)hdev;
return urb;
}
static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
int err;
usb_anchor_urb(urb, &data->tx_anchor);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err < 0) {
if (err != -EPERM && err != -ENODEV)
RTKBT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
kfree(urb->setup_packet);
usb_unanchor_urb(urb);
} else {
usb_mark_last_busy(data->udev);
}
usb_free_urb(urb);
return err;
}
static int submit_or_queue_tx_urb(struct hci_dev *hdev, struct urb *urb)
{
struct btusb_data *data = hci_get_drvdata(hdev);
unsigned long flags;
bool suspending;
spin_lock_irqsave(&data->txlock, flags);
suspending = test_bit(BTUSB_SUSPENDING, &data->flags);
if (!suspending)
data->tx_in_flight++;
spin_unlock_irqrestore(&data->txlock, flags);
if (!suspending)
return submit_tx_urb(hdev, urb);
usb_anchor_urb(urb, &data->deferred);
schedule_work(&data->waker);
usb_free_urb(urb);
return 0;
}
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{ {
@@ -931,14 +1248,20 @@ int btusb_send_frame(struct sk_buff *skb)
struct hci_dev *hdev = (struct hci_dev *)skb->dev; struct hci_dev *hdev = (struct hci_dev *)skb->dev;
#endif #endif
struct urb *urb;
#if HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
struct btusb_data *data = GET_DRV_DATA(hdev); struct btusb_data *data = GET_DRV_DATA(hdev);
struct usb_ctrlrequest *dr; struct usb_ctrlrequest *dr;
struct urb *urb;
unsigned int pipe; unsigned int pipe;
int err; int err;
#endif
// RTKBT_DBG("%s", hdev->name); // RTKBT_DBG("%s", hdev->name);
/* After Kernel version 4.4.0, move the check into the
* hci_send_frame function before calling hdev->send
*/
#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
if (!test_bit(HCI_RUNNING, &hdev->flags)) { if (!test_bit(HCI_RUNNING, &hdev->flags)) {
/* If the parameter is wrong, the hdev isn't the correct /* If the parameter is wrong, the hdev isn't the correct
* one. Then no HCI commands can be sent. * one. Then no HCI commands can be sent.
@@ -946,13 +1269,15 @@ int btusb_send_frame(struct sk_buff *skb)
RTKBT_ERR("HCI is not running"); RTKBT_ERR("HCI is not running");
return -EBUSY; return -EBUSY;
} }
#endif
/* Before kernel/hci version 3.13.0, the skb->dev is set before /* Before kernel/hci version 3.13.0, the skb->dev is set before
* entering btusb_send_frame(). So there is no need to set it here. * entering btusb_send_frame(). So there is no need to set it here.
* *
* The skb->dev will be used in the callbacks when urb transfer * The skb->dev will be used in the callbacks when urb transfer
* completes. See btusb_tx_complete() and btusb_isoc_tx_complete() */ * completes. See btusb_tx_complete() and btusb_isoc_tx_complete() */
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) && \
HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
skb->dev = (void *)hdev; skb->dev = (void *)hdev;
#endif #endif
@@ -963,6 +1288,14 @@ int btusb_send_frame(struct sk_buff *skb)
#ifdef BTCOEX #ifdef BTCOEX
rtk_btcoex_parse_cmd(skb->data, skb->len); rtk_btcoex_parse_cmd(skb->data, skb->len);
#endif #endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
urb = alloc_ctrl_urb(hdev, skb);
if (IS_ERR(urb))
return PTR_ERR(urb);
hdev->stat.cmd_tx++;
return submit_or_queue_tx_urb(hdev, urb);
#else
urb = usb_alloc_urb(0, GFP_ATOMIC); urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) if (!urb)
return -ENOMEM; return -ENOMEM;
@@ -988,11 +1321,24 @@ int btusb_send_frame(struct sk_buff *skb)
hdev->stat.cmd_tx++; hdev->stat.cmd_tx++;
break; break;
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
case HCI_ISODATA_PKT:
#endif
case HCI_ACLDATA_PKT: case HCI_ACLDATA_PKT:
print_acl(skb, 1); print_acl(skb, 1);
#ifdef BTCOEX #ifdef BTCOEX
if(bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT)
rtk_btcoex_parse_l2cap_data_tx(skb->data, skb->len); rtk_btcoex_parse_l2cap_data_tx(skb->data, skb->len);
#endif #endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
urb = alloc_bulk_urb(hdev, skb);
if (IS_ERR(urb))
return PTR_ERR(urb);
hdev->stat.acl_tx++;
return submit_or_queue_tx_urb(hdev, urb);
#else
if (!data->bulk_tx_ep) if (!data->bulk_tx_ep)
return -ENODEV; return -ENODEV;
@@ -1009,7 +1355,22 @@ int btusb_send_frame(struct sk_buff *skb)
hdev->stat.acl_tx++; hdev->stat.acl_tx++;
break; break;
#endif
case HCI_SCODATA_PKT: case HCI_SCODATA_PKT:
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
if (hci_conn_num(hdev, SCO_LINK) < 1)
return -ENODEV;
urb = alloc_isoc_urb(hdev, skb);
if (IS_ERR(urb))
return PTR_ERR(urb);
hdev->stat.sco_tx++;
return submit_tx_urb(hdev, urb);
}
return -EILSEQ;
#else
if (!data->isoc_tx_ep || SCO_NUM < 1) if (!data->isoc_tx_ep || SCO_NUM < 1)
return -ENODEV; return -ENODEV;
@@ -1035,6 +1396,7 @@ int btusb_send_frame(struct sk_buff *skb)
default: default:
return -EILSEQ; return -EILSEQ;
} }
err = inc_tx(data); err = inc_tx(data);
@@ -1057,12 +1419,14 @@ skip_waking:
} else { } else {
usb_mark_last_busy(data->udev); usb_mark_last_busy(data->udev);
} }
usb_free_urb(urb);
done: done:
usb_free_urb(urb);
return err; return err;
#endif
} }
#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) #if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
static void btusb_destruct(struct hci_dev *hdev) static void btusb_destruct(struct hci_dev *hdev)
{ {
@@ -1181,6 +1545,9 @@ static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data,
BT_DBG("Looking for Alt no :%d", alt); BT_DBG("Looking for Alt no :%d", alt);
if (!intf)
return NULL;
for (i = 0; i < intf->num_altsetting; i++) { for (i = 0; i < intf->num_altsetting; i++) {
if (intf->altsetting[i].desc.bAlternateSetting == alt) if (intf->altsetting[i].desc.bAlternateSetting == alt)
return &intf->altsetting[i]; return &intf->altsetting[i];
@@ -1221,7 +1588,14 @@ static void btusb_work(struct work_struct *work)
new_alts = data->sco_num; new_alts = data->sco_num;
} }
} else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) { } else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) {
new_alts = btusb_find_altsetting(data, 6) ? 6 : 1; if (btusb_find_altsetting(data, 6))
new_alts = 6;
else if (btusb_find_altsetting(data, 3) &&
hdev->sco_mtu >= 72 &&
test_bit(BTUSB_USE_ALT3_FOR_WBS, &data->flags))
new_alts = 3;
else
new_alts = 1;
} }
if (btusb_switch_alt_setting(hdev, new_alts) < 0) if (btusb_switch_alt_setting(hdev, new_alts) < 0)
@@ -1421,7 +1795,7 @@ static int rtkbt_pm_notify(struct notifier_block *notifier,
result = __rtk_send_hci_cmd(udev, cmd, 3); result = __rtk_send_hci_cmd(udev, cmd, 3);
kfree(cmd); kfree(cmd);
msleep(100); /* From FW colleague's recommendation */ msleep(100); /* From FW colleague's recommendation */
result = download_lps_patch(intf); result = download_special_patch(intf, "lps_");
#endif #endif
#ifdef RTKBT_TV_POWERON_WHITELIST #ifdef RTKBT_TV_POWERON_WHITELIST
@@ -1540,13 +1914,23 @@ static int btusb_probe(struct usb_interface *intf,
struct usb_device *udev; struct usb_device *udev;
udev = interface_to_usbdev(intf); udev = interface_to_usbdev(intf);
RTKBT_DBG("btusb_probe intf->cur_altsetting->desc.bInterfaceNumber %d", RTKBT_INFO("btusb_probe intf->cur_altsetting->desc.bInterfaceNumber %d",
intf->cur_altsetting->desc.bInterfaceNumber); intf->cur_altsetting->desc.bInterfaceNumber);
/* interface numbers are hardcoded in the spec */ /* interface numbers are hardcoded in the spec */
if (intf->cur_altsetting->desc.bInterfaceNumber != 0) if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return -ENODEV; return -ENODEV;
if (!id->driver_info) {
const struct usb_device_id *match;
match = usb_match_id(intf, blacklist_table);
if (match)
id = match;
else
return -ENODEV;
}
/*******************************/ /*******************************/
flag1 = device_can_wakeup(&udev->dev); flag1 = device_can_wakeup(&udev->dev);
flag2 = device_may_wakeup(&udev->dev); flag2 = device_may_wakeup(&udev->dev);
@@ -1636,6 +2020,13 @@ static int btusb_probe(struct usb_interface *intf,
hdev->flush = btusb_flush; hdev->flush = btusb_flush;
hdev->send = btusb_send_frame; hdev->send = btusb_send_frame;
hdev->notify = btusb_notify; hdev->notify = btusb_notify;
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)
hdev->setup = btusb_setup;
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
hdev->shutdown = btusb_shutdown;
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
hci_set_drvdata(hdev, data); hci_set_drvdata(hdev, data);
@@ -1645,10 +2036,14 @@ static int btusb_probe(struct usb_interface *intf,
hdev->owner = THIS_MODULE; hdev->owner = THIS_MODULE;
#endif #endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
set_bit(BTUSB_USE_ALT3_FOR_WBS, &data->flags);
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1) #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
if (!reset) if (!reset)
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
RTKBT_DBG("set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);");
#endif #endif
/* Interface numbers are hardcoded in the specification */ /* Interface numbers are hardcoded in the specification */

View File

@@ -1,12 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* *
* Realtek Bluetooth USB driver * Realtek Bluetooth USB driver
* *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/ */
#ifndef __RTK_BT_H__
#define __RTK_BT_H__
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
@@ -30,14 +41,17 @@
/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */ /* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */
#define HCI_VERSION_CODE LINUX_VERSION_CODE #define HCI_VERSION_CODE LINUX_VERSION_CODE
#ifdef CONFIG_BTCOEX #define CONFIG_BTCOEX 1
#define CONFIG_BTUSB_WAKEUP_HOST 0
#if CONFIG_BTCOEX
#define BTCOEX #define BTCOEX
#endif #endif
/*********************************** /***********************************
** Realtek - For rtk_btusb driver ** ** Realtek - For rtk_btusb driver **
***********************************/ ***********************************/
#ifdef CONFIG_BTUSB_WAKEUP_HOST #if CONFIG_BTUSB_WAKEUP_HOST
#define BTUSB_WAKEUP_HOST #define BTUSB_WAKEUP_HOST
#endif #endif
@@ -84,6 +98,7 @@ int btusb_send_frame(struct sk_buff *skb);
#define BTUSB_ISOC_RUNNING 2 #define BTUSB_ISOC_RUNNING 2
#define BTUSB_SUSPENDING 3 #define BTUSB_SUSPENDING 3
#define BTUSB_DID_ISO_RESUME 4 #define BTUSB_DID_ISO_RESUME 4
#define BTUSB_USE_ALT3_FOR_WBS 15
struct btusb_data { struct btusb_data {
struct hci_dev *hdev; struct hci_dev *hdev;
@@ -125,6 +140,7 @@ struct btusb_data {
#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) #if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
unsigned int air_mode; unsigned int air_mode;
bool usb_alt6_packet_flow;
#endif #endif
int isoc_altsetting; int isoc_altsetting;
int suspend_count; int suspend_count;
@@ -136,6 +152,3 @@ struct btusb_data {
struct notifier_block shutdown_notifier; struct notifier_block shutdown_notifier;
void *context; void *context;
}; };
#endif /* __RTK_BT_H__ */

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* *
* Realtek Bluetooth USB driver * Realtek Bluetooth USB driver
* *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/ */
#ifndef __RTK_COEX_H__
#define __RTK_COEX_H__
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
#include <linux/list.h> #include <linux/list.h>
@@ -38,6 +49,13 @@
#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 #define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03
#define HCI_EV_LE_ENHANCED_CONN_COMPLETE 0x0a #define HCI_EV_LE_ENHANCED_CONN_COMPLETE 0x0a
#define HCI_EV_LE_CIS_EST 0x19
#define HCI_EV_LE_CREATE_BIG_CPL 0x1b
#define HCI_EV_LE_TERM_BIG_CPL 0x1c
#define HCI_EV_LE_BIG_SYNC_EST 0x1d
#define HCI_EV_LE_BIG_SYNC_LOST 0x1e
#define HCI_EV_LE_REMOVE_ISO_DATA_PATH 0x23
//vendor cmd to fw //vendor cmd to fw
#define HCI_VENDOR_ENABLE_PROFILE_REPORT_COMMAND 0xfc18 #define HCI_VENDOR_ENABLE_PROFILE_REPORT_COMMAND 0xfc18
#define HCI_VENDOR_SET_PROFILE_REPORT_LEGACY_COMMAND 0xfc19 #define HCI_VENDOR_SET_PROFILE_REPORT_LEGACY_COMMAND 0xfc19
@@ -140,7 +158,11 @@ enum {
profile_hogp = 5, profile_hogp = 5,
profile_voice = 6, profile_voice = 6,
profile_sink = 7, profile_sink = 7,
profile_max = 8 profile_lea_src = 8,
profile_opprx = 9,
profile_lea_snk = 10,
profile_a2dpsink = 11,
profile_max = 12
}; };
#define A2DP_SIGNAL 0x01 #define A2DP_SIGNAL 0x01
@@ -159,7 +181,10 @@ typedef struct {
//profile info for each connection //profile info for each connection
typedef struct rtl_hci_conn { typedef struct rtl_hci_conn {
struct list_head list; struct list_head list;
uint16_t handle; u16 big_handle;
u16 handle;
u8 direction;
u8 remove_path;
struct delayed_work a2dp_count_work; struct delayed_work a2dp_count_work;
struct delayed_work pan_count_work; struct delayed_work pan_count_work;
struct delayed_work hogp_count_work; struct delayed_work hogp_count_work;
@@ -170,7 +195,7 @@ typedef struct rtl_hci_conn {
uint8_t type; // 0:l2cap, 1:sco/esco, 2:le uint8_t type; // 0:l2cap, 1:sco/esco, 2:le
uint16_t profile_bitmap; uint16_t profile_bitmap;
uint16_t profile_status; uint16_t profile_status;
int8_t profile_refcount[8]; int8_t profile_refcount[profile_max];
} rtk_conn_prof, *prtk_conn_prof; } rtk_conn_prof, *prtk_conn_prof;
#ifdef RTB_SOFTWARE_MAILBOX #ifdef RTB_SOFTWARE_MAILBOX
@@ -204,21 +229,40 @@ struct rtl_btinfo_ctl {
}; };
#endif /* RTB_SOFTWARE_MAILBOX */ #endif /* RTB_SOFTWARE_MAILBOX */
#define HCI_PT_CMD 0x01
#define HCI_PT_EVT 0x02
#define HCI_PT_L2SIG_RX 0x03
#define HCI_PT_L2SIG_TX 0x04
#define HCI_PT_L2DATA_RX 0x05
#define HCI_PT_L2DATA_TX 0x06
struct rtl_hci_hdr {
struct list_head list;
u8 type;
u16 len;
};
#define MAX_LEN_OF_HCI_EV 32 #define MAX_LEN_OF_HCI_EV 32
#define NUM_RTL_HCI_EV 32 #define NUM_RTL_HCI_EV 32
struct rtl_hci_ev { struct rtl_hci_ev {
__u8 data[MAX_LEN_OF_HCI_EV];
__u16 len;
struct list_head list; struct list_head list;
u8 type;
u16 len;
/* private */
__u8 data[MAX_LEN_OF_HCI_EV];
}; };
#define L2_MAX_SUBSEC_LEN 128 #define L2_MAX_SUBSEC_LEN 128
#define L2_MAX_PKTS 16 #define L2_MAX_PKTS 16
struct rtl_l2_buff { struct rtl_l2_buff {
__u8 data[L2_MAX_SUBSEC_LEN];
__u16 len;
__u16 out;
struct list_head list; struct list_head list;
u8 type;
u16 len;
/* private */
__u8 data[L2_MAX_SUBSEC_LEN];
__u16 out;
}; };
struct rtl_coex_struct { struct rtl_coex_struct {
@@ -236,18 +280,18 @@ struct rtl_coex_struct {
struct delayed_work sock_work; struct delayed_work sock_work;
#endif #endif
struct workqueue_struct *fw_wq; struct workqueue_struct *fw_wq;
struct workqueue_struct *timer_wq;
struct delayed_work fw_work; struct delayed_work fw_work;
struct delayed_work l2_work; struct delayed_work cmd_work;
#ifdef RTB_SOFTWARE_MAILBOX #ifdef RTB_SOFTWARE_MAILBOX
struct sock *sk; struct sock *sk;
#endif #endif
struct urb *urb; struct urb *urb;
spinlock_t spin_lock_sock; spinlock_t spin_lock_sock;
spinlock_t spin_lock_profile; struct mutex profile_mutex;
struct mutex conn_mutex;
uint16_t profile_bitmap; uint16_t profile_bitmap;
uint16_t profile_status; uint16_t profile_status;
int8_t profile_refcount[8]; int8_t profile_refcount[profile_max];
uint8_t ispairing; uint8_t ispairing;
uint8_t isinquirying; uint8_t isinquirying;
uint8_t ispaging; uint8_t ispaging;
@@ -266,11 +310,8 @@ struct rtl_coex_struct {
uint8_t wifi_on; uint8_t wifi_on;
uint8_t sock_open; uint8_t sock_open;
#endif #endif
unsigned long cmd_last_tx;
/* hci ev buff */ unsigned long cmd_last_tx;
struct list_head ev_used_list;
struct list_head ev_free_list;
spinlock_t rxlock; spinlock_t rxlock;
__u8 pkt_type; __u8 pkt_type;
@@ -279,16 +320,18 @@ struct rtl_coex_struct {
__u16 elen; __u16 elen;
__u8 back_buff[HCI_MAX_EVENT_SIZE]; __u8 back_buff[HCI_MAX_EVENT_SIZE];
/* l2cap rx buff */ struct list_head ev_free_list;
struct list_head l2_used_list;
struct list_head l2_free_list; struct list_head l2_free_list;
struct list_head hci_pkt_list;
/* buff addr and size */ /* buff addr and size */
spinlock_t buff_lock; spinlock_t buff_lock;
unsigned long pages_addr; unsigned long pages_addr;
unsigned long buff_size; unsigned long buff_size;
#define RTL_COEX_RUNNING (1 << 0) #define RTL_COEX_RUNNING 1
#define RTL_COEX_PKT_COUNTING 2
#define RTL_COEX_CONN_REMOVING 3
unsigned long flags; unsigned long flags;
}; };
@@ -363,6 +406,3 @@ void rtk_btcoex_close(void);
void rtk_btcoex_probe(struct hci_dev *hdev); void rtk_btcoex_probe(struct hci_dev *hdev);
void rtk_btcoex_init(void); void rtk_btcoex_init(void);
void rtk_btcoex_exit(void); void rtk_btcoex_exit(void);
#endif /* __RTK_COEX_H__ */

View File

@@ -1,8 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* *
* Realtek Bluetooth USB download firmware driver * Realtek Bluetooth USB download firmware driver
* *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
@@ -30,6 +44,7 @@
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/version.h> #include <linux/version.h>
@@ -54,7 +69,7 @@ struct cfg_list_item {
struct list_head list; struct list_head list;
u16 offset; u16 offset;
u8 len; u8 len;
u8 data[]; u8 data[0];
}; };
static struct list_head list_configs; static struct list_head list_configs;
@@ -84,6 +99,7 @@ static struct list_head list_extracfgs;
#define HCI_VENDOR_READ_RTK_ROM_VERISION 0xfc6d #define HCI_VENDOR_READ_RTK_ROM_VERISION 0xfc6d
#define HCI_VENDOR_READ_LMP_VERISION 0x1001 #define HCI_VENDOR_READ_LMP_VERISION 0x1001
#define HCI_VENDOR_READ_CMD 0xfc61 #define HCI_VENDOR_READ_CMD 0xfc61
#define HCI_VENDOR_WRITE_CMD 0xfc62
#define ROM_LMP_NONE 0x0000 #define ROM_LMP_NONE 0x0000
#define ROM_LMP_8723a 0x1200 #define ROM_LMP_8723a 0x1200
@@ -92,6 +108,9 @@ static struct list_head list_extracfgs;
#define ROM_LMP_8761a 0X8761 #define ROM_LMP_8761a 0X8761
#define ROM_LMP_8822b 0X8822 #define ROM_LMP_8822b 0X8822
#define ROM_LMP_8852a 0x8852 #define ROM_LMP_8852a 0x8852
#define ROM_LMP_8851b 0x8851
#define ROM_LMP_8922a 0x8922
#define ROM_LMP_8723c 0x8703
#define PATCH_SNIPPETS 0x01 #define PATCH_SNIPPETS 0x01
#define PATCH_DUMMY_HEADER 0x02 #define PATCH_DUMMY_HEADER 0x02
@@ -163,41 +182,30 @@ static const uint8_t RTK_EPATCH_SIGNATURE_NEW[8] =
//Extension Section IGNATURE:0x77FD0451 //Extension Section IGNATURE:0x77FD0451
static const uint8_t Extension_Section_SIGNATURE[4] = { 0x51, 0x04, 0xFD, 0x77 }; static const uint8_t Extension_Section_SIGNATURE[4] = { 0x51, 0x04, 0xFD, 0x77 };
static uint16_t project_id[] = { static const struct {
ROM_LMP_8723a, __u16 lmp_subver;
ROM_LMP_8723b, __u8 id;
ROM_LMP_8821a, } project_id_to_lmp_subver[] = {
ROM_LMP_8761a, { ROM_LMP_8723a, 0 },
ROM_LMP_NONE, { ROM_LMP_8723b, 1 },
ROM_LMP_NONE, { ROM_LMP_8821a, 2 },
ROM_LMP_NONE, { ROM_LMP_8761a, 3 },
ROM_LMP_NONE, { ROM_LMP_8723c, 7 },
ROM_LMP_8822b, { ROM_LMP_8822b, 8 }, /* 8822B */
ROM_LMP_8723b, /* RTL8723DU */ { ROM_LMP_8723b, 9 }, /* 8723D */
ROM_LMP_8821a, /* RTL8821CU */ { ROM_LMP_8821a, 10 }, /* 8821C */
ROM_LMP_NONE, { ROM_LMP_8822b, 13 }, /* 8822C */
ROM_LMP_NONE, { ROM_LMP_8761a, 14 }, /* 8761B */
ROM_LMP_8822b, /* RTL8822CU */ { ROM_LMP_8852a, 18 }, /* 8852A */
ROM_LMP_8761a, /* index 14 for 8761BU */ { ROM_LMP_8723b, 19 }, /* 8733B */
ROM_LMP_NONE, { ROM_LMP_8852a, 20 }, /* 8852B */
ROM_LMP_NONE, { ROM_LMP_8852a, 25 }, /* 8852C */
ROM_LMP_NONE, { ROM_LMP_8822b, 33 }, /* 8822E */
ROM_LMP_8852a, /* index 18 for 8852AU */ { ROM_LMP_8851b, 36 }, /* 8851B */
ROM_LMP_8723b, /* index 19 for 8723FU */ { ROM_LMP_8852a, 42 }, /* 8852D */
ROM_LMP_8852a, /* index 20 for 8852BU */ { ROM_LMP_8922a, 44 }, /* 8922A */
ROM_LMP_NONE, { ROM_LMP_8852a, 47 }, /* 8852BT */
ROM_LMP_NONE, { ROM_LMP_8761a, 51 }, /* 8761C */
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_8852a, /* index 25 for 8852CU */
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_8822b, /* index 33 for 8822EU */
}; };
enum rtk_endpoit { enum rtk_endpoit {
@@ -215,10 +223,16 @@ enum rtk_endpoit {
#define RTL8822CU 0x73 #define RTL8822CU 0x73
#define RTL8761BU 0x74 #define RTL8761BU 0x74
#define RTL8852AU 0x75 #define RTL8852AU 0x75
#define RTL8723FU 0x76 #define RTL8733BU 0x76
#define RTL8852BU 0x77 #define RTL8852BU 0x77
#define RTL8852CU 0x78 #define RTL8852CU 0x78
#define RTL8822EU 0x79 #define RTL8822EU 0x79
#define RTL8851BU 0x7A
#define RTL8852DU 0x7B
#define RTL8922AU 0x7C
#define RTL8852BTU 0x7D
#define RTL8761CU 0x80
#define RTL8723CU 0x81
typedef struct { typedef struct {
uint16_t prod_id; uint16_t prod_id;
@@ -279,7 +293,7 @@ static uint8_t g_key_id = 0;
static dev_data *dev_data_find(struct usb_interface *intf); static dev_data *dev_data_find(struct usb_interface *intf);
static patch_info *get_patch_entry(struct usb_device *udev); static patch_info *get_patch_entry(struct usb_device *udev);
static int load_firmware(dev_data * dev_entry, uint8_t ** buff); static int load_firmware(dev_data *dev_entry, xchange_data *xdata);
static void init_xdata(xchange_data * xdata, dev_data * dev_entry); static void init_xdata(xchange_data * xdata, dev_data * dev_entry);
static int check_fw_version(xchange_data * xdata); static int check_fw_version(xchange_data * xdata);
static int download_data(xchange_data * xdata); static int download_data(xchange_data * xdata);
@@ -344,6 +358,8 @@ static patch_info fw_patch_table[] = {
{0xb009, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU */ {0xb009, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU */
{0x0231, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU for LiteOn */ {0x0231, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU for LiteOn */
{0xb703, 0x8703, "mp_rtl8723cu_fw", "rtl8723cu_fw", "rtl8723cu_config", RTL8723CU}, /* RTL8723CU */
{0xb820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CU */ {0xb820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CU */
{0xc820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CU */ {0xc820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CU */
{0xc821, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ {0xc821, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */
@@ -377,6 +393,7 @@ static patch_info fw_patch_table[] = {
{0xc82e, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */ {0xc82e, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */
{0xc81d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */ {0xc81d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */
{0xd820, 0x8822, "mp_rtl8821du_fw", "rtl8821du_fw", "rtl8821du_config", RTL8822CU}, /* RTL8821DU */ {0xd820, 0x8822, "mp_rtl8821du_fw", "rtl8821du_fw", "rtl8821du_config", RTL8822CU}, /* RTL8821DU */
{0x053b, 0x8822, "mp_rtl8821du_fw", "rtl8821du_fw", "rtl8821du_config", RTL8822CU}, /* RTL8821DU for Epson*/
{0xc822, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ {0xc822, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */
{0xc82b, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ {0xc82b, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */
@@ -405,6 +422,7 @@ static patch_info fw_patch_table[] = {
{0xc03f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE-VS */ {0xc03f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE-VS */
{0x8771, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", RTL8761BU}, /* RTL8761BU only */ {0x8771, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", RTL8761BU}, /* RTL8761BU only */
{0x876e, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", RTL8761BU}, /* RTL8761BUE */
{0xa725, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU */ {0xa725, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU */
{0xa72A, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU BT only */ {0xa72A, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU BT only */
@@ -425,14 +443,13 @@ static patch_info fw_patch_table[] = {
{0xc125, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {0xc125, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0xe852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {0xe852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0xb852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {0xb852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0xc852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0xc549, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {0xc549, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0xc127, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {0xc127, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0x3565, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {0x3565, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */
{0xb733, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */ {0xb733, 0x8723, "mp_rtl8733bu_fw", "rtl8733bu_fw", "rtl8733bu_config", RTL8733BU}, /* RTL8733BU */
{0xb73a, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */ {0xb73a, 0x8723, "mp_rtl8733bu_fw", "rtl8733bu_fw", "rtl8733bu_config", RTL8733BU}, /* RTL8733BU */
{0xf72b, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */ {0xf72b, 0x8723, "mp_rtl8733bu_fw", "rtl8733bu_fw", "rtl8733bu_config", RTL8733BU}, /* RTL8733BU */
{0x8851, 0x8852, "mp_rtl8851au_fw", "rtl8851au_fw", "rtl8851au_config", RTL8852BU}, /* RTL8851AU */ {0x8851, 0x8852, "mp_rtl8851au_fw", "rtl8851au_fw", "rtl8851au_config", RTL8852BU}, /* RTL8851AU */
{0xa85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BU */ {0xa85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BU */
@@ -453,6 +470,7 @@ static patch_info fw_patch_table[] = {
{0x1670, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ {0x1670, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */
{0xc85a, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CU */ {0xc85a, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CU */
{0xc85d, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CU */
{0x0852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ {0x0852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */
{0x5852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ {0x5852, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */
{0xc85c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ {0xc85c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */
@@ -464,6 +482,23 @@ static patch_info fw_patch_table[] = {
{0xe822, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", RTL8822EU}, /* RTL8822EU */ {0xe822, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", RTL8822EU}, /* RTL8822EU */
{0xa82a, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", RTL8822EU}, /* RTL8822EU */ {0xa82a, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", RTL8822EU}, /* RTL8822EU */
{0xb851, 0x8851, "mp_rtl8851bu_fw", "rtl8851bu_fw", "rtl8851bu_config", RTL8851BU}, /* RTL8851BU */
{0xd85a, 0x8852, "mp_rtl8852du_fw", "rtl8852du_fw", "rtl8852du_config", RTL8852DU}, /* RTL8852DU */
{0x892a, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AU */
{0x8922, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */
{0xa890, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */
{0xa891, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */
{0xa892, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */
{0xd922, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */
{0xb85f, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */
{0xc852, 0x8852, "mp_rtl8852btu_fw", "rtl8852btu_fw", "rtl8852btu_config", RTL8852BTU}, /* RTL8852BTU */
{0x8520, 0x8852, "mp_rtl8852btu_fw", "rtl8852btu_fw", "rtl8852btu_config", RTL8852BTU}, /* RTL8852BTE */
{0xc761, 0x8761, "mp_rtl8761cu_fw", "rtl8761cu_mx_fw", "rtl8761cu_mx_config", RTL8761CU}, /* RTL8761CU */
/* NOTE: must append patch entries above the null entry */ /* NOTE: must append patch entries above the null entry */
{0, 0, NULL, NULL, NULL, 0} {0, 0, NULL, NULL, NULL, 0}
}; };
@@ -746,6 +781,7 @@ static inline int get_max_patch_size(u8 chip_type)
max_patch_size = 25 * 1024; max_patch_size = 25 * 1024;
break; break;
case RTL8723DU: case RTL8723DU:
case RTL8723CU:
case RTL8822CU: case RTL8822CU:
case RTL8761BU: case RTL8761BU:
case RTL8821CU: case RTL8821CU:
@@ -754,10 +790,11 @@ static inline int get_max_patch_size(u8 chip_type)
case RTL8852AU: case RTL8852AU:
max_patch_size = 0x114D0 + 529; /* 69.2KB */ max_patch_size = 0x114D0 + 529; /* 69.2KB */
break; break;
case RTL8723FU: case RTL8733BU:
max_patch_size = 0xC4Cf + 529; /* 49.2KB */ max_patch_size = 0xC4Cf + 529; /* 49.2KB */
break; break;
case RTL8852BU: case RTL8852BU:
case RTL8851BU:
max_patch_size = 0x104D0 + 529; /* 65KB */ max_patch_size = 0x104D0 + 529; /* 65KB */
break; break;
case RTL8852CU: case RTL8852CU:
@@ -766,6 +803,18 @@ static inline int get_max_patch_size(u8 chip_type)
case RTL8822EU: case RTL8822EU:
max_patch_size = 0x24620 + 529; /* 145KB */ max_patch_size = 0x24620 + 529; /* 145KB */
break; break;
case RTL8852DU:
max_patch_size = 0x20D90 + 529; /* 131KB */
break;
case RTL8922AU:
max_patch_size = 0x23810 + 529; /* 142KB */
break;
case RTL8852BTU:
max_patch_size = 0x27E00 + 529; /* 159.5KB */
break;
case RTL8761CU:
max_patch_size = 1024 * 1024; /* 1MB */
break;
default: default:
max_patch_size = 40 * 1024; max_patch_size = 40 * 1024;
break; break;
@@ -774,11 +823,64 @@ static inline int get_max_patch_size(u8 chip_type)
return max_patch_size; return max_patch_size;
} }
static int rtk_vendor_write(dev_data * dev_entry)
{
int ret_val;
xchange_data *xdata = NULL;
unsigned char cmd_buf[] = {0x31, 0x90, 0xd0, 0x29, 0x80, 0x00, 0x00,
0x00, 0x00};
xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL);
if (NULL == xdata) {
ret_val = 0xFE;
RTKBT_DBG("NULL == xdata");
return -1;
}
init_xdata(xdata, dev_entry);
xdata->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_WRITE_CMD);
xdata->cmd_hdr->plen = 9;
memcpy(xdata->send_pkt, &(xdata->cmd_hdr->opcode), 2);
memcpy(xdata->send_pkt+2, &(xdata->cmd_hdr->plen), 1);
memcpy(xdata->send_pkt+3, cmd_buf, sizeof(cmd_buf));
xdata->pkt_len = CMD_HDR_LEN + 9;
ret_val = send_hci_cmd(xdata);
if (ret_val < 0) {
RTKBT_ERR("%s: Failed to send HCI command.", __func__);
goto end;
}
ret_val = rcv_hci_evt(xdata);
if (ret_val < 0) {
RTKBT_ERR("%s: Failed to receive HCI event.", __func__);
goto end;
}
ret_val = 0;
end:
if (xdata != NULL) {
if (xdata->send_pkt)
kfree(xdata->send_pkt);
if (xdata->rcv_pkt)
kfree(xdata->rcv_pkt);
kfree(xdata);
}
return ret_val;
}
static int check_fw_chip_ver(dev_data * dev_entry, xchange_data * xdata) static int check_fw_chip_ver(dev_data * dev_entry, xchange_data * xdata)
{ {
int ret_val; int ret_val;
uint16_t chip = 0; uint16_t chip = 0;
uint16_t chip_ver = 0; uint16_t chip_ver = 0;
uint16_t lmp_subver, hci_rev;
patch_info *patch_entry;
struct hci_rp_read_local_version *read_ver_rsp;
chip = rtk_vendor_read(dev_entry, READ_CHIP_TYPE); chip = rtk_vendor_read(dev_entry, READ_CHIP_TYPE);
if(chip == 0x8822) { if(chip == 0x8822) {
@@ -801,9 +903,16 @@ static int check_fw_chip_ver(dev_data * dev_entry, xchange_data * xdata)
gEVersion = rtk_get_eversion(dev_entry); gEVersion = rtk_get_eversion(dev_entry);
} }
return ret_val; return ret_val;
} else {
patch_entry = xdata->dev_entry->patch_entry;
read_ver_rsp = (struct hci_rp_read_local_version *)(xdata->rsp_para);
lmp_subver = le16_to_cpu(read_ver_rsp->lmp_subver);
hci_rev = le16_to_cpu(read_ver_rsp->hci_rev);
if (lmp_subver == 0x8852 && hci_rev == 0x000d)
ret_val = rtk_vendor_write(dev_entry);
} }
return 0; return ret_val;
} }
int download_patch(struct usb_interface *intf) int download_patch(struct usb_interface *intf)
@@ -836,7 +945,7 @@ int download_patch(struct usb_interface *intf)
if (ret_val != 0 ) if (ret_val != 0 )
goto patch_end; goto patch_end;
xdata->fw_len = load_firmware(dev_entry, &xdata->fw_data); xdata->fw_len = load_firmware(dev_entry, xdata);
if (xdata->fw_len <= 0) { if (xdata->fw_len <= 0) {
RTKBT_ERR("load firmware failed!"); RTKBT_ERR("load firmware failed!");
ret_val = -1; ret_val = -1;
@@ -875,7 +984,7 @@ int download_patch(struct usb_interface *intf)
ret_val = 0; ret_val = 0;
patch_fail: patch_fail:
kfree(fw_buf); vfree(fw_buf);
patch_end: patch_end:
if (xdata != NULL) { if (xdata != NULL) {
if (xdata->send_pkt) if (xdata->send_pkt)
@@ -893,7 +1002,7 @@ patch_end:
* -1: error * -1: error
* 0: download patch successfully * 0: download patch successfully
* >0: patch already exists */ * >0: patch already exists */
int download_lps_patch(struct usb_interface *intf) int download_special_patch(struct usb_interface *intf, const char *special_name)
{ {
dev_data *dev_entry; dev_data *dev_entry;
patch_info *pinfo; patch_info *pinfo;
@@ -935,15 +1044,18 @@ int download_lps_patch(struct usb_interface *intf)
} }
goto patch_end; goto patch_end;
} }
memset(name1, 0, sizeof(name1));
memset(name2, 0, sizeof(name2));
origin_name1 = dev_entry->patch_entry->patch_name; origin_name1 = dev_entry->patch_entry->patch_name;
origin_name2 = dev_entry->patch_entry->config_name; origin_name2 = dev_entry->patch_entry->config_name;
snprintf(name1, sizeof(name1), "lps_%s", origin_name1); memcpy(name1, special_name, strlen(special_name));
snprintf(name2, sizeof(name2), "lps_%s", origin_name2); strncat(name1, origin_name1, sizeof(name1) - 1 - strlen(special_name));
memcpy(name2, special_name, strlen(special_name));
strncat(name2, origin_name2, sizeof(name2) - 1 - strlen(special_name));
dev_entry->patch_entry->patch_name = name1; dev_entry->patch_entry->patch_name = name1;
dev_entry->patch_entry->config_name = name2; dev_entry->patch_entry->config_name = name2;
RTKBT_INFO("Loading %s and %s", name1, name2); RTKBT_INFO("Loading %s and %s", name1, name2);
xdata->fw_len = load_firmware(dev_entry, &xdata->fw_data); xdata->fw_len = load_firmware(dev_entry, xdata);
dev_entry->patch_entry->patch_name = origin_name1; dev_entry->patch_entry->patch_name = origin_name1;
dev_entry->patch_entry->config_name = origin_name2; dev_entry->patch_entry->config_name = origin_name2;
if (xdata->fw_len <= 0) { if (xdata->fw_len <= 0) {
@@ -955,11 +1067,13 @@ int download_lps_patch(struct usb_interface *intf)
fw_buf = xdata->fw_data; fw_buf = xdata->fw_data;
pinfo = dev_entry->patch_entry; pinfo = dev_entry->patch_entry;
/*
if (!pinfo) { if (!pinfo) {
RTKBT_ERR("%s: No patch entry", __func__); RTKBT_ERR("%s: No patch entry", __func__);
result = -1; result = -1;
goto patch_fail; goto patch_fail;
} }
*/
max_patch_size = get_max_patch_size(pinfo->chip_type); max_patch_size = get_max_patch_size(pinfo->chip_type);
if (xdata->fw_len > max_patch_size) { if (xdata->fw_len > max_patch_size) {
result = -1; result = -1;
@@ -998,6 +1112,39 @@ patch_end:
} }
#endif #endif
int setup_btrealtek_flag(struct usb_interface *intf, struct hci_dev *hdev)
{
dev_data *dev_entry;
patch_info *pinfo;
int ret_val = 0;
dev_entry = dev_data_find(intf);
if (NULL == dev_entry) {
ret_val = -1;
RTKBT_ERR("%s: NULL == dev_entry", __func__);
return ret_val;
}
pinfo = dev_entry->patch_entry;
if (!pinfo) {
RTKBT_ERR("%s: No patch entry", __func__);
ret_val = -1;
return ret_val;
}
switch (pinfo->chip_type){
case RTL8852CU:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
btrealtek_set_flag(hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP);
#endif
break;
default:
break;
}
return ret_val;
}
#if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SHUTDOWN_WAKEUP || defined RTKBT_SWITCH_PATCH #if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SHUTDOWN_WAKEUP || defined RTKBT_SWITCH_PATCH
int set_scan(struct usb_interface *intf) int set_scan(struct usb_interface *intf)
{ {
@@ -1045,7 +1192,7 @@ end:
dev_data *dev_data_find(struct usb_interface * intf) dev_data *dev_data_find(struct usb_interface * intf)
{ {
dev_data *dev_entry; dev_data *dev_entry = NULL;
list_for_each_entry(dev_entry, &dev_data_list, list_node) { list_for_each_entry(dev_entry, &dev_data_list, list_node) {
if (dev_entry->intf == intf) { if (dev_entry->intf == intf) {
@@ -1088,6 +1235,7 @@ static int is_mac(u8 chip_type, u16 offset)
switch (chip_type) { switch (chip_type) {
case RTL8822BU: case RTL8822BU:
case RTL8723DU: case RTL8723DU:
case RTL8723CU:
case RTL8821CU: case RTL8821CU:
if (offset == 0x0044) if (offset == 0x0044)
return 1; return 1;
@@ -1095,10 +1243,15 @@ static int is_mac(u8 chip_type, u16 offset)
case RTL8822CU: case RTL8822CU:
case RTL8761BU: case RTL8761BU:
case RTL8852AU: case RTL8852AU:
case RTL8723FU: case RTL8733BU:
case RTL8852BU: case RTL8852BU:
case RTL8852CU: case RTL8852CU:
case RTL8822EU: case RTL8822EU:
case RTL8851BU:
case RTL8852DU:
case RTL8922AU:
case RTL8852BTU:
case RTL8761CU:
if (offset == 0x0030) if (offset == 0x0030)
return 1; return 1;
break; break;
@@ -1116,15 +1269,21 @@ static uint16_t get_mac_offset(u8 chip_type)
switch (chip_type) { switch (chip_type) {
case RTL8822BU: case RTL8822BU:
case RTL8723DU: case RTL8723DU:
case RTL8723CU:
case RTL8821CU: case RTL8821CU:
return 0x0044; return 0x0044;
case RTL8822CU: case RTL8822CU:
case RTL8761BU: case RTL8761BU:
case RTL8852AU: case RTL8852AU:
case RTL8723FU: case RTL8733BU:
case RTL8852BU: case RTL8852BU:
case RTL8852CU: case RTL8852CU:
case RTL8822EU: case RTL8822EU:
case RTL8851BU:
case RTL8852DU:
case RTL8922AU:
case RTL8852BTU:
case RTL8761CU:
return 0x0030; return 0x0030;
case RTLPREVIOUS: case RTLPREVIOUS:
return 0x003c; return 0x003c;
@@ -1452,14 +1611,12 @@ static uint8_t *rtb_get_patch_header(int *len,
} }
break; break;
default: default:
RTKBT_ERR("Wrong Opcode"); RTKBT_INFO("Unknown Opcode. Ignore");
goto wrong_opcode;
} }
section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len); section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len);
} }
*len = patch_len; *len = patch_len;
wrong_opcode:
return NULL; return NULL;
} }
@@ -1743,7 +1900,7 @@ static int rtk_vendor_read(dev_data * dev_entry, uint8_t class)
xchange_data *xdata = NULL; xchange_data *xdata = NULL;
unsigned char cmd_ct_buf[] = {0x10, 0x38, 0x04, 0x28, 0x80}; unsigned char cmd_ct_buf[] = {0x10, 0x38, 0x04, 0x28, 0x80};
unsigned char cmd_cv_buf[] = {0x10, 0x3A, 0x04, 0x28, 0x80}; unsigned char cmd_cv_buf[] = {0x10, 0x3A, 0x04, 0x28, 0x80};
unsigned char cmd_sec_buf[] = {0x10, 0xA4, 0x0D, 0x00, 0xb0}; unsigned char cmd_sec_buf[] = {0x10, 0xA4, 0xAD, 0x00, 0xb0};
xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL);
if (NULL == xdata) { if (NULL == xdata) {
@@ -1811,7 +1968,6 @@ static int rtk_vendor_read(dev_data * dev_entry, uint8_t class)
} }
} }
read_end: read_end:
if (xdata != NULL) { if (xdata != NULL) {
if (xdata->send_pkt) if (xdata->send_pkt)
@@ -1823,7 +1979,57 @@ read_end:
return ret_val; return ret_val;
} }
int load_firmware(dev_data * dev_entry, uint8_t ** buff) static int needs_hci_upgrade(xchange_data *xdata, u8 *buf, u32 buf_len)
{
struct {
u8 status;
u8 subopcode;
u8 ota;
} __attribute__((packed)) *evt_params;
#define UPG_DL_BLOCK_SIZE 128
#define UPG_SUBCMD_CODE 0x01
u8 len = UPG_DL_BLOCK_SIZE;
u8 *cmd_params;
int ret;
cmd_params = xdata->req_para;
evt_params = (void *)xdata->rsp_para;
xdata->cmd_hdr->opcode = cpu_to_le16(0xfdbb);
if (buf_len < len)
len = buf_len;
xdata->cmd_hdr->plen = 1 + len;
xdata->pkt_len = sizeof(*xdata->cmd_hdr) + xdata->cmd_hdr->plen;
*cmd_params++ = UPG_SUBCMD_CODE;
memcpy(cmd_params, buf, len);
ret = send_hci_cmd(xdata);
if (ret < 0)
return ret;
ret = rcv_hci_evt(xdata);
if (ret < 0)
return ret;
if (evt_params->status) {
RTKBT_ERR("needs_hci_upgrade: status %02x", evt_params->status);
return -1;
}
if (evt_params->subopcode != UPG_SUBCMD_CODE) {
RTKBT_ERR("needs_hci_upgrade: return subopcode %02x",
evt_params->subopcode);
return -2;
}
RTKBT_INFO("needs_hci_upgrade: state %02x", evt_params->ota);
return evt_params->ota;
}
/* buff: points to the allocated buffer that stores extracted fw and config
* This function returns the total length of extracted fw and config
*/
int load_firmware(dev_data *dev_entry, xchange_data *xdata)
{ {
const struct firmware *fw; const struct firmware *fw;
struct usb_device *udev; struct usb_device *udev;
@@ -1835,12 +2041,13 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
uint8_t need_download_fw = 1; uint8_t need_download_fw = 1;
uint16_t lmp_version; uint16_t lmp_version;
struct rtk_epatch_entry current_entry = { 0 }; struct rtk_epatch_entry current_entry = { 0 };
struct list_head *pos, *next; struct list_head *pos, *next;
struct patch_node *tmp; struct patch_node *tmp;
struct patch_node patch_node_hdr; struct patch_node patch_node_hdr;
int i;
RTKBT_DBG("load_firmware start"); RTKBT_DBG("load_firmware start");
udev = dev_entry->udev; udev = dev_entry->udev;
patch_entry = dev_entry->patch_entry; patch_entry = dev_entry->patch_entry;
lmp_version = patch_entry->lmp_sub; lmp_version = patch_entry->lmp_sub;
@@ -1853,16 +2060,13 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
ret_val = request_firmware(&fw, fw_name, &udev->dev); ret_val = request_firmware(&fw, fw_name, &udev->dev);
if (ret_val < 0) { if (ret_val < 0) {
RTKBT_ERR("request_firmware error"); RTKBT_ERR("request_firmware error");
fw_len = 0;
kfree(config_file_buf);
config_file_buf = NULL;
goto fw_fail; goto fw_fail;
} }
INIT_LIST_HEAD(&patch_node_hdr.list); INIT_LIST_HEAD(&patch_node_hdr.list);
epatch_buf = kzalloc(fw->size, GFP_KERNEL); epatch_buf = vzalloc(fw->size);
if (NULL == epatch_buf) if (!epatch_buf)
goto alloc_fail; goto alloc_fail;
memcpy(epatch_buf, fw->data, fw->size); memcpy(epatch_buf, fw->data, fw->size);
@@ -1874,20 +2078,24 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
if (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) == 0) { if (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) == 0) {
RTKBT_ERR("8723a Check signature error!"); RTKBT_ERR("8723a Check signature error!");
need_download_fw = 0; need_download_fw = 0;
} else { goto sign_err;
if (!(buf = kzalloc(buf_len, GFP_KERNEL))) { }
buf = vzalloc(buf_len);
if (!buf) {
RTKBT_ERR("Can't alloc memory for fw&config"); RTKBT_ERR("Can't alloc memory for fw&config");
buf_len = -1; buf_len = -1;
} else { goto alloc_buf_err;
}
RTKBT_DBG("8723a, fw copy direct"); RTKBT_DBG("8723a, fw copy direct");
memcpy(buf, epatch_buf, fw->size); memcpy(buf, epatch_buf, fw->size);
if (config_len) { if (config_len)
memcpy(&buf[buf_len - config_len], memcpy(&buf[buf_len - config_len], config_file_buf,
config_file_buf, config_len); config_len);
goto done;
} }
}
}
} else {
RTKBT_ERR("This is not 8723a, use new patch style!"); RTKBT_ERR("This is not 8723a, use new patch style!");
/* Get version from ROM */ /* Get version from ROM */
@@ -1896,64 +2104,64 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
if (gEVersion == 0xFE) { if (gEVersion == 0xFE) {
RTKBT_ERR("%s: Read ROM version failure", __func__); RTKBT_ERR("%s: Read ROM version failure", __func__);
need_download_fw = 0; need_download_fw = 0;
fw_len = 0;
goto alloc_fail; goto alloc_fail;
} }
/* check Signature and Extension Section Field */ /* check Signature and Extension Section Field */
if (((memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) != 0) && (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) != 0))|| if ((memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) &&
memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8)) ||
memcmp(epatch_buf + buf_len - config_len - 4, memcmp(epatch_buf + buf_len - config_len - 4,
Extension_Section_SIGNATURE, 4) != 0) { Extension_Section_SIGNATURE, 4) != 0) {
RTKBT_ERR("Check SIGNATURE error! do not download fw"); RTKBT_ERR("Check SIGNATURE error! do not download fw");
need_download_fw = 0; need_download_fw = 0;
} else { goto sign_err;
proj_id = }
rtk_get_fw_project_id(epatch_buf + buf_len -
config_len - 5);
if (lmp_version != project_id[proj_id]) { proj_id = rtk_get_fw_project_id(epatch_buf + buf_len - config_len - 5);
RTKBT_ERR
("lmp_version is %x, project_id is %x, does not match!!!", for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
lmp_version, project_id[proj_id]); if (proj_id == project_id_to_lmp_subver[i].id &&
lmp_version == project_id_to_lmp_subver[i].lmp_subver) {
break;
}
}
if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
RTKBT_ERR("lmp_version %04x, project_id %u, does not match!!!",
lmp_version, proj_id);
need_download_fw = 0; need_download_fw = 0;
} else { goto proj_id_err;
RTKBT_DBG }
("lmp_version is %x, project_id is %x, match!",
lmp_version, project_id[proj_id]); RTKBT_DBG("lmp_version is %04x, project_id is %u, match!",
lmp_version, proj_id);
if (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) { if (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) {
int key_id = rtk_vendor_read(dev_entry, READ_SEC_PROJ); int key_id = rtk_vendor_read(dev_entry, READ_SEC_PROJ);
int tmp_len = 0;
RTKBT_DBG("%s: key id %d", __func__, key_id); RTKBT_DBG("%s: key id %d", __func__, key_id);
if (key_id < 0) { if (key_id < 0) {
RTKBT_ERR("%s: Read key id failure", __func__); RTKBT_ERR("%s: Read key id failure", __func__);
need_download_fw = 0; need_download_fw = 0;
fw_len = 0; fw_len = 0;
goto alloc_fail; goto extract_err;
} }
rtb_get_patch_header(&buf_len, &patch_node_hdr, epatch_buf, key_id); rtb_get_patch_header(&buf_len, &patch_node_hdr, epatch_buf,
if(buf_len == 0) key_id);
goto alloc_fail; if (!buf_len)
goto extract_err;
RTKBT_DBG("buf_len = 0x%x", buf_len); RTKBT_DBG("buf_len = 0x%x", buf_len);
buf_len += config_len; buf_len += config_len;
} else {
rtk_get_patch_entry(epatch_buf, &current_entry);
if (current_entry.patch_length == 0) buf = vzalloc(buf_len);
goto alloc_fail; if (!buf) {
RTKBT_ERR("Can't alloc memory for multi fw&config");
buf_len = current_entry.patch_length + config_len; buf_len = -1;
RTKBT_DBG("buf_len = 0x%x", buf_len); goto alloc_buf_err;
} }
if (!(buf = kzalloc(buf_len, GFP_KERNEL))) { list_for_each_safe(pos, next, &patch_node_hdr.list) {
RTKBT_ERR
("Can't alloc memory for multi fw&config");
buf_len = -1;
} else {
if(memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) {
int tmp_len = 0;
list_for_each_safe(pos, next, &patch_node_hdr.list)
{
tmp = list_entry(pos, struct patch_node, list); tmp = list_entry(pos, struct patch_node, list);
RTKBT_DBG("len = 0x%x", tmp->len); RTKBT_DBG("len = 0x%x", tmp->len);
memcpy(buf + tmp_len, tmp->payload, tmp->len); memcpy(buf + tmp_len, tmp->payload, tmp->len);
@@ -1961,50 +2169,83 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
list_del_init(pos); list_del_init(pos);
kfree(tmp); kfree(tmp);
} }
if (config_len) { if (config_len)
memcpy(&buf memcpy(&buf[buf_len - config_len], config_file_buf,
[buf_len - config_len],
config_file_buf,
config_len); config_len);
} else {
rtk_get_patch_entry(epatch_buf, &current_entry);
if (current_entry.patch_length == 0)
goto extract_err;
buf_len = current_entry.patch_length + config_len;
RTKBT_DBG("buf_len = 0x%x", buf_len);
buf = vzalloc(buf_len);
if (!buf) {
RTKBT_ERR("Can't alloc memory for multi fw&config");
buf_len = -1;
goto alloc_buf_err;
}
memcpy(buf, epatch_buf + current_entry.start_offset,
current_entry.patch_length);
/* Copy fw version */
memcpy(buf + current_entry.patch_length - 4, epatch_buf + 8, 4);
if (config_len)
memcpy(&buf[buf_len - config_len], config_file_buf,
config_len);
}
if (patch_entry->chip_type == RTL8761CU) {
if (needs_hci_upgrade(xdata, buf, buf_len) <= 0) {
if (config_len > 0) {
memmove(buf, buf + buf_len - config_len,
config_len);
buf_len = config_len;
} else {
#define FAKE_SEG_LEN 16
if (buf_len > FAKE_SEG_LEN)
buf_len = FAKE_SEG_LEN;
memset(buf, 0, buf_len);
} }
} else { } else {
memcpy(buf, /* It does not need to download config when upgrading */
epatch_buf + buf_len -= config_len;
current_entry.start_offset,
current_entry.patch_length);
memcpy(buf + current_entry.patch_length - 4, epatch_buf + 8, 4); /*fw version */
if (config_len) {
memcpy(&buf
[buf_len - config_len],
config_file_buf,
config_len);
}
}
}
}
} }
} }
done:
RTKBT_DBG("fw:%s exists, config file:%s exists", RTKBT_DBG("fw:%s exists, config file:%s exists",
(buf_len > 0) ? "" : "not", (config_len > 0) ? "" : "not"); buf_len > 0 ? "" : "not", config_len > 0 ? "" : "not");
if (buf && (buf_len > 0) && (need_download_fw)) { if (buf && buf_len > 0 && need_download_fw) {
fw_len = buf_len; fw_len = buf_len;
*buff = buf; xdata->fw_data = buf;
} }
RTKBT_DBG("load_firmware done"); RTKBT_DBG("load_firmware done");
alloc_buf_err:
extract_err:
/* Make sure all the patch nodes freed */
list_for_each_safe(pos, next, &patch_node_hdr.list) {
tmp = list_entry(pos, struct patch_node, list);
list_del_init(pos);
kfree(tmp);
}
proj_id_err:
sign_err:
alloc_fail: alloc_fail:
release_firmware(fw); release_firmware(fw);
if (epatch_buf) if (epatch_buf)
kfree(epatch_buf); vfree(epatch_buf);
fw_fail:
if (config_file_buf) if (config_file_buf)
kfree(config_file_buf); kfree(config_file_buf);
fw_fail:
if (fw_len == 0) if (fw_len == 0)
kfree(buf); vfree(buf);
return fw_len; return fw_len;
} }
@@ -2149,7 +2390,7 @@ int download_data(xchange_data * xdata)
uint8_t *pcur; uint8_t *pcur;
int pkt_len, frag_num, frag_len; int pkt_len, frag_num, frag_len;
int i, ret_val; int i, ret_val;
int j; int j = 0;
RTKBT_DBG("download_data start"); RTKBT_DBG("download_data start");
@@ -2161,12 +2402,11 @@ int download_data(xchange_data * xdata)
frag_len = PATCH_SEG_MAX; frag_len = PATCH_SEG_MAX;
for (i = 0; i < frag_num; i++) { for (i = 0; i < frag_num; i++) {
if (i > 0x7f) cmd_para->index = j++;
j = (i & 0x7f) + 1;
else if(cmd_para->index == 0x7f)
j = i; j = 1;
cmd_para->index = j;
if (i == (frag_num - 1)) { if (i == (frag_num - 1)) {
cmd_para->index |= DATA_END; cmd_para->index |= DATA_END;
frag_len = xdata->fw_len % PATCH_SEG_MAX; frag_len = xdata->fw_len % PATCH_SEG_MAX;

View File

@@ -1,11 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
/* /*
* *
* Realtek Bluetooth USB download firmware driver * Realtek Bluetooth USB download firmware driver
* *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/ */
#ifndef __RTK_MISC_H__
#define __RTK_MISC_H__
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
@@ -14,6 +26,8 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#define CONFIG_BTUSB_AUTOSUSPEND 0
/* Download LPS patch when host suspends or power off /* Download LPS patch when host suspends or power off
* LPS patch name: lps_rtl8xxx_fw * LPS patch name: lps_rtl8xxx_fw
* LPS config name: lps_rtl8xxx_config * LPS config name: lps_rtl8xxx_config
@@ -33,19 +47,21 @@
#if 1 #if 1
#define RTKBT_DBG(fmt, arg...) printk(KERN_DEBUG "rtk_btusb: " fmt "\n" , ## arg) #define RTKBT_DBG(fmt, arg...) printk(KERN_DEBUG "rtk_btusb: " fmt "\n" , ## arg)
#define RTKBT_INFO(fmt, arg...) printk(KERN_INFO "rtk_btusb: " fmt "\n" , ## arg) #define RTKBT_INFO(fmt, arg...) printk(KERN_INFO "rtk_btusb: " fmt "\n" , ## arg)
#define RTKBT_WARN(fmt, arg...) printk(KERN_DEBUG "rtk_btusb: " fmt "\n", ## arg) #define RTKBT_WARN(fmt, arg...) printk(KERN_WARNING "rtk_btusb: " fmt "\n", ## arg)
#else #else
#define RTKBT_DBG(fmt, arg...) #define RTKBT_DBG(fmt, arg...)
#endif #endif
#if 1 #if 1
#define RTKBT_ERR(fmt, arg...) printk(KERN_DEBUG "rtk_btusb: " fmt "\n" , ## arg) #define RTKBT_ERR(fmt, arg...) printk(KERN_ERR "rtk_btusb: " fmt "\n" , ## arg)
#else #else
#define RTKBT_ERR(fmt, arg...) #define RTKBT_ERR(fmt, arg...)
#endif #endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33) #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
#define USB_RPM #define USB_RPM 1
#else
#define USB_RPM 0
#endif #endif
#define CONFIG_NEEDS_BINDING #define CONFIG_NEEDS_BINDING
@@ -56,7 +72,7 @@
#endif #endif
/* USB SS */ /* USB SS */
#if (defined CONFIG_BTUSB_AUTOSUSPEND) && (defined USB_RPM) #if (CONFIG_BTUSB_AUTOSUSPEND && USB_RPM)
#define BTUSB_RPM #define BTUSB_RPM
#endif #endif
@@ -82,12 +98,41 @@ struct api_context {
int status; int status;
}; };
int download_lps_patch(struct usb_interface *intf); int download_special_patch(struct usb_interface *intf, const char *special_name);
#endif #endif
int setup_btrealtek_flag(struct usb_interface *intf, struct hci_dev *hdev);
enum {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
REALTEK_ALT6_CONTINUOUS_TX_CHIP,
#endif
__REALTEK_NUM_FLAGS,
};
struct btrealtek_data {
DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS);
};
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
static inline void *hci_get_priv(struct hci_dev *hdev)
{
return (char *)hdev + sizeof(*hdev);
}
#endif
#define btrealtek_set_flag(hdev, nr) \
do { \
struct btrealtek_data *realtek = hci_get_priv((hdev)); \
set_bit((nr), realtek->flags); \
} while (0)
#define btrealtek_get_flag(hdev) \
(((struct btrealtek_data *)hci_get_priv(hdev))->flags)
#define btrealtek_test_flag(hdev, nr) test_bit((nr), btrealtek_get_flag(hdev))
#if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SHUTDOWN_WAKEUP || defined RTKBT_SWITCH_PATCH #if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SHUTDOWN_WAKEUP || defined RTKBT_SWITCH_PATCH
int set_scan(struct usb_interface *intf); int set_scan(struct usb_interface *intf);
#endif #endif
#endif /* __RTK_MISC_H__ */