From 721d4eefeed73dbcdfe5fc4a386232d0a5fc41ed Mon Sep 17 00:00:00 2001 From: payton Date: Tue, 12 Dec 2023 19:22:56 +0800 Subject: [PATCH] =?UTF-8?q?1.=E8=93=9D=E7=89=99=E9=A9=B1=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../na51089_evb_cardv_defconfig_release | 7 +- BSP/linux-kernel/drivers/bluetooth/Kconfig | 10 + BSP/linux-kernel/drivers/bluetooth/Makefile | 22 +- BSP/linux-kernel/drivers/bluetooth/rtk_bt.c | 2309 +++++++++++++++ BSP/linux-kernel/drivers/bluetooth/rtk_bt.h | 151 + BSP/linux-kernel/drivers/bluetooth/rtk_coex.c | 14 +- BSP/linux-kernel/drivers/bluetooth/rtk_coex.h | 3 +- BSP/linux-kernel/drivers/bluetooth/rtk_misc.c | 2593 +++++++++++++++++ BSP/linux-kernel/drivers/bluetooth/rtk_misc.h | 134 + 9 files changed, 5230 insertions(+), 13 deletions(-) create mode 100755 BSP/linux-kernel/drivers/bluetooth/rtk_bt.c create mode 100755 BSP/linux-kernel/drivers/bluetooth/rtk_bt.h create mode 100755 BSP/linux-kernel/drivers/bluetooth/rtk_misc.c create mode 100755 BSP/linux-kernel/drivers/bluetooth/rtk_misc.h diff --git a/BSP/linux-kernel/arch/arm/configs/na51089_evb_cardv_defconfig_release b/BSP/linux-kernel/arch/arm/configs/na51089_evb_cardv_defconfig_release index 991eafdac..5e6849477 100755 --- a/BSP/linux-kernel/arch/arm/configs/na51089_evb_cardv_defconfig_release +++ b/BSP/linux-kernel/arch/arm/configs/na51089_evb_cardv_defconfig_release @@ -670,12 +670,9 @@ CONFIG_BT_DEBUGFS=y # Bluetooth device drivers # # CONFIG_BT_HCIBTUSB is not set +CONFIG_BT_HCIBTUSB_RTLBTUSB=m # CONFIG_BT_HCIBTSDIO is not set -CONFIG_BT_HCIUART=y -CONFIG_BT_HCIUART_H4=y -# CONFIG_BT_HCIUART_BCSP is not set -CONFIG_BT_HCIUART_RTKH5=y -# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUART is not set # CONFIG_BT_HCIBCM203X is not set # CONFIG_BT_HCIBPA10X is not set # CONFIG_BT_HCIBFUSB is not set diff --git a/BSP/linux-kernel/drivers/bluetooth/Kconfig b/BSP/linux-kernel/drivers/bluetooth/Kconfig index 03576faf3..a6e2fae0c 100644 --- a/BSP/linux-kernel/drivers/bluetooth/Kconfig +++ b/BSP/linux-kernel/drivers/bluetooth/Kconfig @@ -13,6 +13,16 @@ config BT_HCIBTUSB Say Y here to compile support for Bluetooth USB devices into the kernel or say M to compile it as module (btusb). +config BT_HCIBTUSB_RTLBTUSB + tristate "Realtek HCI USB driver support + depends on USB + help + Realtek Bluetooth HCI USB driver. + This driver is required if you want to use Realtek Bluetooth + device with USB interface. + Say Y here to compile support for Bluetooth USB devices into the + kernel or say M to compile it as module (rtk_btusb). + config BT_HCIBTSDIO tristate "HCI SDIO driver" depends on MMC diff --git a/BSP/linux-kernel/drivers/bluetooth/Makefile b/BSP/linux-kernel/drivers/bluetooth/Makefile index a45ee4403..1afcef21f 100644 --- a/BSP/linux-kernel/drivers/bluetooth/Makefile +++ b/BSP/linux-kernel/drivers/bluetooth/Makefile @@ -1,8 +1,22 @@ -ifneq ($(KERNELRELEASE),) - obj-m := hci_uart.o - hci_uart-y := hci_ldisc.o hci_h4.o hci_rtk_h5.o rtk_coex.o - #EXTRA_CFLAGS += -DDEBUG +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 + +ifneq ($(KERNELRELEASE),) + obj-m := rtk_btusb.o + rtk_btusb-y = rtk_coex.o rtk_misc.o rtk_bt.o else PWD := $(shell pwd) KVER := $(shell uname -r) diff --git a/BSP/linux-kernel/drivers/bluetooth/rtk_bt.c b/BSP/linux-kernel/drivers/bluetooth/rtk_bt.c new file mode 100755 index 000000000..b00b464da --- /dev/null +++ b/BSP/linux-kernel/drivers/bluetooth/rtk_bt.c @@ -0,0 +1,2309 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtk_bt.h" +#include "rtk_misc.h" + +#define VERSION "3.1.0a23ffd.20230801-094611" + +#ifdef BTCOEX +#include "rtk_coex.h" +#endif + +#ifdef RTKBT_SWITCH_PATCH +#include +#include +static DEFINE_SEMAPHORE(switch_sem); +#endif + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1) +static bool reset = true; +#endif + +static struct usb_driver btusb_driver; +#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, + .idVendor = 0x0bda, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x13d3, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x0489, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x1358, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x04ca, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x2ff8, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x0b05, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x0930, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x10ec, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x04c5, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x0cb5, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x0cb8, + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x04b8, + }, { } +}; + +static void rtk_free(struct btusb_data *data) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1) + kfree(data); +#endif + return; +} + +static struct btusb_data *rtk_alloc(struct usb_interface *intf) +{ + struct btusb_data *data; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 1) + data = kzalloc(sizeof(*data), GFP_KERNEL); +#else + data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL); +#endif + return data; +} + +MODULE_DEVICE_TABLE(usb, btusb_table); + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) +static inline void btusb_free_frags(struct btusb_data *data) +{ + unsigned long flags; + + spin_lock_irqsave(&data->rxlock, flags); + + kfree_skb(data->evt_skb); + data->evt_skb = NULL; + + kfree_skb(data->acl_skb); + data->acl_skb = NULL; + + kfree_skb(data->sco_skb); + data->sco_skb = NULL; + + spin_unlock_irqrestore(&data->rxlock, flags); +} + +static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) +{ + struct sk_buff *skb; + int err = 0; + + spin_lock(&data->rxlock); + skb = data->evt_skb; + + while (count) { + int len; + + if (!skb) { + skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC); + if (!skb) { + err = -ENOMEM; + break; + } + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE; + } + + 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); +#endif + + count -= len; + buffer += len; + bt_cb(skb)->expect -= len; + + if (skb->len == HCI_EVENT_HDR_SIZE) { + /* Complete event header */ + bt_cb(skb)->expect = hci_event_hdr(skb)->plen; + + if (skb_tailroom(skb) < bt_cb(skb)->expect) { + kfree_skb(skb); + skb = NULL; + + err = -EILSEQ; + break; + } + } + + if (bt_cb(skb)->expect == 0) { + /* Complete frame */ + hci_recv_frame(data->hdev, skb); + skb = NULL; + } + } + + data->evt_skb = skb; + spin_unlock(&data->rxlock); + + return err; +} + +static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) +{ + struct sk_buff *skb; + int err = 0; + + spin_lock(&data->rxlock); + skb = data->acl_skb; + + while (count) { + int len; + + if (!skb) { + skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!skb) { + err = -ENOMEM; + break; + } + + bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; + bt_cb(skb)->expect = HCI_ACL_HDR_SIZE; + } + + 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); +#endif + + count -= len; + buffer += len; + bt_cb(skb)->expect -= len; + + if (skb->len == HCI_ACL_HDR_SIZE) { + 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 */ + bt_cb(skb)->expect = __le16_to_cpu(dlen); + + if (skb_tailroom(skb) < bt_cb(skb)->expect) { + kfree_skb(skb); + skb = NULL; + + err = -EILSEQ; + break; + } + } + + if (bt_cb(skb)->expect == 0) { + /* Complete frame */ + hci_recv_frame(data->hdev, skb); + skb = NULL; + } + } + + data->acl_skb = skb; + spin_unlock(&data->rxlock); + + 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) +{ + struct sk_buff *skb; + 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); + skb = data->sco_skb; + + while (count) { + int len; + + if (!skb) { + skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC); + if (!skb) { + err = -ENOMEM; + break; + } + + bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; + bt_cb(skb)->expect = HCI_SCO_HDR_SIZE; + } + + 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); +#endif + + count -= len; + buffer += len; + bt_cb(skb)->expect -= len; + + if (skb->len == HCI_SCO_HDR_SIZE) { + /* Complete SCO header */ + bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen; + + if (skb_tailroom(skb) < bt_cb(skb)->expect) { + kfree_skb(skb); + skb = NULL; + + err = -EILSEQ; + break; + } + } + + if (bt_cb(skb)->expect == 0) { + /* Complete frame */ + hci_recv_frame(data->hdev, skb); + skb = NULL; + } + } + + data->sco_skb = skb; + spin_unlock(&data->rxlock); + + 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 + +static void btusb_intr_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = GET_DRV_DATA(hdev); + int err; + + //RTKBT_DBG("%s: urb %p status %d count %d ", __func__, + //urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; + + if (urb->status == 0) { + hdev->stat.byte_rx += urb->actual_length; + +#ifdef BTCOEX + rtk_btcoex_parse_event(urb->transfer_buffer, + urb->actual_length); +#endif +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + if (hci_recv_fragment(hdev, HCI_EVENT_PKT, + urb->transfer_buffer, + urb->actual_length) < 0) { + RTKBT_ERR("%s: Corrupted event packet", __func__); + hdev->stat.err_rx++; + } +#else + if (btusb_recv_intr(data, urb->transfer_buffer, + urb->actual_length) < 0) { + RTKBT_ERR("%s corrupted event packet", hdev->name); + hdev->stat.err_rx++; + } +#endif + } + /* Avoid suspend failed when usb_kill_urb */ + else if (urb->status == -ENOENT) { + return; + } + + if (!test_bit(BTUSB_INTR_RUNNING, &data->flags)) + return; + + usb_mark_last_busy(data->udev); + usb_anchor_urb(urb, &data->intr_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected */ + if (err != -EPERM && err != -ENODEV) + RTKBT_ERR("%s: Failed to re-submit urb %p, err %d", + __func__, urb, err); + usb_unanchor_urb(urb); + } +} + +static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size; + + //RTKBT_DBG("%s", hdev->name); + + if (!data->intr_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, mem_flags); + if (!urb) + return -ENOMEM; + + size = le16_to_cpu(data->intr_ep->wMaxPacketSize); + + buf = kmalloc(size, mem_flags); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress); + + usb_fill_int_urb(urb, data->udev, pipe, buf, size, + btusb_intr_complete, hdev, data->intr_ep->bInterval); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &data->intr_anchor); + + err = usb_submit_urb(urb, mem_flags); + if (err < 0) { + RTKBT_ERR + ("btusb_submit_intr_urb %s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_bulk_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = GET_DRV_DATA(hdev); + int err; + + //RTKBT_DBG("%s: urb %p status %d count %d", + //__func__, urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; + +#ifdef BTCOEX + if (urb->status == 0) + rtk_btcoex_parse_l2cap_data_rx(urb->transfer_buffer, + urb->actual_length); +#endif + + if (urb->status == 0) { + hdev->stat.byte_rx += urb->actual_length; + +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT, + urb->transfer_buffer, + urb->actual_length) < 0) { + RTKBT_ERR("%s: Corrupted ACL packet", __func__); + hdev->stat.err_rx++; + } +#else + if (data->recv_bulk(data, urb->transfer_buffer, + urb->actual_length) < 0) { + RTKBT_ERR("%s corrupted ACL packet", hdev->name); + hdev->stat.err_rx++; + } +#endif + } + /* Avoid suspend failed when usb_kill_urb */ + else if (urb->status == -ENOENT) { + return; + } + + if (!test_bit(BTUSB_BULK_RUNNING, &data->flags)) + return; + + usb_anchor_urb(urb, &data->bulk_anchor); + usb_mark_last_busy(data->udev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected */ + if (err != -EPERM && err != -ENODEV) + RTKBT_ERR + ("btusb_bulk_complete %s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } +} + +static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = HCI_MAX_FRAME_SIZE; + + //RTKBT_DBG("%s: hdev name %s", __func__, hdev->name); + + if (!data->bulk_rx_ep) + return -ENODEV; + + urb = usb_alloc_urb(0, mem_flags); + if (!urb) + return -ENOMEM; + + buf = kmalloc(size, mem_flags); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, data->udev, pipe, + buf, size, btusb_bulk_complete, hdev); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_mark_last_busy(data->udev); + usb_anchor_urb(urb, &data->bulk_anchor); + + err = usb_submit_urb(urb, mem_flags); + if (err < 0) { + RTKBT_ERR("%s: Failed to submit urb %p, err %d", __func__, urb, + err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_isoc_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = GET_DRV_DATA(hdev); + int i, err; + + /* + RTKBT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); + */ + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; + + if (urb->status == 0) { + for (i = 0; i < urb->number_of_packets; i++) { + unsigned int offset = urb->iso_frame_desc[i].offset; + unsigned int length = + urb->iso_frame_desc[i].actual_length; + + if (urb->iso_frame_desc[i].status) + continue; + + hdev->stat.byte_rx += length; + +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + if (hci_recv_fragment(hdev, HCI_SCODATA_PKT, + urb->transfer_buffer + offset, + length) < 0) { + RTKBT_ERR("%s: Corrupted SCO packet", __func__); + hdev->stat.err_rx++; + } +#else + if (btusb_recv_isoc(data, urb->transfer_buffer + offset, + length) < 0) { + RTKBT_ERR("%s corrupted SCO packet", + hdev->name); + hdev->stat.err_rx++; + } +#endif + } + } + /* Avoid suspend failed when usb_kill_urb */ + else if (urb->status == -ENOENT) { + return; + } + + if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags)) + return; + + usb_anchor_urb(urb, &data->isoc_anchor); + i = 0; +retry: + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + /* -EPERM: urb is being killed; + * -ENODEV: device got disconnected */ + if (err != -EPERM && err != -ENODEV) + RTKBT_ERR + ("%s: Failed to re-sumbit urb %p, retry %d, err %d", + __func__, urb, i, err); + if (i < 10) { + i++; + mdelay(1); + goto retry; + } + + usb_unanchor_urb(urb); + } +} + +#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) +{ + int i, offset = 0; + + //RTKBT_DBG("len %d mtu %d", len, mtu); + + for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu; + i++, offset += mtu, len -= mtu) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = mtu; + } + + 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; +} + +static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size; + + //RTKBT_DBG("%s", hdev->name); + + if (!data->isoc_rx_ep) + return -ENODEV; + + urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags); + if (!urb) + return -ENOMEM; + + size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) * + BTUSB_MAX_ISOC_FRAMES; + + buf = kmalloc(size, mem_flags); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + 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->pipe = pipe; + urb->context = hdev; + urb->complete = btusb_isoc_complete; + urb->interval = data->isoc_rx_ep->bInterval; + + urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP; + urb->transfer_buffer = buf; + urb->transfer_buffer_length = size; +#endif + + __fill_isoc_descriptor(urb, size, + le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize)); + + usb_anchor_urb(urb, &data->isoc_anchor); + + err = usb_submit_urb(urb, mem_flags); + if (err < 0) { + RTKBT_ERR("%s %s urb %p submission failed (%d)", + __func__, hdev->name, urb, err); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct hci_dev *hdev = (struct hci_dev *)skb->dev; + struct btusb_data *data = GET_DRV_DATA(hdev); + +// RTKBT_DBG("btusb_tx_complete %s urb %p status %d count %d", hdev->name, +// urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto done; + + if (!urb->status) + hdev->stat.byte_tx += urb->transfer_buffer_length; + else + hdev->stat.err_tx++; + +done: + spin_lock(&data->txlock); + data->tx_in_flight--; + spin_unlock(&data->txlock); + + kfree(urb->setup_packet); + + kfree_skb(skb); +} + +static void btusb_isoc_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct hci_dev *hdev = (struct hci_dev *)skb->dev; + + RTKBT_DBG("%s: urb %p status %d count %d", __func__, + urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto done; + + if (!urb->status) + hdev->stat.byte_tx += urb->transfer_buffer_length; + else + hdev->stat.err_tx++; + +done: + kfree(urb->setup_packet); + + 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; + + skb = __hci_cmd_sync(hdev, 0xfdab, 0, NULL, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + return PTR_ERR(skb); + } + + if (skb->data[0]) { + RTKBT_ERR("%s: rtl: read failed", hdev->name); + kfree_skb(skb); + return -EIO; + } + + iso_min_conn_handle = skb->data[1] | skb->data[2] << 8; + RTKBT_DBG("rtl: read iso handle range done"); + + kfree_skb(skb); + + return 0; +} +#endif + +static int btusb_open(struct hci_dev *hdev) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + int err; + + err = usb_autopm_get_interface(data->intf); + if (err < 0) + return err; + + data->intf->needs_remote_wakeup = 1; + RTKBT_DBG("%s start", __func__); + + /*******************************/ + if (0 == atomic_read(&hdev->promisc)) { + RTKBT_ERR("btusb_open hdev->promisc ==0"); + //err = -1; + //goto failed; + } + + err = download_patch(data->intf); + if (err < 0) + 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__); + if (test_and_set_bit(HCI_UP, &hdev->flags)) + goto done; + + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) + goto done; + + if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) + goto done; + + err = btusb_submit_intr_urb(hdev, GFP_KERNEL); + if (err < 0) + goto failed; + + err = btusb_submit_bulk_urb(hdev, GFP_KERNEL); + if (err < 0) { + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_kill_anchored_urbs(&data->intr_anchor); + goto failed; + } + + set_bit(BTUSB_BULK_RUNNING, &data->flags); + btusb_submit_bulk_urb(hdev, GFP_KERNEL); + +done: + usb_autopm_put_interface(data->intf); + +#ifdef BTCOEX + rtk_btcoex_open(hdev); +#endif + RTKBT_DBG("%s end", __FUNCTION__); + + return 0; + +failed: + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + clear_bit(HCI_RUNNING, &hdev->flags); + usb_autopm_put_interface(data->intf); + RTKBT_ERR("%s failed", __FUNCTION__); + 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) +{ + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_kill_anchored_urbs(&data->intr_anchor); + usb_kill_anchored_urbs(&data->bulk_anchor); + usb_kill_anchored_urbs(&data->isoc_anchor); +} + +static int btusb_close(struct hci_dev *hdev) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + int err; + +#if HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + int i; +#endif + + /* When in kernel 4.4.0 and greater, the HCI_RUNNING bit is + * cleared in hci_dev_do_close(). */ +#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; +#else + if (test_bit(HCI_RUNNING, &hdev->flags)) { + RTKBT_ERR("HCI_RUNNING is not cleared before."); + return -1; + } +#endif + + RTKBT_DBG("btusb_close"); +#if HCI_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + /*******************************/ + for (i = 0; i < NUM_REASSEMBLY; i++) { + if (hdev->reassembly[i]) { + kfree_skb(hdev->reassembly[i]); + hdev->reassembly[i] = NULL; + RTKBT_DBG("%s free ressembly i=%d", __FUNCTION__, i); + } + } + /*******************************/ +#endif + cancel_work_sync(&data->work); + cancel_work_sync(&data->waker); + + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + + btusb_stop_traffic(data); +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + btusb_free_frags(data); +#endif + + err = usb_autopm_get_interface(data->intf); + if (err < 0) + goto failed; + + data->intf->needs_remote_wakeup = 0; + usb_autopm_put_interface(data->intf); + +#ifdef BTCOEX + rtk_btcoex_close(); +#endif + +failed: + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_scuttle_anchored_urbs(&data->deferred); + +#ifdef RTKBT_SWITCH_PATCH + down(&switch_sem); + if (data->context) { + struct api_context *ctx = data->context; + + if (ctx->flags & RTLBT_CLOSE) { + ctx->flags &= ~RTLBT_CLOSE; + ctx->status = 0; + complete(&ctx->done); + } + } + up(&switch_sem); +#endif + + return 0; +} + +static int btusb_flush(struct hci_dev *hdev) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + + RTKBT_DBG("%s add delay ", __FUNCTION__); + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_kill_anchored_urbs(&data->tx_anchor); +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + btusb_free_frags(data); +#endif + + return 0; +} + +static const char pkt_ind[][8] = { + [HCI_COMMAND_PKT] = "cmd", + [HCI_ACLDATA_PKT] = "acl", + [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) +int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ +#else +int btusb_send_frame(struct sk_buff *skb) +{ + struct hci_dev *hdev = (struct hci_dev *)skb->dev; +#endif + + struct urb *urb; +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + struct btusb_data *data = GET_DRV_DATA(hdev); + struct usb_ctrlrequest *dr; + unsigned int pipe; + int err; +#endif + +// 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 the parameter is wrong, the hdev isn't the correct + * one. Then no HCI commands can be sent. + * This issue is related to the wrong HCI_VERSION_CODE set */ + RTKBT_ERR("HCI is not running"); + return -EBUSY; + } +#endif + + /* 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. + * + * The skb->dev will be used in the callbacks when urb transfer + * completes. See btusb_tx_complete() and btusb_isoc_tx_complete() */ +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) && \ + HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + skb->dev = (void *)hdev; +#endif + + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + print_command(skb); + +#ifdef BTCOEX + rtk_btcoex_parse_cmd(skb->data, skb->len); +#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); + if (!urb) + return -ENOMEM; + + dr = kmalloc(sizeof(*dr), GFP_ATOMIC); + if (!dr) { + usb_free_urb(urb); + return -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); + + hdev->stat.cmd_tx++; + break; + +#endif +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + case HCI_ISODATA_PKT: +#endif + case HCI_ACLDATA_PKT: + print_acl(skb, 1); +#ifdef BTCOEX + if(bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT) + rtk_btcoex_parse_l2cap_data_tx(skb->data, skb->len); +#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) + return -ENODEV; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -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); + + hdev->stat.acl_tx++; + break; + +#endif + 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) + return -ENODEV; + + urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC); + if (!urb) + return -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; + + __fill_isoc_descriptor(urb, skb->len, + le16_to_cpu(data->isoc_tx_ep-> + wMaxPacketSize)); + + hdev->stat.sco_tx++; + goto skip_waking; + + default: + return -EILSEQ; + + } + + err = inc_tx(data); + if (err) { + usb_anchor_urb(urb, &data->deferred); + schedule_work(&data->waker); + err = 0; + goto done; + } + +skip_waking: + usb_anchor_urb(urb, &data->tx_anchor); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + RTKBT_ERR("%s %s urb %p submission for %s failed, err %d", + __func__, hdev->name, urb, + pkt_ind[bt_cb(skb)->pkt_type], err); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } else { + usb_mark_last_busy(data->udev); + } + +done: + usb_free_urb(urb); + return err; +#endif +} + + +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) +static void btusb_destruct(struct hci_dev *hdev) +{ + RTKBT_DBG("btusb_destruct %s", hdev->name); + hci_free_dev(hdev); +} +#endif + +static void btusb_notify(struct hci_dev *hdev, unsigned int evt) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + + RTKBT_DBG("%s: %s evt %d", __func__, hdev->name, evt); + + if (SCO_NUM != data->sco_num) { + data->sco_num = SCO_NUM; + RTKBT_DBG("%s: Update sco num %d", __func__, data->sco_num); +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + data->air_mode = evt; +#endif + schedule_work(&data->work); + } +} + +static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) +{ + struct btusb_data *data = GET_DRV_DATA(hdev); + struct usb_interface *intf = data->isoc; + struct usb_endpoint_descriptor *ep_desc; + int i, err; + + if (!data->isoc) + return -ENODEV; + + RTKBT_INFO("set isoc interface: alt %d", altsetting); + + err = usb_set_interface(data->udev, 1, altsetting); + if (err < 0) { + RTKBT_ERR("%s setting interface failed (%d)", hdev->name, -err); + return err; + } + + data->isoc_altsetting = altsetting; + + data->isoc_tx_ep = NULL; + data->isoc_rx_ep = NULL; + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep_desc = &intf->cur_altsetting->endpoint[i].desc; + + if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) { + data->isoc_tx_ep = ep_desc; + continue; + } + + if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) { + data->isoc_rx_ep = ep_desc; + continue; + } + } + + if (!data->isoc_tx_ep || !data->isoc_rx_ep) { + RTKBT_ERR("%s invalid SCO descriptors", hdev->name); + return -ENODEV; + } + + return 0; +} + +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + int err; + + if (data->isoc_altsetting != new_alts) { + unsigned long flags; + + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->isoc_anchor); + + /* When isochronous alternate setting needs to be + * changed, because SCO connection has been added + * or removed, a packet fragment may be left in the + * reassembling state. This could lead to wrongly + * assembled fragments. + * + * Clear outstanding fragment when selecting a new + * alternate setting. + */ + spin_lock_irqsave(&data->rxlock, flags); + kfree_skb(data->sco_skb); + data->sco_skb = NULL; + spin_unlock_irqrestore(&data->rxlock, flags); + + err = __set_isoc_interface(hdev, new_alts); + if (err < 0) + return err; + } + + if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) { + if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0) + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + else + btusb_submit_isoc_urb(hdev, GFP_KERNEL); + } + + return 0; +} + +static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data, + int alt) +{ + struct usb_interface *intf = data->isoc; + int i; + + BT_DBG("Looking for Alt no :%d", alt); + + if (!intf) + return NULL; + + for (i = 0; i < intf->num_altsetting; i++) { + if (intf->altsetting[i].desc.bAlternateSetting == alt) + return &intf->altsetting[i]; + } + + return NULL; +} +#endif + +static void btusb_work(struct work_struct *work) +{ + struct btusb_data *data = container_of(work, struct btusb_data, work); + struct hci_dev *hdev = data->hdev; + int err; + int new_alts = 0; + + RTKBT_DBG("%s: sco num %d", __func__, data->sco_num); + if (data->sco_num > 0) { + if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) { + err = + usb_autopm_get_interface(data->isoc ? data-> + isoc : data->intf); + if (err < 0) { + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + mdelay(URB_CANCELING_DELAY_MS); + usb_kill_anchored_urbs(&data->isoc_anchor); + return; + } + + set_bit(BTUSB_DID_ISO_RESUME, &data->flags); + } +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_CVSD) { + if (hdev->voice_setting & 0x0020) { + static const int alts[3] = { 2, 4, 5 }; + new_alts = alts[data->sco_num - 1]; + } else { + new_alts = data->sco_num; + } + } else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) { + 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) + RTKBT_ERR("set USB alt:(%d) failed!", new_alts); +#else +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + if (hdev->voice_setting & 0x0020) { + static const int alts[3] = { 2, 4, 5 }; + new_alts = alts[data->sco_num - 1]; + } else { + new_alts = data->sco_num; + } + if (data->isoc_altsetting != new_alts) { +#else + if (data->isoc_altsetting != 2) { + new_alts = 2; +#endif + + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + mdelay(URB_CANCELING_DELAY_MS); + usb_kill_anchored_urbs(&data->isoc_anchor); + + if (__set_isoc_interface(hdev, new_alts) < 0) + return; + } + + if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) { + RTKBT_INFO("submit SCO RX urb."); + if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0) + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + else + btusb_submit_isoc_urb(hdev, GFP_KERNEL); + } +#endif + } else { + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + mdelay(URB_CANCELING_DELAY_MS); + usb_kill_anchored_urbs(&data->isoc_anchor); + + __set_isoc_interface(hdev, 0); + if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &data->flags)) + usb_autopm_put_interface(data->isoc ? data-> + isoc : data->intf); + } +} + +static void btusb_waker(struct work_struct *work) +{ + struct btusb_data *data = container_of(work, struct btusb_data, waker); + int err; + + err = usb_autopm_get_interface(data->intf); + RTKBT_DBG("%s start", __FUNCTION__); + if (err < 0) + return; + + usb_autopm_put_interface(data->intf); + RTKBT_DBG("%s end", __FUNCTION__); +} + +#ifdef RTKBT_TV_POWERON_WHITELIST +static int rtkbt_lookup_le_device_poweron_whitelist(struct hci_dev *hdev, + struct usb_device *udev) +{ + struct hci_conn_params *p; + u8 *cmd; + int result = 0; + + hci_dev_lock(hdev); + list_for_each_entry(p, &hdev->le_conn_params, list) { +#if 0 // for debug message + RTKBT_DBG("%s(): auto_connect = %d", __FUNCTION__, p->auto_connect); + RTKBT_DBG("%s(): addr_type = 0x%02x", __FUNCTION__, p->addr_type); + RTKBT_DBG("%s(): addr=%02x:%02x:%02x:%02x:%02x:%02x", __FUNCTION__, + p->addr.b[5], p->addr.b[4], p->addr.b[3], + p->addr.b[2], p->addr.b[1], p->addr.b[0]); +#endif + if ( p->auto_connect == HCI_AUTO_CONN_ALWAYS && + p->addr_type == ADDR_LE_DEV_PUBLIC ) { + + RTKBT_DBG("%s(): Set RTKBT LE Power-on Whitelist for " + "%02x:%02x:%02x:%02x:%02x:%02x", __FUNCTION__, + p->addr.b[5], p->addr.b[4], p->addr.b[3], + p->addr.b[2], p->addr.b[1], p->addr.b[0]); + + cmd = kzalloc(16, GFP_ATOMIC); + if (!cmd) { + RTKBT_ERR("Can't allocate memory for cmd"); + return -ENOMEM; + } + cmd[0] = 0x7b; + cmd[1] = 0xfc; + cmd[2] = 0x07; + cmd[3] = 0x00; + cmd[4] = p->addr.b[0]; + cmd[5] = p->addr.b[1]; + cmd[6] = p->addr.b[2]; + cmd[7] = p->addr.b[3]; + cmd[8] = p->addr.b[4]; + cmd[9] = p->addr.b[5]; + + result = __rtk_send_hci_cmd(udev, cmd, 10); + kfree(cmd); + } + } + hci_dev_unlock(hdev); + + return result; +} +#endif + +static int rtkbt_pm_notify(struct notifier_block *notifier, + ulong pm_event, void *unused) +{ + struct btusb_data *data; + struct usb_device *udev; + struct usb_interface *intf; + struct hci_dev *hdev; + /* int err; */ +#if defined RTKBT_SWITCH_PATCH || defined RTKBT_TV_POWERON_WHITELIST + int result = 0; +#endif +#ifdef RTKBT_SWITCH_PATCH + u8 *cmd; + static u8 hci_state = 0; + struct api_context ctx; +#endif + + data = container_of(notifier, struct btusb_data, pm_notifier); + udev = data->udev; + intf = data->intf; + hdev = data->hdev; + + RTKBT_DBG("%s: pm_event %ld", __func__, pm_event); + switch (pm_event) { + case PM_SUSPEND_PREPARE: + case PM_HIBERNATION_PREPARE: + /* No need to load firmware because the download firmware + * process is deprecated in resume. + * We use rebind after resume instead */ + /* err = usb_autopm_get_interface(data->intf); + * if (err < 0) + * return err; + * patch_entry->fw_len = + * load_firmware(dev_entry, &patch_entry->fw_cache); + * usb_autopm_put_interface(data->intf); + * if (patch_entry->fw_len <= 0) { + * RTKBT_DBG("rtkbt_pm_notify return NOTIFY_BAD"); + * return NOTIFY_BAD; + * } */ + + RTKBT_DBG("%s: suspend prepare", __func__); + + if (!device_may_wakeup(&udev->dev)) { +#ifdef CONFIG_NEEDS_BINDING + intf->needs_binding = 1; + RTKBT_DBG("Remote wakeup not support, set " + "intf->needs_binding = 1"); +#else + RTKBT_DBG("Remote wakeup not support, no needs binding"); +#endif + } + +#ifdef RTKBT_SWITCH_PATCH + if (test_bit(HCI_UP, &hdev->flags)) { + unsigned long expire; + + init_completion(&ctx.done); + hci_state = 1; + + down(&switch_sem); + data->context = &ctx; + ctx.flags = RTLBT_CLOSE; + queue_work(hdev->req_workqueue, &hdev->power_off.work); + up(&switch_sem); + + expire = msecs_to_jiffies(1000); + if (!wait_for_completion_timeout(&ctx.done, expire)) + RTKBT_ERR("hdev close timeout"); + + down(&switch_sem); + data->context = NULL; + up(&switch_sem); + } + + cmd = kzalloc(16, GFP_ATOMIC); + if (!cmd) { + RTKBT_ERR("Can't allocate memory for cmd"); + return -ENOMEM; + } + + /* Clear patch */ + cmd[0] = 0x66; + cmd[1] = 0xfc; + cmd[2] = 0x00; + + result = __rtk_send_hci_cmd(udev, cmd, 3); + kfree(cmd); + msleep(100); /* From FW colleague's recommendation */ + result = download_special_patch(intf, "lps_"); +#endif + +#ifdef RTKBT_TV_POWERON_WHITELIST + result = rtkbt_lookup_le_device_poweron_whitelist(hdev, udev); + if (result < 0) { + RTKBT_ERR("rtkbt_lookup_le_device_poweron_whitelist error: %d", result); + } +#endif + +#if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SWITCH_PATCH +#ifdef RTKBT_POWERKEY_WAKEUP + /* Tell the controller to wake up host if received special + * advertising packet + */ + set_scan(intf); +#endif + /* Send special vendor commands */ +#endif + + break; + + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + /* if (patch_entry->fw_len > 0) { + * kfree(patch_entry->fw_cache); + * patch_entry->fw_cache = NULL; + * patch_entry->fw_len = 0; + * } */ + +#ifdef RTKBT_SWITCH_PATCH + cmd = kzalloc(16, GFP_ATOMIC); + if (!cmd) { + RTKBT_ERR("Can't allocate memory for cmd"); + return -ENOMEM; + } + + /* Clear patch */ + cmd[0] = 0x66; + cmd[1] = 0xfc; + cmd[2] = 0x00; + + result = __rtk_send_hci_cmd(udev, cmd, 3); + kfree(cmd); + msleep(100); /* From FW colleague's recommendation */ + result = download_patch(intf); + if (hci_state) { + hci_state = 0; + queue_work(hdev->req_workqueue, &hdev->power_on); + } +#endif + +#ifdef BTUSB_RPM + RTKBT_DBG("%s: Re-enable autosuspend", __func__); + /* pm_runtime_use_autosuspend(&udev->dev); + * pm_runtime_set_autosuspend_delay(&udev->dev, 2000); + * pm_runtime_set_active(&udev->dev); + * pm_runtime_allow(&udev->dev); + * pm_runtime_mark_last_busy(&udev->dev); + * pm_runtime_autosuspend(&udev->dev); + * pm_runtime_put_autosuspend(&udev->dev); + * usb_disable_autosuspend(udev); */ + /* FIXME: usb_enable_autosuspend(udev) is useless here. + * Because it is always enabled after enabled in btusb_probe() + */ + usb_enable_autosuspend(udev); + pm_runtime_mark_last_busy(&udev->dev); +#endif + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static int rtkbt_shutdown_notify(struct notifier_block *notifier, + ulong pm_event, void *unused) +{ + struct btusb_data *data; + struct usb_device *udev; + struct usb_interface *intf; + struct hci_dev *hdev; + /* int err; */ + + data = container_of(notifier, struct btusb_data, shutdown_notifier); + udev = data->udev; + intf = data->intf; + hdev = data->hdev; + + RTKBT_DBG("%s: pm_event %ld", __func__, pm_event); + switch (pm_event) { + case SYS_POWER_OFF: + case SYS_RESTART: +#ifdef RTKBT_SHUTDOWN_WAKEUP + RTKBT_DBG("%s: power off", __func__); + set_scan(intf); +#endif + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static int btusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ep_desc; + struct btusb_data *data; + struct hci_dev *hdev; + int i, err, flag1, flag2; + struct usb_device *udev; + udev = interface_to_usbdev(intf); + + RTKBT_INFO("btusb_probe intf->cur_altsetting->desc.bInterfaceNumber %d", + intf->cur_altsetting->desc.bInterfaceNumber); + + /* interface numbers are hardcoded in the spec */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + 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); + flag2 = device_may_wakeup(&udev->dev); + RTKBT_DBG("btusb_probe can_wakeup %x, may wakeup %x", flag1, flag2); +#ifdef BTUSB_WAKEUP_HOST + device_wakeup_enable(&udev->dev); +#endif + //device_wakeup_enable(&udev->dev); + /*device_wakeup_disable(&udev->dev); + flag1=device_can_wakeup(&udev->dev); + flag2=device_may_wakeup(&udev->dev); + RTKBT_DBG("btusb_probe can_wakeup=%x flag2=%x",flag1,flag2); + */ + err = patch_add(intf); + if (err < 0) + return -1; + /*******************************/ + + data = rtk_alloc(intf); + if (!data) + return -ENOMEM; + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep_desc = &intf->cur_altsetting->endpoint[i].desc; + if (!data->intr_ep && usb_endpoint_is_bulk_in(ep_desc) && (ep_desc->bEndpointAddress == 0x81)) { + data->intr_ep = ep_desc; + continue; + } + + if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) { + data->intr_ep = ep_desc; + continue; + } + + if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) { + data->bulk_tx_ep = ep_desc; + continue; + } + + if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) { + data->bulk_rx_ep = ep_desc; + continue; + } + } + + if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) { + rtk_free(data); + return -ENODEV; + } + + data->cmdreq_type = USB_TYPE_CLASS; + + data->udev = interface_to_usbdev(intf); + data->intf = intf; + + spin_lock_init(&data->lock); + + INIT_WORK(&data->work, btusb_work); + INIT_WORK(&data->waker, btusb_waker); + spin_lock_init(&data->txlock); + + init_usb_anchor(&data->tx_anchor); + init_usb_anchor(&data->intr_anchor); + init_usb_anchor(&data->bulk_anchor); + init_usb_anchor(&data->isoc_anchor); + init_usb_anchor(&data->deferred); + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + spin_lock_init(&data->rxlock); + data->recv_bulk = btusb_recv_bulk; +#endif + + hdev = hci_alloc_dev(); + if (!hdev) { + rtk_free(data); + return -ENOMEM; + } + + HDEV_BUS = HCI_USB; + + data->hdev = hdev; + + SET_HCIDEV_DEV(hdev, &intf->dev); + + hdev->open = btusb_open; + hdev->close = btusb_close; + hdev->flush = btusb_flush; + hdev->send = btusb_send_frame; + 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) + hci_set_drvdata(hdev, data); +#else + hdev->driver_data = data; + hdev->destruct = btusb_destruct; + hdev->owner = THIS_MODULE; +#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 (!reset) + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); +#endif + + /* Interface numbers are hardcoded in the specification */ + data->isoc = usb_ifnum_to_if(data->udev, 1); + + if (data->isoc) { + err = usb_driver_claim_interface(&btusb_driver, + data->isoc, data); + if (err < 0) { + hci_free_dev(hdev); + rtk_free(data); + return err; + } + } + +#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); +#endif + + err = hci_register_dev(hdev); + if (err < 0) { + hci_free_dev(hdev); + rtk_free(data); + return err; + } + + usb_set_intfdata(intf, data); + + /* Register PM notifier */ + data->pm_notifier.notifier_call = rtkbt_pm_notify; + register_pm_notifier(&data->pm_notifier); + + /* Register POWER-OFF notifier */ + data->shutdown_notifier.notifier_call = rtkbt_shutdown_notify; + register_reboot_notifier(&data->shutdown_notifier); +#ifdef BTCOEX + rtk_btcoex_probe(hdev); +#endif + + RTKBT_DBG("%s: done", __func__); + + return 0; +} + +static void btusb_disconnect(struct usb_interface *intf) +{ + struct btusb_data *data = usb_get_intfdata(intf); + struct hci_dev *hdev; + struct usb_device *udev; + udev = interface_to_usbdev(intf); + + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return; + + if (!data) + return; + + RTKBT_DBG("btusb_disconnect"); + + /* Un-register PM notifier */ + unregister_pm_notifier(&data->pm_notifier); + unregister_reboot_notifier(&data->shutdown_notifier); + + /*******************************/ + patch_remove(intf); + /*******************************/ + + hdev = data->hdev; + +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + __hci_dev_hold(hdev); +#endif + + usb_set_intfdata(data->intf, NULL); + + if (data->isoc) + usb_set_intfdata(data->isoc, NULL); + + hci_unregister_dev(hdev); + + if (intf == data->isoc) + usb_driver_release_interface(&btusb_driver, data->intf); + else if (data->isoc) + usb_driver_release_interface(&btusb_driver, data->isoc); + +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + __hci_dev_put(hdev); +#endif + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + btusb_free_frags(data); +#endif + + hci_free_dev(hdev); + rtk_free(data); +} + +#ifdef CONFIG_PM +static int btusb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct btusb_data *data = usb_get_intfdata(intf); + + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return 0; + + /*******************************/ + RTKBT_DBG("btusb_suspend message.event 0x%x, data->suspend_count %d", + message.event, data->suspend_count); + if (!test_bit(HCI_RUNNING, &data->hdev->flags)) { + RTKBT_INFO("%s: hdev is not HCI_RUNNING", __func__); + /* set_scan(data->intf); */ + } + /*******************************/ + + if (data->suspend_count++) + return 0; + + spin_lock_irq(&data->txlock); + if (!((message.event & PM_EVENT_AUTO) && data->tx_in_flight)) { + set_bit(BTUSB_SUSPENDING, &data->flags); + spin_unlock_irq(&data->txlock); + RTKBT_INFO("%s: suspending...", __func__); + } else { + spin_unlock_irq(&data->txlock); + data->suspend_count--; + return -EBUSY; + } + + cancel_work_sync(&data->work); + + btusb_stop_traffic(data); + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_kill_anchored_urbs(&data->tx_anchor); + + return 0; +} + +static void play_deferred(struct btusb_data *data) +{ + struct urb *urb; + int err; + + while ((urb = usb_get_from_anchor(&data->deferred))) { + /************************************/ + usb_anchor_urb(urb, &data->tx_anchor); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + RTKBT_ERR("play_deferred urb %p submission failed", + urb); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } else { + usb_mark_last_busy(data->udev); + } + usb_free_urb(urb); + /************************************/ + data->tx_in_flight++; + } + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_scuttle_anchored_urbs(&data->deferred); +} + +static int btusb_resume(struct usb_interface *intf) +{ + struct btusb_data *data = usb_get_intfdata(intf); + struct hci_dev *hdev = data->hdev; + int err = 0; + + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return 0; + + /*******************************/ + RTKBT_DBG("%s: data->suspend_count %d", __func__, data->suspend_count); + + /* if intf->needs_binding is set, driver will be rebind. + * The probe will be called instead of resume */ + /* if (!test_bit(HCI_RUNNING, &hdev->flags)) { + * RTKBT_DBG("btusb_resume-----bt is off,download patch"); + * download_patch(intf); + * } else + * RTKBT_DBG("btusb_resume,----bt is on"); + */ + /*******************************/ + if (--data->suspend_count) + return 0; + + if (test_bit(BTUSB_INTR_RUNNING, &data->flags)) { + err = btusb_submit_intr_urb(hdev, GFP_NOIO); + if (err < 0) { + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + goto failed; + } + } + + if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) { + err = btusb_submit_bulk_urb(hdev, GFP_NOIO); + if (err < 0) { + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + goto failed; + } + + btusb_submit_bulk_urb(hdev, GFP_NOIO); + } + + if (test_bit(BTUSB_ISOC_RUNNING, &data->flags)) { + if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0) + clear_bit(BTUSB_ISOC_RUNNING, &data->flags); + else + btusb_submit_isoc_urb(hdev, GFP_NOIO); + } + + spin_lock_irq(&data->txlock); + play_deferred(data); + clear_bit(BTUSB_SUSPENDING, &data->flags); + spin_unlock_irq(&data->txlock); + schedule_work(&data->work); + + RTKBT_DBG("%s: data->suspend_count %d, done", __func__, + data->suspend_count); + + return 0; + +failed: + mdelay(URB_CANCELING_DELAY_MS); // Added by Realtek + usb_scuttle_anchored_urbs(&data->deferred); +//done: + spin_lock_irq(&data->txlock); + clear_bit(BTUSB_SUSPENDING, &data->flags); + spin_unlock_irq(&data->txlock); + RTKBT_DBG("%s: data->suspend_count %d, fail", __func__, + data->suspend_count); + + return err; +} +#endif + +static struct usb_driver btusb_driver = { + .name = "rtk_btusb", + .probe = btusb_probe, + .disconnect = btusb_disconnect, +#ifdef CONFIG_PM + .suspend = btusb_suspend, + .resume = btusb_resume, +#if defined RTKBT_SWITCH_PATCH || defined RTKBT_SUSPEND_WAKEUP || defined \ + RTKBT_SHUTDOWN_WAKEUP + .reset_resume = btusb_resume, +#endif +#endif + .id_table = btusb_table, + .supports_autosuspend = 1, +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 1) + .disable_hub_initiated_lpm = 1, +#endif +}; + +static int __init btusb_init(void) +{ + RTKBT_DBG("Realtek Bluetooth USB driver ver %s", VERSION); +#ifdef BTCOEX + rtk_btcoex_init(); +#endif + return usb_register(&btusb_driver); +} + +static void __exit btusb_exit(void) +{ + RTKBT_DBG("rtk_btusb: btusb_exit"); + usb_deregister(&btusb_driver); + +#ifdef BTCOEX + rtk_btcoex_exit(); +#endif +} + +module_init(btusb_init); +module_exit(btusb_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Realtek Bluetooth USB driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/BSP/linux-kernel/drivers/bluetooth/rtk_bt.h b/BSP/linux-kernel/drivers/bluetooth/rtk_bt.h new file mode 100755 index 000000000..fc835222e --- /dev/null +++ b/BSP/linux-kernel/drivers/bluetooth/rtk_bt.h @@ -0,0 +1,151 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */ +#define HCI_VERSION_CODE LINUX_VERSION_CODE + +#ifdef CONFIG_BTCOEX +#define BTCOEX +#endif + +/*********************************** +** Realtek - For rtk_btusb driver ** +***********************************/ +#ifdef CONFIG_BTUSB_WAKEUP_HOST +#define BTUSB_WAKEUP_HOST +#endif + +#define URB_CANCELING_DELAY_MS 10 // Added by Realtek +#if HCI_VERSION_CODE > KERNEL_VERSION(2, 6, 33) +#define HDEV_BUS hdev->bus +#else +#define HDEV_BUS hdev->type +#endif + +#if HCI_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +#define NUM_REASSEMBLY 3 +#endif + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +#define GET_DRV_DATA(x) hci_get_drvdata(x) +#else +#define GET_DRV_DATA(x) x->driver_data +#endif + +#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +#define SCO_NUM hdev->conn_hash.sco_num +#else +#define SCO_NUM hci_conn_num(hdev, SCO_LINK) +#endif + +int patch_add(struct usb_interface *intf); +void patch_remove(struct usb_interface *intf); +int download_patch(struct usb_interface *intf); +int set_btoff(struct usb_interface *intf); +void print_event(struct sk_buff *skb); +void print_command(struct sk_buff *skb); +void print_acl(struct sk_buff *skb, int dataOut); + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb); +#else +int btusb_send_frame(struct sk_buff *skb); +#endif + +#define BTUSB_MAX_ISOC_FRAMES 10 +#define BTUSB_INTR_RUNNING 0 +#define BTUSB_BULK_RUNNING 1 +#define BTUSB_ISOC_RUNNING 2 +#define BTUSB_SUSPENDING 3 +#define BTUSB_DID_ISO_RESUME 4 +#define BTUSB_USE_ALT3_FOR_WBS 15 + +struct btusb_data { + struct hci_dev *hdev; + struct usb_device *udev; + struct usb_interface *intf; + struct usb_interface *isoc; + + spinlock_t lock; + + unsigned long flags; + + struct work_struct work; + struct work_struct waker; + + struct usb_anchor tx_anchor; + struct usb_anchor intr_anchor; + struct usb_anchor bulk_anchor; + struct usb_anchor isoc_anchor; + struct usb_anchor deferred; + int tx_in_flight; + spinlock_t txlock; + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + spinlock_t rxlock; + struct sk_buff *evt_skb; + struct sk_buff *acl_skb; + struct sk_buff *sco_skb; +#endif + + struct usb_endpoint_descriptor *intr_ep; + struct usb_endpoint_descriptor *bulk_tx_ep; + struct usb_endpoint_descriptor *bulk_rx_ep; + struct usb_endpoint_descriptor *isoc_tx_ep; + struct usb_endpoint_descriptor *isoc_rx_ep; + + __u8 cmdreq_type; + + unsigned int sco_num; + +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + unsigned int air_mode; + bool usb_alt6_packet_flow; +#endif + int isoc_altsetting; + int suspend_count; + +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + int (*recv_bulk) (struct btusb_data * data, void *buffer, int count); +#endif + struct notifier_block pm_notifier; + struct notifier_block shutdown_notifier; + void *context; +}; diff --git a/BSP/linux-kernel/drivers/bluetooth/rtk_coex.c b/BSP/linux-kernel/drivers/bluetooth/rtk_coex.c index 61fb29ba8..beaf97e30 100755 --- a/BSP/linux-kernel/drivers/bluetooth/rtk_coex.c +++ b/BSP/linux-kernel/drivers/bluetooth/rtk_coex.c @@ -57,10 +57,10 @@ #define RTK_VERSION "1.2" -#define RTKBT_DBG(fmt, arg...) printk(KERN_INFO "rtk_btcoex: " fmt "\n" , ## arg) +#define RTKBT_DBG(fmt, arg...) printk(KERN_DEBUG "rtk_btcoex: " fmt "\n" , ## arg) #define RTKBT_INFO(fmt, arg...) printk(KERN_INFO "rtk_btcoex: " fmt "\n" , ## arg) #define RTKBT_WARN(fmt, arg...) printk(KERN_WARNING "rtk_btcoex: " fmt "\n", ## arg) -#define RTKBT_ERR(fmt, arg...) printk(KERN_WARNING "rtk_btcoex: " fmt "\n", ## arg) +#define RTKBT_ERR(fmt, arg...) printk(KERN_ERR "rtk_btcoex: " fmt "\n", ## arg) static struct rtl_coex_struct btrtl_coex; @@ -2922,6 +2922,11 @@ static void check_profileinfo_cmd(void) profileinfo_buf); } +static void rtl_cmd_work(struct work_struct *work) +{ + check_profileinfo_cmd(); +} + void rtk_btcoex_open(struct hci_dev *hdev) { if (test_and_set_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { @@ -2935,6 +2940,7 @@ void rtk_btcoex_open(struct hci_dev *hdev) //struct rtl_btinfo_ctl ctl; INIT_DELAYED_WORK(&btrtl_coex.fw_work, (void *)rtl_ev_work); + INIT_DELAYED_WORK(&btrtl_coex.cmd_work, rtl_cmd_work); #ifdef RTB_SOFTWARE_MAILBOX #ifdef RTK_COEX_OVER_SYMBOL INIT_WORK(&rtw_work, rtw_work_func); @@ -2966,7 +2972,8 @@ void rtk_btcoex_open(struct hci_dev *hdev) #endif rtkbt_coexmsg_send(invite_req, sizeof(invite_req)); #endif - check_profileinfo_cmd(); + queue_delayed_work(btrtl_coex.fw_wq, &btrtl_coex.cmd_work, + msecs_to_jiffies(10)); /* Just for test */ //ctl.polling_enable = 1; //ctl.polling_time = 1; @@ -3010,6 +3017,7 @@ void rtk_btcoex_close(void) cancel_delayed_work_sync(&btrtl_coex.fw_work); cancel_delayed_work_sync(&btrtl_coex.l2_work); + cancel_delayed_work_sync(&btrtl_coex.cmd_work); flush_connection_hash(&btrtl_coex); flush_profile_hash(&btrtl_coex); diff --git a/BSP/linux-kernel/drivers/bluetooth/rtk_coex.h b/BSP/linux-kernel/drivers/bluetooth/rtk_coex.h index bb182bbf8..287641e87 100755 --- a/BSP/linux-kernel/drivers/bluetooth/rtk_coex.h +++ b/BSP/linux-kernel/drivers/bluetooth/rtk_coex.h @@ -27,7 +27,7 @@ #define BTRTL_HCIUSB 0 #define BTRTL_HCIUART 1 -#define BTRTL_HCI_IF BTRTL_HCIUART +#define BTRTL_HCI_IF BTRTL_HCIUSB #define TRUE 1 #define FALSE 0 @@ -250,6 +250,7 @@ struct rtl_coex_struct { struct workqueue_struct *timer_wq; struct delayed_work fw_work; struct delayed_work l2_work; + struct delayed_work cmd_work; #ifdef RTB_SOFTWARE_MAILBOX struct sock *sk; #endif diff --git a/BSP/linux-kernel/drivers/bluetooth/rtk_misc.c b/BSP/linux-kernel/drivers/bluetooth/rtk_misc.c new file mode 100755 index 000000000..41223fc8a --- /dev/null +++ b/BSP/linux-kernel/drivers/bluetooth/rtk_misc.c @@ -0,0 +1,2593 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) +#include +#endif + +#include +#include +#include +#include +#include + +#include "rtk_misc.h" + +#include +#include +#define BDADDR_STRING_LEN 17 +#define BDADDR_FILE "/opt/bdaddr" + +struct cfg_list_item { + struct list_head list; + u16 offset; + u8 len; + u8 data[0]; +}; + +static struct list_head list_configs; + +#define EXTRA_CONFIG_FILE "/opt/rtk_btconfig.txt" +static struct list_head list_extracfgs; + +#define CMD_CMP_EVT 0x0e +#define PKT_LEN 300 +#define MSG_TO 1000 //us +#define PATCH_SEG_MAX 252 +#define DATA_END 0x80 +#define DOWNLOAD_OPCODE 0xfc20 +/* This command is used only for TV patch + * if host is going to suspend state, it should send this command to + * Controller. Controller will scan the special advertising packet + * which indicates Controller to wake up host */ +#define STARTSCAN_OPCODE 0xfc28 +#define TRUE 1 +#define FALSE 0 +#define CMD_HDR_LEN sizeof(struct hci_command_hdr) +#define EVT_HDR_LEN sizeof(struct hci_event_hdr) +#define CMD_CMP_LEN sizeof(struct hci_ev_cmd_complete) + +#define HCI_CMD_READ_BD_ADDR 0x1009 +#define HCI_VENDOR_CHANGE_BDRATE 0xfc17 +#define HCI_VENDOR_READ_RTK_ROM_VERISION 0xfc6d +#define HCI_VENDOR_READ_LMP_VERISION 0x1001 +#define HCI_VENDOR_READ_CMD 0xfc61 +#define HCI_VENDOR_WRITE_CMD 0xfc62 + +#define ROM_LMP_NONE 0x0000 +#define ROM_LMP_8723a 0x1200 +#define ROM_LMP_8723b 0x8723 +#define ROM_LMP_8821a 0X8821 +#define ROM_LMP_8761a 0X8761 +#define ROM_LMP_8822b 0X8822 +#define ROM_LMP_8852a 0x8852 +#define ROM_LMP_8851b 0x8851 + +#define PATCH_SNIPPETS 0x01 +#define PATCH_DUMMY_HEADER 0x02 +#define PATCH_SECURITY_HEADER 0x03 +#define PATCH_OTA_FLAG 0x04 +#define SECTION_HEADER_SIZE 8 + +struct rtk_eversion_evt { + uint8_t status; + uint8_t version; +} __attribute__ ((packed)); + +struct rtk_security_proj_evt { + uint8_t status; + uint8_t key_id; +} __attribute__ ((packed)); + +struct rtk_chip_type_evt { + uint8_t status; + uint16_t chip; +} __attribute__ ((packed)); + +enum rtk_read_class { + READ_NONE = 0, + READ_CHIP_TYPE = 1, + READ_CHIP_VER = 2, + READ_SEC_PROJ = 3 +}; + +struct rtk_epatch_entry { + uint16_t chipID; + uint16_t patch_length; + uint32_t start_offset; +} __attribute__ ((packed)); + +struct rtk_epatch { + uint8_t signature[8]; + __le32 fw_version; + __le16 number_of_total_patch; + struct rtk_epatch_entry entry[0]; +} __attribute__ ((packed)); + +struct rtk_extension_entry { + uint8_t opcode; + uint8_t length; + uint8_t *data; +} __attribute__ ((packed)); + +struct rtb_section_hdr { + uint32_t opcode; + uint32_t section_len; + uint32_t soffset; +} __attribute__ ((packed)); + +struct rtb_new_patch_hdr { + uint8_t signature[8]; + uint8_t fw_version[8]; + __le32 number_of_section; +} __attribute__ ((packed)); + +//signature: Realtech +static const uint8_t RTK_EPATCH_SIGNATURE[8] = + { 0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68 }; + +//signature: RTBTCore +static const uint8_t RTK_EPATCH_SIGNATURE_NEW[8] = + { 0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65 }; + +//Extension Section IGNATURE:0x77FD0451 +static const uint8_t Extension_Section_SIGNATURE[4] = { 0x51, 0x04, 0xFD, 0x77 }; + +static uint16_t project_id[] = { + ROM_LMP_8723a, + ROM_LMP_8723b, + ROM_LMP_8821a, + ROM_LMP_8761a, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_8822b, + ROM_LMP_8723b, /* RTL8723DU */ + ROM_LMP_8821a, /* RTL8821CU */ + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_8822b, /* RTL8822CU */ + ROM_LMP_8761a, /* index 14 for 8761BU */ + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_8852a, /* index 18 for 8852AU */ + ROM_LMP_8723b, /* index 19 for 8723FU */ + ROM_LMP_8852a, /* index 20 for 8852BU */ + ROM_LMP_NONE, + ROM_LMP_NONE, + 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 */ + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_8851b, /* index 36 for 8851BU */ + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_NONE, + ROM_LMP_8852a, /* index 42 for 8852D*/ +}; + +enum rtk_endpoit { + CTRL_EP = 0, + INTR_EP = 1, + BULK_EP = 2, + ISOC_EP = 3 +}; + +/* software id */ +#define RTLPREVIOUS 0x00 +#define RTL8822BU 0x70 +#define RTL8723DU 0x71 +#define RTL8821CU 0x72 +#define RTL8822CU 0x73 +#define RTL8761BU 0x74 +#define RTL8852AU 0x75 +#define RTL8733BU 0x76 +#define RTL8852BU 0x77 +#define RTL8852CU 0x78 +#define RTL8822EU 0x79 +#define RTL8851BU 0x7A +#define RTL8852DU 0x7B + +typedef struct { + uint16_t prod_id; + uint16_t lmp_sub; + char * mp_patch_name; + char * patch_name; + char * config_name; + u8 chip_type; +} patch_info; + +typedef struct { + struct list_head list_node; + struct usb_interface *intf; + struct usb_device *udev; + patch_info *patch_entry; +} dev_data; + +typedef struct { + dev_data *dev_entry; + int pipe_in, pipe_out; + uint8_t *send_pkt; + uint8_t *rcv_pkt; + struct hci_command_hdr *cmd_hdr; + struct hci_event_hdr *evt_hdr; + struct hci_ev_cmd_complete *cmd_cmp; + uint8_t *req_para, *rsp_para; + uint8_t *fw_data; + int pkt_len, fw_len; +} xchange_data; + +typedef struct { + uint8_t index; + uint8_t data[PATCH_SEG_MAX]; +} __attribute__ ((packed)) download_cp; + +typedef struct { + uint8_t status; + uint8_t index; +} __attribute__ ((packed)) download_rp; + +#define RTK_VENDOR_CONFIG_MAGIC 0x8723ab55 +static const u8 cfg_magic[4] = { 0x55, 0xab, 0x23, 0x87 }; +struct rtk_bt_vendor_config_entry { + __le16 offset; + uint8_t entry_len; + uint8_t entry_data[0]; +} __attribute__ ((packed)); + +struct rtk_bt_vendor_config { + __le32 signature; + __le16 data_len; + struct rtk_bt_vendor_config_entry entry[0]; +} __attribute__ ((packed)); +#define BT_CONFIG_HDRLEN sizeof(struct rtk_bt_vendor_config) + +static uint8_t gEVersion = 0xFF; +static uint8_t g_key_id = 0; + +static dev_data *dev_data_find(struct usb_interface *intf); +static patch_info *get_patch_entry(struct usb_device *udev); +static int load_firmware(dev_data * dev_entry, uint8_t ** buff); +static void init_xdata(xchange_data * xdata, dev_data * dev_entry); +static int check_fw_version(xchange_data * xdata); +static int download_data(xchange_data * xdata); +static int send_hci_cmd(xchange_data * xdata); +static int rcv_hci_evt(xchange_data * xdata); +static uint8_t rtk_get_eversion(dev_data * dev_entry); +static int rtk_vendor_read(dev_data * dev_entry, uint8_t class); + +static patch_info fw_patch_table[] = { +/* { pid, lmp_sub, mp_fw_name, fw_name, config_name, chip_type } */ + {0x1724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* RTL8723A */ + {0x8723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AE */ + {0xA723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AE for LI */ + {0x0723, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AE */ + {0x3394, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AE for Azurewave */ + + {0x0724, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AU */ + {0x8725, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AU */ + {0x872A, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AU */ + {0x872B, 0x1200, "mp_rtl8723a_fw", "rtl8723a_fw", "rtl8723a_config", RTLPREVIOUS}, /* 8723AU */ + + {0xb720, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BU */ + {0xb72A, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BU */ + {0xb728, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for LC */ + {0xb723, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE */ + {0xb72B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE */ + {0xb001, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for HP */ + {0xb002, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE */ + {0xb003, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE */ + {0xb004, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE */ + {0xb005, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE */ + + {0x3410, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for Azurewave */ + {0x3416, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for Azurewave */ + {0x3459, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for Azurewave */ + {0xE085, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for Foxconn */ + {0xE08B, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for Foxconn */ + {0xE09E, 0x8723, "mp_rtl8723b_fw", "rtl8723b_fw", "rtl8723b_config", RTLPREVIOUS}, /* RTL8723BE for Foxconn */ + + {0xA761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", RTLPREVIOUS}, /* RTL8761AU only */ + {0x818B, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", RTLPREVIOUS}, /* RTL8761AW + 8192EU */ + {0x818C, 0x8761, "mp_rtl8761a_fw", "rtl8761aw_fw", "rtl8761aw_config", RTLPREVIOUS}, /* RTL8761AW + 8192EU */ + {0x8760, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", RTLPREVIOUS}, /* RTL8761AU + 8192EE */ + {0xB761, 0x8761, "mp_rtl8761a_fw", "rtl8761au_fw", "rtl8761a_config", RTLPREVIOUS}, /* RTL8761AUV only */ + {0x8761, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", RTLPREVIOUS}, /* RTL8761AU + 8192EE for LI */ + {0x8A60, 0x8761, "mp_rtl8761a_fw", "rtl8761au8812ae_fw", "rtl8761a_config", RTLPREVIOUS}, /* RTL8761AU + 8812AE */ + {0x3527, 0x8761, "mp_rtl8761a_fw", "rtl8761au8192ee_fw", "rtl8761a_config", RTLPREVIOUS}, /* RTL8761AU + 8814AE */ + + {0x8821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AE */ + {0x0821, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AE */ + {0x0823, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AU */ + {0x3414, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AE */ + {0x3458, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AE */ + {0x3461, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AE */ + {0x3462, 0x8821, "mp_rtl8821a_fw", "rtl8821a_fw", "rtl8821a_config", RTLPREVIOUS}, /* RTL8821AE */ + + {0xb82c, 0x8822, "mp_rtl8822bu_fw", "rtl8822bu_fw", "rtl8822bu_config", RTL8822BU}, /* RTL8822BU */ + + {0xd720, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU */ + {0xd723, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU */ + {0xd739, 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 */ + + {0xb820, 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 */ + {0xc823, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc824, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc825, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc827, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc025, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc024, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc030, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xb00a, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xb00e, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0xc032, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0x4000, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for LiteOn */ + {0x4001, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for LiteOn */ + {0x3529, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3530, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3532, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3533, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3538, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3539, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3558, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3559, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3581, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */ + {0x3540, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ + {0x3541, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for GSD */ + {0x3543, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for GSD */ + {0xc80c, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CUH */ + + {0xc82c, 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 */ + {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 */ + {0xc82b, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xb00c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xb00d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc123, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc126, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc127, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc128, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc129, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc131, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc136, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0x3549, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE for Azurewave */ + {0x3548, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE for Azurewave */ + {0xc125, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0x4005, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE for LiteOn */ + {0x3051, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE for LiteOn */ + {0x18ef, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0x161f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0x3053, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc547, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0x3553, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0x3555, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */ + {0xc82f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE-VS */ + {0xc02f, 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 */ + {0xa725, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU */ + {0xa72A, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU BT only */ + + {0x885a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AU */ + {0x8852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0xa852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x2852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x385a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x3852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x1852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x4852, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x4006, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x3561, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x3562, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x588a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x589a, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x590a, 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 */ + {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 */ + {0xc127, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + {0x3565, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ + + {0xb733, 0x8723, "mp_rtl8733bu_fw", "rtl8733bu_fw", "rtl8733bu_config", RTL8733BU}, /* RTL8733BU */ + {0xb73a, 0x8723, "mp_rtl8733bu_fw", "rtl8733bu_fw", "rtl8733bu_config", RTL8733BU}, /* RTL8733BU */ + {0xf72b, 0x8723, "mp_rtl8733bu_fw", "rtl8733bu_fw", "rtl8733bu_config", RTL8733BU}, /* RTL8733BU */ + + {0x8851, 0x8852, "mp_rtl8851au_fw", "rtl8851au_fw", "rtl8851au_config", RTL8852BU}, /* RTL8851AU */ + {0xa85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BU */ + {0xb85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0xb85c, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x3571, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x3570, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x3572, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x4b06, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x885b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x886b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x887b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0xc559, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0xb052, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0xb152, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0xb252, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */ + {0x4853, 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 */ + {0xc85d, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CU */ + {0x0852, 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 */ + {0x885c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ + {0x886c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ + {0x887c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ + {0x4007, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */ + + {0xe822, 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 */ + +/* NOTE: must append patch entries above the null entry */ + {0, 0, NULL, NULL, NULL, 0} +}; + +static LIST_HEAD(dev_data_list); + +static void util_hexdump(const u8 *buf, size_t len) +{ + static const char hexdigits[] = "0123456789abcdef"; + char str[16 * 3]; + size_t i; + + if (!buf || !len) + return; + + for (i = 0; i < len; i++) { + str[((i % 16) * 3)] = hexdigits[buf[i] >> 4]; + str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf]; + str[((i % 16) * 3) + 2] = ' '; + if ((i + 1) % 16 == 0) { + str[16 * 3 - 1] = '\0'; + RTKBT_DBG("%s", str); + } + } + + if (i % 16 > 0) { + str[(i % 16) * 3 - 1] = '\0'; + RTKBT_DBG("%s", str); + } +} + +#if defined RTKBT_SWITCH_PATCH || defined RTKBT_TV_POWERON_WHITELIST +int __rtk_send_hci_cmd(struct usb_device *udev, u8 *buf, u16 size) +{ + int result; + unsigned int pipe = usb_sndctrlpipe(udev, 0); + + result = usb_control_msg(udev, pipe, 0, USB_TYPE_CLASS, 0, 0, + buf, size, 1000); /* 1000 msecs */ + + if (result < 0) + RTKBT_ERR("%s: Couldn't send hci cmd, err %d", + __func__, result); + + return result; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) +static inline struct inode *file_inode(const struct file *f) +{ + return f->f_path.dentry->d_inode; +} +#endif + +static int config_lists_init(void) +{ + INIT_LIST_HEAD(&list_configs); + INIT_LIST_HEAD(&list_extracfgs); + + return 0; +} + +static void config_lists_free(void) +{ + struct list_head *iter; + struct list_head *tmp; + struct list_head *head; + struct cfg_list_item *n; + + if (!list_empty(&list_extracfgs)) + list_splice_tail(&list_extracfgs, &list_configs); + head = &list_configs; + list_for_each_safe(iter, tmp, head) { + n = list_entry(iter, struct cfg_list_item, list); + if (n) { + list_del(&n->list); + kfree(n); + } + } + + INIT_LIST_HEAD(&list_configs); + INIT_LIST_HEAD(&list_extracfgs); +} + +static void line_process(char *buf, int len) +{ + char *argv[32]; + int argc = 0; + unsigned long offset; + u8 l; + u8 i = 0; + char *ptr = buf; + char *head = buf; + struct cfg_list_item *item; + + while ((ptr = strsep(&head, ", \t")) != NULL) { + if (!ptr[0]) + continue; + argv[argc++] = ptr; + if (argc >= 32) { + RTKBT_WARN("%s: Config item is too long", __func__); + break; + } + } + + if (argc < 4) { + RTKBT_WARN("%s: Invalid Config item, ignore", __func__); + return; + } + + offset = simple_strtoul(argv[0], NULL, 16); + offset = offset | (simple_strtoul(argv[1], NULL, 16) << 8); + l = (u8)simple_strtoul(argv[2], NULL, 16); + if (l != (u8)(argc - 3)) { + RTKBT_ERR("invalid len %u", l); + return; + } + + item = kzalloc(sizeof(*item) + l, GFP_KERNEL); + if (!item) { + RTKBT_WARN("%s: Cannot alloc mem for item, %04lx, %u", __func__, + offset, l); + return; + } + + item->offset = (u16)offset; + item->len = l; + for (i = 0; i < l; i++) + item->data[i] = (u8)simple_strtoul(argv[3 + i], NULL, 16); + list_add_tail(&item->list, &list_extracfgs); +} + +static void config_process(u8 *buff, int len) +{ + char *head = (void *)buff; + char *ptr = (void *)buff; + + while ((ptr = strsep(&head, "\n\r")) != NULL) { + if (!ptr[0]) + continue; + line_process(ptr, strlen(ptr) + 1); + } +} + +static void config_file_proc(const char *path) +{ + int size; + int rc; + struct file *file; + u8 tbuf[256]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + loff_t pos = 0; +#endif + + file = filp_open(path, O_RDONLY, 0); + if (IS_ERR(file)) + return; + + if (!S_ISREG(file_inode(file)->i_mode)) + return; + size = i_size_read(file_inode(file)); + if (size <= 0) + return; + + memset(tbuf, 0, sizeof(tbuf)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rc = kernel_read(file, tbuf, size, &pos); +#else + rc = kernel_read(file, 0, tbuf, size); +#endif + fput(file); + if (rc != size) { + if (rc >= 0) + rc = -EIO; + return; + } + + tbuf[rc++] = '\n'; + tbuf[rc++] = '\0'; + config_process(tbuf, rc); +} + +int patch_add(struct usb_interface *intf) +{ + dev_data *dev_entry; + struct usb_device *udev; + + RTKBT_DBG("patch_add"); + dev_entry = dev_data_find(intf); + if (NULL != dev_entry) { + return -1; + } + + udev = interface_to_usbdev(intf); +#ifdef BTUSB_RPM + RTKBT_DBG("auto suspend is enabled"); + usb_enable_autosuspend(udev); + pm_runtime_set_autosuspend_delay(&(udev->dev), 2000); +#else + RTKBT_DBG("auto suspend is disabled"); + usb_disable_autosuspend(udev); +#endif + + dev_entry = kzalloc(sizeof(dev_data), GFP_KERNEL); + dev_entry->intf = intf; + dev_entry->udev = udev; + dev_entry->patch_entry = get_patch_entry(udev); + if (NULL == dev_entry->patch_entry) { + kfree(dev_entry); + return -1; + } + list_add(&dev_entry->list_node, &dev_data_list); + + /* Should reset the gEVersion to 0xff, otherwise the stored gEVersion + * would cause rtk_get_eversion() returning previous gEVersion if + * change to different ECO chip. + * This would cause downloading wrong patch, and the controller can't + * work. */ + RTKBT_DBG("%s: Reset gEVersion to 0xff", __func__); + gEVersion = 0xff; + + return 0; +} + +void patch_remove(struct usb_interface *intf) +{ + dev_data *dev_entry; + struct usb_device *udev; + + udev = interface_to_usbdev(intf); +#ifdef BTUSB_RPM + usb_disable_autosuspend(udev); +#endif + + dev_entry = dev_data_find(intf); + if (NULL == dev_entry) { + return; + } + + RTKBT_DBG("patch_remove"); + list_del(&dev_entry->list_node); + kfree(dev_entry); +} + +static int send_reset_command(xchange_data *xdata) +{ + int ret_val; + + RTKBT_DBG("HCI reset."); + + xdata->cmd_hdr->opcode = cpu_to_le16(HCI_OP_RESET); + xdata->cmd_hdr->plen = 0; + xdata->pkt_len = CMD_HDR_LEN; + + ret_val = send_hci_cmd(xdata); + if (ret_val < 0) { + RTKBT_ERR("failed to send hci cmd."); + return ret_val; + } + + ret_val = rcv_hci_evt(xdata); + if (ret_val < 0) { + RTKBT_ERR("failed to recv hci event."); + return ret_val; + } + + return 0; +} + +static inline int get_max_patch_size(u8 chip_type) +{ + int max_patch_size = 0; + + switch (chip_type) { + case RTLPREVIOUS: + max_patch_size = 24 * 1024; + break; + case RTL8822BU: + max_patch_size = 25 * 1024; + break; + case RTL8723DU: + case RTL8822CU: + case RTL8761BU: + case RTL8821CU: + max_patch_size = 40 * 1024; + break; + case RTL8852AU: + max_patch_size = 0x114D0 + 529; /* 69.2KB */ + break; + case RTL8733BU: + max_patch_size = 0xC4Cf + 529; /* 49.2KB */ + break; + case RTL8852BU: + case RTL8851BU: + max_patch_size = 0x104D0 + 529; /* 65KB */ + break; + case RTL8852CU: + max_patch_size = 0x130D0 + 529; /* 76.2KB */ + break; + case RTL8822EU: + max_patch_size = 0x24620 + 529; /* 145KB */ + break; + case RTL8852DU: + max_patch_size = 0x20D90 + 529; /* 131KB */ + break; + default: + max_patch_size = 40 * 1024; + break; + } + + 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) +{ + int ret_val; + uint16_t chip = 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); + if(chip == 0x8822) { + chip_ver = rtk_vendor_read(dev_entry, READ_CHIP_VER); + if(chip_ver == 0x000e) { + return 0; + } + } + + ret_val = check_fw_version(xdata); + if (ret_val < 0) { + RTKBT_ERR("Failed to get Local Version Information"); + return ret_val; + + } else if (ret_val > 0) { + RTKBT_DBG("Firmware already exists"); + /* Patch alread exists, just return */ + if (gEVersion == 0xff) { + RTKBT_DBG("global_version is not set, get it!"); + gEVersion = rtk_get_eversion(dev_entry); + } + 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 ret_val; +} + +int download_patch(struct usb_interface *intf) +{ + dev_data *dev_entry; + patch_info *pinfo; + xchange_data *xdata = NULL; + uint8_t *fw_buf; + int ret_val; + int max_patch_size = 0; + + RTKBT_DBG("download_patch start"); + dev_entry = dev_data_find(intf); + if (NULL == dev_entry) { + ret_val = -1; + RTKBT_ERR("NULL == dev_entry"); + goto patch_end; + } + + xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); + if (NULL == xdata) { + ret_val = -1; + RTKBT_DBG("NULL == xdata"); + goto patch_end; + } + + init_xdata(xdata, dev_entry); + + ret_val = check_fw_chip_ver(dev_entry, xdata); + if (ret_val != 0 ) + goto patch_end; + + xdata->fw_len = load_firmware(dev_entry, &xdata->fw_data); + if (xdata->fw_len <= 0) { + RTKBT_ERR("load firmware failed!"); + ret_val = -1; + goto patch_end; + } + + fw_buf = xdata->fw_data; + + pinfo = dev_entry->patch_entry; + if (!pinfo) { + RTKBT_ERR("%s: No patch entry", __func__); + ret_val = -1; + goto patch_fail; + } + max_patch_size = get_max_patch_size(pinfo->chip_type); + if (xdata->fw_len > max_patch_size) { + RTKBT_ERR("FW/CONFIG total length larger than allowed %d", + max_patch_size); + ret_val = -1; + goto patch_fail; + } + + ret_val = download_data(xdata); + if (ret_val < 0) { + RTKBT_ERR("download_data failed, err %d", ret_val); + goto patch_fail; + } + + ret_val = check_fw_version(xdata); + if (ret_val <= 0) { + RTKBT_ERR("%s: Read Local Version Info failure after download", + __func__); + ret_val = -1; + goto patch_fail; + } + + ret_val = 0; +patch_fail: + kfree(fw_buf); +patch_end: + if (xdata != NULL) { + if (xdata->send_pkt) + kfree(xdata->send_pkt); + if (xdata->rcv_pkt) + kfree(xdata->rcv_pkt); + kfree(xdata); + } + RTKBT_DBG("Rtk patch end %d", ret_val); + return ret_val; +} + +#ifdef RTKBT_SWITCH_PATCH +/* @return: + * -1: error + * 0: download patch successfully + * >0: patch already exists */ +int download_special_patch(struct usb_interface *intf, const char *special_name) +{ + dev_data *dev_entry; + patch_info *pinfo; + xchange_data *xdata = NULL; + uint8_t *fw_buf; + int result; + char name1[64]; + char *origin_name1; + char name2[64]; + char *origin_name2; + int max_patch_size = 0; + + RTKBT_DBG("Download LPS Patch start"); + dev_entry = dev_data_find(intf); + if (!dev_entry) { + RTKBT_ERR("No Patch found"); + return -1; + } + + xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); + if (!xdata) { + RTKBT_ERR("Couldn't alloc xdata"); + return -1; + } + + init_xdata(xdata, dev_entry); + + result = check_fw_version(xdata); + if (result < 0) { + RTKBT_ERR("Failed to get Local Version Information"); + goto patch_end; + + } else if (result > 0) { + RTKBT_DBG("Firmware already exists"); + /* Patch alread exists, just return */ + if (gEVersion == 0xff) { + RTKBT_DBG("global_version is not set, get it!"); + gEVersion = rtk_get_eversion(dev_entry); + } + goto patch_end; + } + memset(name1, 0, sizeof(name1)); + memset(name2, 0, sizeof(name2)); + origin_name1 = dev_entry->patch_entry->patch_name; + origin_name2 = dev_entry->patch_entry->config_name; + memcpy(name1, special_name, strlen(special_name)); + 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->config_name = name2; + RTKBT_INFO("Loading %s and %s", name1, name2); + xdata->fw_len = load_firmware(dev_entry, &xdata->fw_data); + dev_entry->patch_entry->patch_name = origin_name1; + dev_entry->patch_entry->config_name = origin_name2; + if (xdata->fw_len <= 0) { + result = -1; + RTKBT_ERR("load firmware failed!"); + goto patch_end; + } + + fw_buf = xdata->fw_data; + + pinfo = dev_entry->patch_entry; + if (!pinfo) { + RTKBT_ERR("%s: No patch entry", __func__); + result = -1; + goto patch_fail; + } + max_patch_size = get_max_patch_size(pinfo->chip_type); + if (xdata->fw_len > max_patch_size) { + result = -1; + RTKBT_ERR("FW/CONFIG total length larger than allowed %d", + max_patch_size); + goto patch_fail; + } + + result = download_data(xdata); + if (result < 0) { + RTKBT_ERR("download_data failed, err %d", result); + goto patch_fail; + } + + result = check_fw_version(xdata); + if (result <= 0) { + RTKBT_ERR("%s: Read Local Version Info failure after download", + __func__); + result = -1; + goto patch_fail; + } + + result = 0; + +patch_fail: + kfree(fw_buf); +patch_end: + if (xdata->send_pkt) + kfree(xdata->send_pkt); + if (xdata->rcv_pkt) + kfree(xdata->rcv_pkt); + kfree(xdata); + RTKBT_DBG("Download LPS Patch end %d", result); + + return result; +} +#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 +int set_scan(struct usb_interface *intf) +{ + dev_data *dev_entry; + xchange_data *xdata = NULL; + int result; + + RTKBT_DBG("%s", __func__); + dev_entry = dev_data_find(intf); + if (!dev_entry) + return -1; + + xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); + if (!xdata) { + RTKBT_ERR("Could not alloc xdata"); + return -1; + } + + init_xdata(xdata, dev_entry); + + if ( !xdata->send_pkt || !xdata->rcv_pkt ){ + result = -1; + goto end; + } + + xdata->cmd_hdr->opcode = cpu_to_le16(STARTSCAN_OPCODE); + xdata->cmd_hdr->plen = 1; + xdata->pkt_len = CMD_HDR_LEN + 1; + xdata->send_pkt[CMD_HDR_LEN] = 1; + + result = send_hci_cmd(xdata); + if (result < 0) + goto end; + +end: + kfree(xdata->send_pkt); + kfree(xdata->rcv_pkt); + kfree(xdata); + + RTKBT_DBG("%s done", __func__); + + return result; +} +#endif + +dev_data *dev_data_find(struct usb_interface * intf) +{ + dev_data *dev_entry; + + list_for_each_entry(dev_entry, &dev_data_list, list_node) { + if (dev_entry->intf == intf) { + patch_info *patch = dev_entry->patch_entry; + if (!patch) + return NULL; + + RTKBT_INFO("chip type value: 0x%02x", patch->chip_type); + return dev_entry; + } + } + + return NULL; +} + +patch_info *get_patch_entry(struct usb_device * udev) +{ + patch_info *patch_entry; + uint16_t pid; + + patch_entry = fw_patch_table; + pid = le16_to_cpu(udev->descriptor.idProduct); + RTKBT_DBG("pid = 0x%x", pid); + while (pid != patch_entry->prod_id) { + if (0 == patch_entry->prod_id) { + RTKBT_DBG + ("get_patch_entry =NULL, can not find device pid in patch_table"); + return NULL; //break; + } + patch_entry++; + } + + return patch_entry; +} + +static int is_mac(u8 chip_type, u16 offset) +{ + int result = 0; + + switch (chip_type) { + case RTL8822BU: + case RTL8723DU: + case RTL8821CU: + if (offset == 0x0044) + return 1; + break; + case RTL8822CU: + case RTL8761BU: + case RTL8852AU: + case RTL8733BU: + case RTL8852BU: + case RTL8852CU: + case RTL8822EU: + case RTL8851BU: + case RTL8852DU: + if (offset == 0x0030) + return 1; + break; + case RTLPREVIOUS: + if (offset == 0x003c) + return 1; + break; + } + + return result; +} + +static uint16_t get_mac_offset(u8 chip_type) +{ + switch (chip_type) { + case RTL8822BU: + case RTL8723DU: + case RTL8821CU: + return 0x0044; + case RTL8822CU: + case RTL8761BU: + case RTL8852AU: + case RTL8733BU: + case RTL8852BU: + case RTL8852CU: + case RTL8822EU: + case RTL8851BU: + case RTL8852DU: + return 0x0030; + case RTLPREVIOUS: + return 0x003c; + default: + return 0x003c; + } +} + +static void merge_configs(struct list_head *head, struct list_head *head2) +{ + struct list_head *epos, *enext; + struct list_head *pos, *next; + struct cfg_list_item *n; + struct cfg_list_item *extra; + + if (!head || !head2) + return; + + if (list_empty(head2)) + return; + + if (list_empty(head)) { + list_splice_tail(head2, head); + INIT_LIST_HEAD(head2); + return; + } + + /* Add or update & replace */ + list_for_each_safe(epos, enext, head2) { + extra = list_entry(epos, struct cfg_list_item, list); + + list_for_each_safe(pos, next, head) { + n = list_entry(pos, struct cfg_list_item, list); + if (extra->offset == n->offset) { + if (extra->len < n->len) { + /* Update the cfg data */ + RTKBT_INFO("Update cfg: ofs %04x len %u", + n->offset, n->len); + memcpy(n->data, extra->data, + extra->len); + list_del(epos); + kfree(extra); + break; + } else { + /* Replace the item */ + list_del(epos); + list_replace_init(pos, epos); + /* free the old item */ + kfree(n); + } + } + + } + + } + + if (list_empty(head2)) + return; + list_for_each_safe(epos, enext, head2) { + extra = list_entry(epos, struct cfg_list_item, list); + RTKBT_INFO("Add new cfg: ofs %04x, len %u", extra->offset, + extra->len); + /* Add the item to list */ + list_del(epos); + list_add_tail(epos, head); + } +} + +static int rtk_parse_config_file(u8 *config_buf, int filelen) +{ + struct rtk_bt_vendor_config *config = (void *)config_buf; + u16 config_len = 0, temp = 0; + struct rtk_bt_vendor_config_entry *entry = NULL; + u32 i = 0; + struct cfg_list_item *item; + + if (!config_buf) + return -EINVAL; + + config_len = le16_to_cpu(config->data_len); + entry = config->entry; + + if (le32_to_cpu(config->signature) != RTK_VENDOR_CONFIG_MAGIC) { + RTKBT_ERR("sig magic num %08x, not rtk vendor magic %08x", + config->signature, RTK_VENDOR_CONFIG_MAGIC); + return -1; + } + + if (config_len != filelen - BT_CONFIG_HDRLEN) { + RTKBT_ERR("config length %u is not right %u", config_len, + (u16)(filelen - BT_CONFIG_HDRLEN)); + return -1; + } + + for (i = 0; i < config_len;) { + /* Add config item to list */ + item = kzalloc(sizeof(*item) + entry->entry_len, GFP_KERNEL); + if (item) { + item->offset = le16_to_cpu(entry->offset); + item->len = entry->entry_len; + memcpy(item->data, entry->entry_data, item->len); + list_add_tail(&item->list, &list_configs); + } else { + RTKBT_ERR("Cannot alloc mem for entry %04x, %u", + entry->offset, entry->entry_len); + break; + } + + temp = entry->entry_len + + sizeof(struct rtk_bt_vendor_config_entry); + i += temp; + entry = + (struct rtk_bt_vendor_config_entry *)((uint8_t *) entry + + temp); + } + + return 0; +} + +static uint8_t rtk_get_fw_project_id(uint8_t * p_buf) +{ + uint8_t opcode; + uint8_t len; + uint8_t data = 0; + + do { + opcode = *p_buf; + len = *(p_buf - 1); + if (opcode == 0x00) { + if (len == 1) { + data = *(p_buf - 2); + RTKBT_DBG + ("rtk_get_fw_project_id: opcode %d, len %d, data %d", + opcode, len, data); + break; + } else { + RTKBT_ERR + ("rtk_get_fw_project_id: invalid len %d", + len); + } + } + p_buf -= len + 2; + } while (*p_buf != 0xFF); + + return data; +} + +struct rtb_ota_flag { + uint8_t eco; + uint8_t enable; + uint16_t reserve; +} __attribute__ ((packed)); + +struct rtb_security_hdr { + uint8_t eco; + uint8_t pri; + uint8_t key_id; + uint8_t reserve; + uint32_t security_len; + uint8_t *payload; +} __attribute__ ((packed)); + +struct rtb_dummy_hdr { + uint8_t eco; + uint8_t pri; + uint8_t reserve; + uint32_t dummy_len; + uint8_t *payload; +} __attribute__ ((packed)); + +struct rtb_snippet_hdr { + uint8_t eco; + uint8_t pri; + uint16_t reserve; + uint32_t snippet_len; + uint8_t *payload; +} __attribute__ ((packed)); + +struct patch_node { + uint8_t eco; + uint8_t pri; + uint8_t key_id; + uint8_t reserve; + uint32_t len; + uint8_t *payload; + struct list_head list; +} __attribute__ ((packed)); + +/* Add a node to alist that is in ascending order. */ +static void insert_queue_sort(struct list_head *head, struct patch_node *node) +{ + struct list_head *pos; + struct list_head *next; + struct patch_node *tmp; + + if(!head || !node) { + return; + } + list_for_each_safe(pos, next, head) { + tmp = list_entry(pos, struct patch_node, list); + if(tmp->pri >= node->pri) + break; + } + __list_add(&node->list, pos->prev, pos); +} + +static int insert_patch(struct patch_node *patch_node_hdr, uint8_t *section_pos, + uint32_t opcode, uint32_t *patch_len, uint8_t *sec_flag) +{ + struct patch_node *tmp; + int i; + uint32_t numbers; + uint32_t section_len = 0; + uint8_t eco = 0; + uint8_t *pos = section_pos + 8; + + numbers = get_unaligned_le16(pos); + RTKBT_DBG("number 0x%04x", numbers); + + pos += 4; + for (i = 0; i < numbers; i++) { + eco = (uint8_t)*(pos); + RTKBT_DBG("eco 0x%02x, Eversion:%02x", eco, gEVersion); + if (eco == gEVersion + 1) { + tmp = (struct patch_node*)kzalloc(sizeof(struct patch_node), GFP_KERNEL); + tmp->pri = (uint8_t)*(pos + 1); + if(opcode == PATCH_SECURITY_HEADER) + tmp->key_id = (uint8_t)*(pos + 1); + + section_len = get_unaligned_le32(pos + 4); + tmp->len = section_len; + *patch_len += section_len; + RTKBT_DBG("Pri:%d, Patch length 0x%04x", tmp->pri, tmp->len); + tmp->payload = pos + 8; + if(opcode != PATCH_SECURITY_HEADER) { + insert_queue_sort(&(patch_node_hdr->list), tmp); + } else { + if((g_key_id == tmp->key_id) && (g_key_id > 0)) { + insert_queue_sort(&(patch_node_hdr->list), tmp); + *sec_flag = 1; + } else { + pos += (8 + section_len); + kfree(tmp); + continue; + } + } + } else { + section_len = get_unaligned_le32(pos + 4); + RTKBT_DBG("Patch length 0x%04x", section_len); + } + pos += (8 + section_len); + } + return 0; +} + +static uint8_t *rtb_get_patch_header(int *len, + struct patch_node *patch_node_hdr, uint8_t * epatch_buf, + uint8_t key_id) +{ + uint16_t i, j; + struct rtb_new_patch_hdr *new_patch; + uint8_t sec_flag = 0; + uint32_t number_of_ota_flag; + uint32_t patch_len = 0; + uint8_t *section_pos; + uint8_t *ota_flag_pos; + uint32_t number_of_section; + + struct rtb_section_hdr section_hdr; + struct rtb_ota_flag ota_flag; + + new_patch = (struct rtb_new_patch_hdr *)epatch_buf; + number_of_section = le32_to_cpu(new_patch->number_of_section); + + RTKBT_DBG("FW version 0x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", + *(epatch_buf + 8), *(epatch_buf + 9), *(epatch_buf + 10), + *(epatch_buf + 11),*(epatch_buf + 12), *(epatch_buf + 13), + *(epatch_buf + 14), *(epatch_buf + 15)); + + section_pos = epatch_buf + 20; + + for (i = 0; i < number_of_section; i++) { + section_hdr.opcode = get_unaligned_le32(section_pos); + section_hdr.section_len = get_unaligned_le32(section_pos + 4); + RTKBT_DBG("opcode 0x%04x", section_hdr.opcode); + + switch (section_hdr.opcode) { + case PATCH_SNIPPETS: + insert_patch(patch_node_hdr, section_pos, PATCH_SNIPPETS, &patch_len, NULL); + break; + case PATCH_SECURITY_HEADER: + if(!g_key_id) + break; + + sec_flag = 0; + insert_patch(patch_node_hdr, section_pos, PATCH_SECURITY_HEADER, &patch_len, &sec_flag); + if(sec_flag) + break; + + for (i = 0; i < number_of_section; i++) { + section_hdr.opcode = get_unaligned_le32(section_pos); + section_hdr.section_len = get_unaligned_le32(section_pos + 4); + if(section_hdr.opcode == PATCH_DUMMY_HEADER) { + insert_patch(patch_node_hdr, section_pos, PATCH_DUMMY_HEADER, &patch_len, NULL); + } + section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len); + } + break; + case PATCH_DUMMY_HEADER: + if(g_key_id) { + break; + } + insert_patch(patch_node_hdr, section_pos, PATCH_DUMMY_HEADER, &patch_len, NULL); + break; + case PATCH_OTA_FLAG: + ota_flag_pos = section_pos + 4; + number_of_ota_flag = get_unaligned_le32(ota_flag_pos); + ota_flag.eco = (uint8_t)*(ota_flag_pos + 1); + if (ota_flag.eco == gEVersion + 1) { + for (j = 0; j < number_of_ota_flag; j++) { + if (ota_flag.eco == gEVersion + 1) { + ota_flag.enable = get_unaligned_le32(ota_flag_pos + 4); + } + } + } + break; + default: + RTKBT_ERR("Unknown Opcode. Ignore"); + } + section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len); + } + *len = patch_len; + + return NULL; +} + +static int rtk_get_patch_entry(uint8_t * epatch_buf, + struct rtk_epatch_entry *entry) +{ + uint32_t svn_ver; + uint32_t coex_ver; + uint32_t tmp; + uint16_t i; + uint16_t number_of_total_patch; + struct rtk_epatch *epatch_info = (struct rtk_epatch *)epatch_buf; + + number_of_total_patch = + le16_to_cpu(epatch_info->number_of_total_patch); + RTKBT_DBG("fw_version = 0x%x", le32_to_cpu(epatch_info->fw_version)); + RTKBT_DBG("number_of_total_patch = %d", number_of_total_patch); + + /* get right epatch entry */ + for (i = 0; i < number_of_total_patch; i++) { + if (get_unaligned_le16(epatch_buf + 14 + 2 * i) == + gEVersion + 1) { + entry->chipID = gEVersion + 1; + entry->patch_length = get_unaligned_le16(epatch_buf + + 14 + + 2 * number_of_total_patch + + 2 * i); + entry->start_offset = get_unaligned_le32(epatch_buf + + 14 + + 4 * number_of_total_patch + + 4 * i); + break; + } + } + + if (i >= number_of_total_patch) { + entry->patch_length = 0; + entry->start_offset = 0; + RTKBT_ERR("No corresponding patch found\n"); + return 0; + } + + svn_ver = get_unaligned_le32(epatch_buf + + entry->start_offset + + entry->patch_length - 8); + coex_ver = get_unaligned_le32(epatch_buf + + entry->start_offset + + entry->patch_length - 12); + + RTKBT_DBG("chipID %d", entry->chipID); + RTKBT_DBG("patch_length 0x%04x", entry->patch_length); + RTKBT_DBG("start_offset 0x%08x", entry->start_offset); + + RTKBT_DBG("Svn version: %8d", svn_ver); + tmp = ((coex_ver >> 16) & 0x7ff) + (coex_ver >> 27) * 10000; + RTKBT_DBG("Coexistence: BTCOEX_20%06d-%04x", + tmp, (coex_ver & 0xffff)); + + return 0; +} + +static int bachk(const char *str) +{ + if (!str) + return -1; + + if (strlen(str) != 17) + return -1; + + while (*str) { + if (!isxdigit(*str++)) + return -1; + + if (!isxdigit(*str++)) + return -1; + + if (*str == 0) + break; + + if (*str++ != ':') + return -1; + } + + return 0; +} + +static int request_bdaddr(u8 *buf) +{ + int size; + int rc; + struct file *file; + u8 tbuf[BDADDR_STRING_LEN + 1]; + char *str; + int i; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + loff_t pos = 0; +#endif + + if (!buf) + return -EINVAL; + + file = filp_open(BDADDR_FILE, O_RDONLY, 0); + if (IS_ERR(file)) + return -ENOENT; + + if (!S_ISREG(file_inode(file)->i_mode)) + return -EINVAL; + size = i_size_read(file_inode(file)); + if (size <= 0) + return -EINVAL; + + if (size > BDADDR_STRING_LEN) + size = BDADDR_STRING_LEN; + + memset(tbuf, 0, sizeof(tbuf)); + RTKBT_INFO("size = %d", size); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + rc = kernel_read(file, tbuf, size, &pos); +#else + rc = kernel_read(file, 0, tbuf, size); +#endif + fput(file); + if (rc != size) { + if (rc >= 0) + rc = -EIO; + goto fail; + } + + if (bachk(tbuf) < 0) { + rc = -EINVAL; + goto fail; + } + + str = tbuf; + for (i = 5; i >= 0; i--) { + buf[i] = simple_strtol(str, NULL, 16); + str += 3; + } + + return size; +fail: + return rc; +} + +static u8 *load_config(dev_data *dev_entry, int *length) +{ + patch_info *patch_entry; + const char *config_name; + const struct firmware *fw; + struct usb_device *udev; + int result; + u8 *buf; + u8 *p; + u16 config_len; + u16 dlen; + u8 tmp_buf[32]; + int file_sz; + struct cfg_list_item *n; + struct list_head *pos, *next; + u8 chip_type; + + config_lists_init(); + patch_entry = dev_entry->patch_entry; + config_name = patch_entry->config_name; + udev = dev_entry->udev; + chip_type = patch_entry->chip_type; + + RTKBT_INFO("config filename %s", config_name); + result = request_firmware(&fw, config_name, &udev->dev); + if (result < 0) + return NULL; + + file_sz = fw->size; + buf = (u8 *)fw->data; + + /* Load extra configs */ + config_file_proc(EXTRA_CONFIG_FILE); + list_for_each_safe(pos, next, &list_extracfgs) { + n = list_entry(pos, struct cfg_list_item, list); + RTKBT_INFO("extra cfg: ofs %04x, len %u", n->offset, n->len); + } + + /* Load extra bdaddr config */ + memset(tmp_buf, 0, sizeof(tmp_buf)); + result = request_bdaddr(tmp_buf); + if (result > 0) { + n = kzalloc(sizeof(*n) + 6, GFP_KERNEL); + if (n) { + n->offset = get_mac_offset(patch_entry->chip_type); + n->len = 6; + memcpy(n->data, tmp_buf, 6); + list_add_tail(&n->list, &list_extracfgs); + } else { + RTKBT_WARN("Couldn't alloc mem for bdaddr"); + } + } else { + if (result == -ENOENT) + RTKBT_WARN("no bdaddr file %s", BDADDR_FILE); + else + RTKBT_WARN("invalid customer bdaddr %d", result); + } + + RTKBT_INFO("Origin cfg len %u", (u16)file_sz); + util_hexdump((const u8 *)buf, file_sz); + + result = rtk_parse_config_file(buf, file_sz); + if (result < 0) { + RTKBT_ERR("Parse config file error"); + buf = NULL; + goto done; + } + + merge_configs(&list_configs, &list_extracfgs); + + /* Calculate the config_len */ + config_len = 4; /* magic word length */ + config_len += 2; /* data length field */ + dlen = 0; + list_for_each_safe(pos, next, &list_configs) { + n = list_entry(pos, struct cfg_list_item, list); + switch (n->offset) { + case 0x003c: + case 0x0030: + case 0x0044: + if (is_mac(chip_type, n->offset) && n->len == 6) { + char s[18]; + sprintf(s, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + n->data[5], n->data[4], + n->data[3], n->data[2], + n->data[1], n->data[0]); + RTKBT_INFO("bdaddr ofs %04x, %s", n->offset, s); + } + break; + default: + break; + } + + config_len += (3 + n->len); + dlen += (3 + n->len); + } + + + buf = kzalloc(config_len, GFP_KERNEL); + if (!buf) { + RTKBT_ERR("Couldn't alloc buf for configs"); + goto done; + } + + /* Save configs to a buffer */ + memcpy(buf, cfg_magic, 4); + buf[4] = dlen & 0xff; + buf[5] = (dlen >> 8) & 0xff; + p = buf + 6; + list_for_each_safe(pos, next, &list_configs) { + n = list_entry(pos, struct cfg_list_item, list); + p[0] = n->offset & 0xff; + p[1] = (n->offset >> 8) & 0xff; + p[2] = n->len; + memcpy(p + 3, n->data, n->len); + p += (3 + n->len); + } + + RTKBT_INFO("New cfg len %u", config_len); + util_hexdump((const u8 *)buf, config_len); + + *length = config_len; + +done: + config_lists_free(); + release_firmware(fw); + + return buf; +} + +static int rtk_vendor_read(dev_data * dev_entry, uint8_t class) +{ + struct rtk_chip_type_evt *chip_type; + struct rtk_security_proj_evt *sec_proj; + patch_info *patch_entry; + int ret_val = 0; + xchange_data *xdata = NULL; + unsigned char cmd_ct_buf[] = {0x10, 0x38, 0x04, 0x28, 0x80}; + unsigned char cmd_cv_buf[] = {0x10, 0x3A, 0x04, 0x28, 0x80}; + unsigned char cmd_sec_buf[] = {0x10, 0xA4, 0x0D, 0x00, 0xb0}; + + xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); + if (NULL == xdata) { + ret_val = 0xFE; + RTKBT_DBG("NULL == xdata"); + return ret_val; + } + + init_xdata(xdata, dev_entry); + + xdata->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_READ_CMD); + xdata->cmd_hdr->plen = 5; + memcpy(xdata->send_pkt, &(xdata->cmd_hdr->opcode), 2); + memcpy(xdata->send_pkt+2, &(xdata->cmd_hdr->plen), 1); + + switch (class) { + case READ_CHIP_TYPE: + memcpy(xdata->send_pkt+3, cmd_ct_buf, sizeof(cmd_ct_buf)); + break; + case READ_CHIP_VER: + memcpy(xdata->send_pkt+3, cmd_cv_buf, sizeof(cmd_cv_buf)); + break; + case READ_SEC_PROJ: + memcpy(xdata->send_pkt+3, cmd_sec_buf, sizeof(cmd_sec_buf)); + break; + default: + break; + } + + xdata->pkt_len = CMD_HDR_LEN + 5; + + ret_val = send_hci_cmd(xdata); + if (ret_val < 0) { + RTKBT_ERR("Failed to send read RTK chip_type cmd."); + ret_val = 0xFE; + goto read_end; + } + + ret_val = rcv_hci_evt(xdata); + if (ret_val < 0) { + RTKBT_ERR("Failed to receive HCI event for chip type."); + ret_val = 0xFE; + goto read_end; + } + + patch_entry = xdata->dev_entry->patch_entry; + if(class == READ_SEC_PROJ){ + sec_proj = (struct rtk_security_proj_evt *)(xdata->rsp_para); + RTKBT_DBG("sec_proj->status = 0x%x, sec_proj->key_id = 0x%x", + sec_proj->status, sec_proj->key_id); + if (sec_proj->status) { + ret_val = 0; + } else { + ret_val = sec_proj->key_id; + g_key_id = sec_proj->key_id; + } + } else { + chip_type = (struct rtk_chip_type_evt *)(xdata->rsp_para); + RTKBT_DBG("chip_type->status = 0x%x, chip_type->chip = 0x%x", + chip_type->status, chip_type->chip); + if (chip_type->status) { + ret_val = 0; + } else { + ret_val = chip_type->chip; + } + } + +read_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; +} + +int load_firmware(dev_data * dev_entry, uint8_t ** buff) +{ + const struct firmware *fw; + struct usb_device *udev; + patch_info *patch_entry; + char *fw_name; + int fw_len = 0, ret_val = 0, config_len = 0, buf_len = -1; + uint8_t *buf = NULL, *config_file_buf = NULL, *epatch_buf = NULL; + uint8_t proj_id = 0; + uint8_t need_download_fw = 1; + uint16_t lmp_version; + struct rtk_epatch_entry current_entry = { 0 }; + + struct list_head *pos, *next; + struct patch_node *tmp; + struct patch_node patch_node_hdr; + + RTKBT_DBG("load_firmware start"); + udev = dev_entry->udev; + patch_entry = dev_entry->patch_entry; + lmp_version = patch_entry->lmp_sub; + RTKBT_DBG("lmp_version = 0x%04x", lmp_version); + + config_file_buf = load_config(dev_entry, &config_len); + + fw_name = patch_entry->patch_name; + RTKBT_DBG("fw name is %s", fw_name); + ret_val = request_firmware(&fw, fw_name, &udev->dev); + if (ret_val < 0) { + RTKBT_ERR("request_firmware error"); + fw_len = 0; + kfree(config_file_buf); + config_file_buf = NULL; + goto fw_fail; + } + + INIT_LIST_HEAD(&patch_node_hdr.list); + + epatch_buf = kzalloc(fw->size, GFP_KERNEL); + if (NULL == epatch_buf) + goto alloc_fail; + + memcpy(epatch_buf, fw->data, fw->size); + buf_len = fw->size + config_len; + + if (lmp_version == ROM_LMP_8723a) { + RTKBT_DBG("This is 8723a, use old patch style!"); + + if (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) == 0) { + RTKBT_ERR("8723a Check signature error!"); + need_download_fw = 0; + } else { + if (!(buf = kzalloc(buf_len, GFP_KERNEL))) { + RTKBT_ERR("Can't alloc memory for fw&config"); + buf_len = -1; + } else { + RTKBT_DBG("8723a, fw copy direct"); + memcpy(buf, epatch_buf, fw->size); + if (config_len) { + memcpy(&buf[buf_len - config_len], + config_file_buf, config_len); + } + } + } + } else { + RTKBT_ERR("This is not 8723a, use new patch style!"); + + /* Get version from ROM */ + gEVersion = rtk_get_eversion(dev_entry); + RTKBT_DBG("%s: New gEVersion %d", __func__, gEVersion); + if (gEVersion == 0xFE) { + RTKBT_ERR("%s: Read ROM version failure", __func__); + need_download_fw = 0; + fw_len = 0; + goto alloc_fail; + } + + /* check Signature and Extension Section Field */ + if (((memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) != 0) && (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) != 0))|| + memcmp(epatch_buf + buf_len - config_len - 4, + Extension_Section_SIGNATURE, 4) != 0) { + RTKBT_ERR("Check SIGNATURE error! do not download fw"); + need_download_fw = 0; + } else { + proj_id = + rtk_get_fw_project_id(epatch_buf + buf_len - + config_len - 5); + + if (lmp_version != project_id[proj_id]) { + RTKBT_ERR + ("lmp_version is %x, project_id is %x, does not match!!!", + lmp_version, project_id[proj_id]); + need_download_fw = 0; + } else { + RTKBT_DBG + ("lmp_version is %x, project_id is %x, match!", + lmp_version, project_id[proj_id]); + + if(memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) { + int key_id = rtk_vendor_read(dev_entry, READ_SEC_PROJ); + RTKBT_DBG("%s: key id %d", __func__, key_id); + if (key_id < 0) { + RTKBT_ERR("%s: Read key id failure", __func__); + need_download_fw = 0; + fw_len = 0; + goto alloc_fail; + } + rtb_get_patch_header(&buf_len, &patch_node_hdr, epatch_buf, key_id); + if(buf_len == 0) + goto alloc_fail; + RTKBT_DBG("buf_len = 0x%x", buf_len); + buf_len += config_len; + } else { + rtk_get_patch_entry(epatch_buf, ¤t_entry); + + if (current_entry.patch_length == 0) + goto alloc_fail; + + buf_len = current_entry.patch_length + config_len; + RTKBT_DBG("buf_len = 0x%x", buf_len); + } + + if (!(buf = kzalloc(buf_len, GFP_KERNEL))) { + 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); + RTKBT_DBG("len = 0x%x", tmp->len); + memcpy(buf + tmp_len, tmp->payload, tmp->len); + tmp_len += tmp->len; + list_del_init(pos); + kfree(tmp); + } + if (config_len) { + memcpy(&buf + [buf_len - config_len], + config_file_buf, + config_len); + } + } else { + memcpy(buf, + epatch_buf + + 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); + } + } + } + } + } + } + + RTKBT_DBG("fw:%s exists, config file:%s exists", + (buf_len > 0) ? "" : "not", (config_len > 0) ? "" : "not"); + if (buf && (buf_len > 0) && (need_download_fw)) { + fw_len = buf_len; + *buff = buf; + } + + RTKBT_DBG("load_firmware done"); + +alloc_fail: + release_firmware(fw); + + if (epatch_buf) + kfree(epatch_buf); + + if (config_file_buf) + kfree(config_file_buf); +fw_fail: + if (fw_len == 0) + kfree(buf); + + return fw_len; +} + +void init_xdata(xchange_data * xdata, dev_data * dev_entry) +{ + memset(xdata, 0, sizeof(xchange_data)); + xdata->dev_entry = dev_entry; + xdata->pipe_in = usb_rcvintpipe(dev_entry->udev, INTR_EP); + xdata->pipe_out = usb_sndctrlpipe(dev_entry->udev, CTRL_EP); + xdata->send_pkt = kzalloc(PKT_LEN, GFP_KERNEL); + xdata->rcv_pkt = kzalloc(PKT_LEN, GFP_KERNEL); + xdata->cmd_hdr = (struct hci_command_hdr *)(xdata->send_pkt); + xdata->evt_hdr = (struct hci_event_hdr *)(xdata->rcv_pkt); + xdata->cmd_cmp = + (struct hci_ev_cmd_complete *)(xdata->rcv_pkt + EVT_HDR_LEN); + xdata->req_para = xdata->send_pkt + CMD_HDR_LEN; + xdata->rsp_para = xdata->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN; +} + +int check_fw_version(xchange_data * xdata) +{ + struct hci_rp_read_local_version *read_ver_rsp; + patch_info *patch_entry; + int ret_val; + int retry = 0; + uint16_t lmp_subver, hci_rev, manufacturer; + + /* Ensure that the first cmd is hci reset after system suspend + * or system reboot */ + send_reset_command(xdata); + +get_ver: + xdata->cmd_hdr->opcode = cpu_to_le16(HCI_OP_READ_LOCAL_VERSION); + xdata->cmd_hdr->plen = 0; + xdata->pkt_len = CMD_HDR_LEN; + + ret_val = send_hci_cmd(xdata); + if (ret_val < 0) { + RTKBT_ERR("%s: Failed to send HCI command.", __func__); + goto version_end; + } + + ret_val = rcv_hci_evt(xdata); + if (ret_val < 0) { + RTKBT_ERR("%s: Failed to receive HCI event.", __func__); + goto version_end; + } + + 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); + manufacturer = le16_to_cpu(read_ver_rsp->manufacturer); + + RTKBT_DBG("read_ver_rsp->lmp_subver = 0x%x", lmp_subver); + RTKBT_DBG("read_ver_rsp->hci_rev = 0x%x", hci_rev); + RTKBT_DBG("patch_entry->lmp_sub = 0x%x", patch_entry->lmp_sub); + if (patch_entry->lmp_sub != lmp_subver) { + return 1; + } + + ret_val = 0; +version_end: + if (ret_val) { + send_reset_command(xdata); + retry++; + if (retry < 2) + goto get_ver; + } + + return ret_val; +} + +uint8_t rtk_get_eversion(dev_data * dev_entry) +{ + struct rtk_eversion_evt *eversion; + patch_info *patch_entry; + int ret_val = 0; + xchange_data *xdata = NULL; + + RTKBT_DBG("%s: gEVersion %d", __func__, gEVersion); + if (gEVersion != 0xFF && gEVersion != 0xFE) { + RTKBT_DBG("gEVersion != 0xFF, return it directly!"); + return gEVersion; + } + + xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); + if (NULL == xdata) { + ret_val = 0xFE; + RTKBT_DBG("NULL == xdata"); + return ret_val; + } + + init_xdata(xdata, dev_entry); + + xdata->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_READ_RTK_ROM_VERISION); + xdata->cmd_hdr->plen = 0; + xdata->pkt_len = CMD_HDR_LEN; + + ret_val = send_hci_cmd(xdata); + if (ret_val < 0) { + RTKBT_ERR("Failed to send read RTK rom version cmd."); + ret_val = 0xFE; + goto version_end; + } + + ret_val = rcv_hci_evt(xdata); + if (ret_val < 0) { + RTKBT_ERR("Failed to receive HCI event for rom version."); + ret_val = 0xFE; + goto version_end; + } + + patch_entry = xdata->dev_entry->patch_entry; + eversion = (struct rtk_eversion_evt *)(xdata->rsp_para); + RTKBT_DBG("eversion->status = 0x%x, eversion->version = 0x%x", + eversion->status, eversion->version); + if (eversion->status) { + ret_val = 0; + //global_eversion = 0; + } else { + ret_val = eversion->version; + //global_eversion = eversion->version; + } + +version_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; +} + +int download_data(xchange_data * xdata) +{ + download_cp *cmd_para; + download_rp *evt_para; + uint8_t *pcur; + int pkt_len, frag_num, frag_len; + int i, ret_val; + int j = 0; + + RTKBT_DBG("download_data start"); + + cmd_para = (download_cp *) xdata->req_para; + evt_para = (download_rp *) xdata->rsp_para; + pcur = xdata->fw_data; + pkt_len = CMD_HDR_LEN + sizeof(download_cp); + frag_num = xdata->fw_len / PATCH_SEG_MAX + 1; + frag_len = PATCH_SEG_MAX; + + for (i = 0; i < frag_num; i++) { + cmd_para->index = j++; + + if(cmd_para->index == 0x7f) + j = 1; + + if (i == (frag_num - 1)) { + cmd_para->index |= DATA_END; + frag_len = xdata->fw_len % PATCH_SEG_MAX; + pkt_len -= (PATCH_SEG_MAX - frag_len); + } + xdata->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE); + xdata->cmd_hdr->plen = sizeof(uint8_t) + frag_len; + xdata->pkt_len = pkt_len; + memcpy(cmd_para->data, pcur, frag_len); + + ret_val = send_hci_cmd(xdata); + if (ret_val < 0) { + return ret_val; + } + + ret_val = rcv_hci_evt(xdata); + if (ret_val < 0) { + return ret_val; + } + + if (0 != evt_para->status) { + return -1; + } + + pcur += PATCH_SEG_MAX; + } + + RTKBT_DBG("download_data done"); + return xdata->fw_len; +} + +int send_hci_cmd(xchange_data * xdata) +{ + int ret_val; + + ret_val = usb_control_msg(xdata->dev_entry->udev, xdata->pipe_out, + 0, USB_TYPE_CLASS, 0, 0, + (void *)(xdata->send_pkt), + xdata->pkt_len, MSG_TO); + + if (ret_val < 0) + RTKBT_ERR("%s; failed to send ctl msg for hci cmd, err %d", + __func__, ret_val); + + return ret_val; +} + +int rcv_hci_evt(xchange_data * xdata) +{ + int ret_len = 0, ret_val = 0; + int i; // Added by Realtek + + while (1) { + // **************************** Modifed by Realtek (begin) + for (i = 0; i < 5; i++) // Try to send USB interrupt message 5 times. + { + ret_val = + usb_interrupt_msg(xdata->dev_entry->udev, + xdata->pipe_in, + (void *)(xdata->rcv_pkt), PKT_LEN, + &ret_len, MSG_TO); + if (ret_val >= 0) + break; + } + // **************************** Modifed by Realtek (end) + + if (ret_val < 0) { + RTKBT_ERR("%s; no usb intr msg for hci event, err %d", + __func__, ret_val); + return ret_val; + } + + if (CMD_CMP_EVT == xdata->evt_hdr->evt) { + if (xdata->cmd_hdr->opcode == xdata->cmd_cmp->opcode) + return ret_len; + } + } +} + +void print_acl(struct sk_buff *skb, int dataOut) +{ +#if PRINT_ACL_DATA + uint wlength = skb->len; + uint icount = 0; + u16 *handle = (u16 *) (skb->data); + u16 dataLen = *(handle + 1); + u8 *acl_data = (u8 *) (skb->data); +//if (0==dataOut) + printk("%d handle:%04x,len:%d,", dataOut, *handle, dataLen); +//else +// printk("In handle:%04x,len:%d,",*handle,dataLen); +/* for(icount=4;(icountlen; + uint icount = 0; + u16 *opcode = (u16 *) (skb->data); + u8 *cmd_data = (u8 *) (skb->data); + u8 paramLen = *(cmd_data + 2); + + switch (*opcode) { + case HCI_OP_INQUIRY: + printk("HCI_OP_INQUIRY"); + break; + case HCI_OP_INQUIRY_CANCEL: + printk("HCI_OP_INQUIRY_CANCEL"); + break; + case HCI_OP_EXIT_PERIODIC_INQ: + printk("HCI_OP_EXIT_PERIODIC_INQ"); + break; + case HCI_OP_CREATE_CONN: + printk("HCI_OP_CREATE_CONN"); + break; + case HCI_OP_DISCONNECT: + printk("HCI_OP_DISCONNECT"); + break; + case HCI_OP_CREATE_CONN_CANCEL: + printk("HCI_OP_CREATE_CONN_CANCEL"); + break; + case HCI_OP_ACCEPT_CONN_REQ: + printk("HCI_OP_ACCEPT_CONN_REQ"); + break; + case HCI_OP_REJECT_CONN_REQ: + printk("HCI_OP_REJECT_CONN_REQ"); + break; + case HCI_OP_AUTH_REQUESTED: + printk("HCI_OP_AUTH_REQUESTED"); + break; + case HCI_OP_SET_CONN_ENCRYPT: + printk("HCI_OP_SET_CONN_ENCRYPT"); + break; + case HCI_OP_REMOTE_NAME_REQ: + printk("HCI_OP_REMOTE_NAME_REQ"); + break; + case HCI_OP_READ_REMOTE_FEATURES: + printk("HCI_OP_READ_REMOTE_FEATURES"); + break; + case HCI_OP_SNIFF_MODE: + printk("HCI_OP_SNIFF_MODE"); + break; + case HCI_OP_EXIT_SNIFF_MODE: + printk("HCI_OP_EXIT_SNIFF_MODE"); + break; + case HCI_OP_SWITCH_ROLE: + printk("HCI_OP_SWITCH_ROLE"); + break; + case HCI_OP_SNIFF_SUBRATE: + printk("HCI_OP_SNIFF_SUBRATE"); + break; + case HCI_OP_RESET: + printk("HCI_OP_RESET"); + break; + default: + printk("CMD"); + break; + } + printk(":%04x,len:%d,", *opcode, paramLen); + for (icount = 3; (icount < wlength) && (icount < 24); icount++) { + printk("%02x ", *(cmd_data + icount)); + } + printk("\n"); + +#endif +} + +void print_event(struct sk_buff *skb) +{ +#if PRINT_CMD_EVENT + uint wlength = skb->len; + uint icount = 0; + u8 *opcode = (u8 *) (skb->data); + u8 paramLen = *(opcode + 1); + + switch (*opcode) { + case HCI_EV_INQUIRY_COMPLETE: + printk("HCI_EV_INQUIRY_COMPLETE"); + break; + case HCI_EV_INQUIRY_RESULT: + printk("HCI_EV_INQUIRY_RESULT"); + break; + case HCI_EV_CONN_COMPLETE: + printk("HCI_EV_CONN_COMPLETE"); + break; + case HCI_EV_CONN_REQUEST: + printk("HCI_EV_CONN_REQUEST"); + break; + case HCI_EV_DISCONN_COMPLETE: + printk("HCI_EV_DISCONN_COMPLETE"); + break; + case HCI_EV_AUTH_COMPLETE: + printk("HCI_EV_AUTH_COMPLETE"); + break; + case HCI_EV_REMOTE_NAME: + printk("HCI_EV_REMOTE_NAME"); + break; + case HCI_EV_ENCRYPT_CHANGE: + printk("HCI_EV_ENCRYPT_CHANGE"); + break; + case HCI_EV_CHANGE_LINK_KEY_COMPLETE: + printk("HCI_EV_CHANGE_LINK_KEY_COMPLETE"); + break; + case HCI_EV_REMOTE_FEATURES: + printk("HCI_EV_REMOTE_FEATURES"); + break; + case HCI_EV_REMOTE_VERSION: + printk("HCI_EV_REMOTE_VERSION"); + break; + case HCI_EV_QOS_SETUP_COMPLETE: + printk("HCI_EV_QOS_SETUP_COMPLETE"); + break; + case HCI_EV_CMD_COMPLETE: + printk("HCI_EV_CMD_COMPLETE"); + break; + case HCI_EV_CMD_STATUS: + printk("HCI_EV_CMD_STATUS"); + break; + case HCI_EV_ROLE_CHANGE: + printk("HCI_EV_ROLE_CHANGE"); + break; + case HCI_EV_NUM_COMP_PKTS: + printk("HCI_EV_NUM_COMP_PKTS"); + break; + case HCI_EV_MODE_CHANGE: + printk("HCI_EV_MODE_CHANGE"); + break; + case HCI_EV_PIN_CODE_REQ: + printk("HCI_EV_PIN_CODE_REQ"); + break; + case HCI_EV_LINK_KEY_REQ: + printk("HCI_EV_LINK_KEY_REQ"); + break; + case HCI_EV_LINK_KEY_NOTIFY: + printk("HCI_EV_LINK_KEY_NOTIFY"); + break; + case HCI_EV_CLOCK_OFFSET: + printk("HCI_EV_CLOCK_OFFSET"); + break; + case HCI_EV_PKT_TYPE_CHANGE: + printk("HCI_EV_PKT_TYPE_CHANGE"); + break; + case HCI_EV_PSCAN_REP_MODE: + printk("HCI_EV_PSCAN_REP_MODE"); + break; + case HCI_EV_INQUIRY_RESULT_WITH_RSSI: + printk("HCI_EV_INQUIRY_RESULT_WITH_RSSI"); + break; + case HCI_EV_REMOTE_EXT_FEATURES: + printk("HCI_EV_REMOTE_EXT_FEATURES"); + break; + case HCI_EV_SYNC_CONN_COMPLETE: + printk("HCI_EV_SYNC_CONN_COMPLETE"); + break; + case HCI_EV_SYNC_CONN_CHANGED: + printk("HCI_EV_SYNC_CONN_CHANGED"); + break; + case HCI_EV_SNIFF_SUBRATE: + printk("HCI_EV_SNIFF_SUBRATE"); + break; + case HCI_EV_EXTENDED_INQUIRY_RESULT: + printk("HCI_EV_EXTENDED_INQUIRY_RESULT"); + break; + case HCI_EV_IO_CAPA_REQUEST: + printk("HCI_EV_IO_CAPA_REQUEST"); + break; + case HCI_EV_SIMPLE_PAIR_COMPLETE: + printk("HCI_EV_SIMPLE_PAIR_COMPLETE"); + break; + case HCI_EV_REMOTE_HOST_FEATURES: + printk("HCI_EV_REMOTE_HOST_FEATURES"); + break; + default: + printk("event"); + break; + } + printk(":%02x,len:%d,", *opcode, paramLen); + for (icount = 2; (icount < wlength) && (icount < 24); icount++) { + printk("%02x ", *(opcode + icount)); + } + printk("\n"); + +#endif +} diff --git a/BSP/linux-kernel/drivers/bluetooth/rtk_misc.h b/BSP/linux-kernel/drivers/bluetooth/rtk_misc.h new file mode 100755 index 000000000..eb2a53fd4 --- /dev/null +++ b/BSP/linux-kernel/drivers/bluetooth/rtk_misc.h @@ -0,0 +1,134 @@ +/* + * + * 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 +#include +#include +#include +#include +#include + +/* Download LPS patch when host suspends or power off + * LPS patch name: lps_rtl8xxx_fw + * LPS config name: lps_rtl8xxx_config + * Download normal patch when host resume or power on */ +/* #define RTKBT_SWITCH_PATCH */ + +/* RTKBT Power-on for sideband wake-up by LE Advertising from Remote. */ +/* Note that it's necessary to apply TV FW Patch. */ +/* #define RTKBT_SUSPEND_WAKEUP */ +/* #define RTKBT_SHUTDOWN_WAKEUP */ +#define RTKBT_POWERKEY_WAKEUP + +/* RTKBT Power-on Whitelist for sideband wake-up by LE Advertising from Remote. + * Note that it's necessary to apply TV FW Patch. */ +/* #define RTKBT_TV_POWERON_WHITELIST */ + +#if 1 +#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_WARN(fmt, arg...) printk(KERN_WARNING "rtk_btusb: " fmt "\n", ## arg) +#else +#define RTKBT_DBG(fmt, arg...) +#endif + +#if 1 +#define RTKBT_ERR(fmt, arg...) printk(KERN_ERR "rtk_btusb: " fmt "\n" , ## arg) +#else +#define RTKBT_ERR(fmt, arg...) +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33) +#define USB_RPM +#endif + +#define CONFIG_NEEDS_BINDING + +/* If module is still powered when kernel suspended, there is no re-binding. */ +#ifdef RTKBT_SWITCH_PATCH +#undef CONFIG_NEEDS_BINDING +#endif + +/* USB SS */ +#if (defined CONFIG_BTUSB_AUTOSUSPEND) && (defined USB_RPM) +#define BTUSB_RPM +#endif + +#define PRINT_CMD_EVENT 0 +#define PRINT_ACL_DATA 0 + +extern int patch_add(struct usb_interface *intf); +extern void patch_remove(struct usb_interface *intf); +extern int download_patch(struct usb_interface *intf); +extern void print_event(struct sk_buff *skb); +extern void print_command(struct sk_buff *skb); +extern void print_acl(struct sk_buff *skb, int dataOut); + +#if defined RTKBT_SWITCH_PATCH || defined RTKBT_TV_POWERON_WHITELIST +int __rtk_send_hci_cmd(struct usb_device *udev, u8 *buf, u16 size); +#endif + +#ifdef RTKBT_SWITCH_PATCH +#define RTLBT_CLOSE (1 << 0) +struct api_context { + u32 flags; + struct completion done; + int status; +}; + +int download_special_patch(struct usb_interface *intf, const char *special_name); +#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 +int set_scan(struct usb_interface *intf); +#endif