From 3d15bb40b55fd0d1277d29b7675d1eb38f37c8a6 Mon Sep 17 00:00:00 2001 From: Shobek Attupurath Date: Fri, 18 Oct 2024 17:54:13 +0000 Subject: [PATCH] rtk_btusb: Update driver version to 3.1.65ab490.20240531-141726 - Update driver to 3.1.65ab490.20240531-141726 - Add support for 8852CE Bug 4915378 Change-Id: Ib3f695047ee306fd07db424239c985195619aad1 Signed-off-by: Shobek Attupurath Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3237077 GVS: buildbot_gerritrpt Reviewed-by: Ashutosh Jha Reviewed-by: Revanth Kumar Uppala --- drivers/bluetooth/realtek/Makefile | 37 +- drivers/bluetooth/realtek/rtk_bt.c | 575 +++++++++++-- drivers/bluetooth/realtek/rtk_bt.h | 31 +- drivers/bluetooth/realtek/rtk_coex.c | 1133 ++++++++++++++++++++------ drivers/bluetooth/realtek/rtk_coex.h | 92 ++- drivers/bluetooth/realtek/rtk_misc.c | 600 ++++++++++---- drivers/bluetooth/realtek/rtk_misc.h | 67 +- 7 files changed, 1930 insertions(+), 605 deletions(-) diff --git a/drivers/bluetooth/realtek/Makefile b/drivers/bluetooth/realtek/Makefile index 8ab63f97..71439544 100644 --- a/drivers/bluetooth/realtek/Makefile +++ b/drivers/bluetooth/realtek/Makefile @@ -1,22 +1,15 @@ -# SPDX-License-Identifier: GPL-2.0 - -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 - -obj-m := rtk_btusb.o -rtk_btusb-objs := rtk_coex.o \ - rtk_misc.o \ - rtk_bt.o +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) + KDIR := /lib/modules/$(KVER)/build + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + rm -rf *.o *.mod.c *.mod.o *.ko *.symvers *.order *.a + +endif diff --git a/drivers/bluetooth/realtek/rtk_bt.c b/drivers/bluetooth/realtek/rtk_bt.c index 95b7b7e4..b2f034c3 100644 --- a/drivers/bluetooth/realtek/rtk_bt.c +++ b/drivers/bluetooth/realtek/rtk_bt.c @@ -1,8 +1,22 @@ -// SPDX-License-Identifier: GPL-2.0-only /* * * 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 @@ -22,7 +36,7 @@ #include "rtk_bt.h" #include "rtk_misc.h" -#define VERSION "3.1.6fd4e69.20220818-105856" +#define VERSION "3.1.65ab490.20240531-141726" #ifdef BTCOEX #include "rtk_coex.h" @@ -35,95 +49,64 @@ static DEFINE_SEMAPHORE(switch_sem); #endif #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1) -static bool reset = 0; +static bool reset = true; #endif static struct usb_driver btusb_driver; -static struct usb_device_id btusb_table[] = { +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static u16 iso_min_conn_handle = 0x1b; +#endif + +static const struct usb_device_id btusb_table[] = { + /* Generic Bluetooth USB device */ + { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, + + /* Generic Bluetooth USB interface */ + { USB_INTERFACE_INFO(0xe0, 0x01, 0x01) }, + + {} +}; + +static const struct usb_device_id blacklist_table[] = { { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x0bda, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x13d3, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x0489, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x1358, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x04ca, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x2ff8, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x0b05, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x0930, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x10ec, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x04c5, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x0cb5, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 }, { - .match_flags = USB_DEVICE_ID_MATCH_VENDOR | - USB_DEVICE_ID_MATCH_INT_INFO, + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, .idVendor = 0x0cb8, - .bInterfaceClass = 0xe0, - .bInterfaceSubClass = 0x01, - .bInterfaceProtocol = 0x01 + }, { + .match_flags = USB_DEVICE_ID_MATCH_VENDOR, + .idVendor = 0x04b8, }, { } }; @@ -148,20 +131,6 @@ static struct btusb_data *rtk_alloc(struct usb_interface *intf) MODULE_DEVICE_TABLE(usb, btusb_table); -static int inc_tx(struct btusb_data *data) -{ - unsigned long flags; - int rv; - - spin_lock_irqsave(&data->txlock, flags); - rv = test_bit(BTUSB_SUSPENDING, &data->flags); - if (!rv) - data->tx_in_flight++; - spin_unlock_irqrestore(&data->txlock, flags); - - return rv; -} - #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) static inline void btusb_free_frags(struct btusb_data *data) { @@ -204,7 +173,11 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) } len = min_t(uint, bt_cb(skb)->expect, count); +#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; @@ -259,15 +232,26 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) } len = min_t(uint, bt_cb(skb)->expect, count); +#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) { - __le16 dlen = hci_acl_hdr(skb)->dlen; + struct hci_acl_hdr *h = hci_acl_hdr(skb); + __le16 dlen = h->dlen; +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + __u16 handle = __le16_to_cpu(h->handle) & 0xfff; + if(handle >= iso_min_conn_handle) { + bt_cb(skb)->pkt_type = HCI_ISODATA_PKT; + } +#endif /* Complete ACL header */ bt_cb(skb)->expect = __le16_to_cpu(dlen); @@ -293,10 +277,42 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count) return err; } +#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; @@ -316,7 +332,24 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) } len = min_t(uint, bt_cb(skb)->expect, count); + +#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; @@ -347,6 +380,21 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count) 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) @@ -626,6 +674,51 @@ retry: } } +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) +static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len, + int mtu, struct btusb_data *data) +{ + int i = 0, offset = 0; + unsigned int interval; + + BT_DBG("len %d mtu %d", len, mtu); + + /* For mSBC ALT 6 settings some Realtek chips need to transmit the data + * continuously without the zero length of USB packets. + */ + if (btrealtek_test_flag(data->hdev, REALTEK_ALT6_CONTINUOUS_TX_CHIP)) + goto ignore_usb_alt6_packet_flow; + + /* For mSBC ALT 6 setting the host will send the packet at continuous + * flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting + * 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets. + * To maintain the rate we send 63bytes of usb packets alternatively for + * 7ms and 8ms to maintain the rate as 7.5ms. + */ + if (data->usb_alt6_packet_flow) { + interval = 7; + data->usb_alt6_packet_flow = false; + } else { + interval = 6; + data->usb_alt6_packet_flow = true; + } + + for (i = 0; i < interval; i++) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = offset; + } + +ignore_usb_alt6_packet_flow: + if (len && i < BTUSB_MAX_ISOC_FRAMES) { + urb->iso_frame_desc[i].offset = offset; + urb->iso_frame_desc[i].length = len; + i++; + } + + urb->number_of_packets = i; +} +#endif + static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu) { int i, offset = 0; @@ -675,6 +768,12 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress); +#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; @@ -684,6 +783,7 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP; urb->transfer_buffer = buf; urb->transfer_buffer_length = size; +#endif __fill_isoc_descriptor(urb, size, le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize)); @@ -734,8 +834,8 @@ 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); + 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; @@ -751,6 +851,49 @@ done: kfree_skb(skb); } +#if HCI_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static int rtl_read_iso_handle_range(struct hci_dev *hdev) +{ + struct sk_buff *skb; + struct __rp { + u8 status; + u8 min_handle[2]; + } *rp; + int ret = -EIO; + + skb = __hci_cmd_sync(hdev, 0xfdab, 0, NULL, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + return PTR_ERR(skb); + } + + /* FIXME: if the return status is not zero, __hci_cmd_sync() would + * return an error and we would not reach here. + */ + if (skb->data[0]) { + RTKBT_ERR("%s: Read failed, status %0x", hdev->name, + skb->data[0]); + goto err; + } + + if (skb->len < sizeof(*rp)) { + RTKBT_WARN("%s: The len %u of rp is too short", __func__, + skb->len); + goto err; + } + + rp = (void *)skb->data; + iso_min_conn_handle = (u16)rp->min_handle[1] << 8 | rp->min_handle[0]; + RTKBT_DBG("ISO handle range (handle >= %04x)", iso_min_conn_handle); + + kfree_skb(skb); + + return 0; +err: + kfree_skb(skb); + return ret; +} +#endif + static int btusb_open(struct hci_dev *hdev) { struct btusb_data *data = GET_DRV_DATA(hdev); @@ -775,6 +918,10 @@ static int btusb_open(struct hci_dev *hdev) 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; @@ -817,6 +964,32 @@ failed: 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 @@ -920,8 +1093,152 @@ 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) { @@ -931,14 +1248,20 @@ 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; - struct urb *urb; unsigned int pipe; int err; +#endif - //RTKBT_DBG("%s", hdev->name); +// RTKBT_DBG("%s", hdev->name); + /* After Kernel version 4.4.0, move the check into the + * hci_send_frame function before calling hdev->send + */ +#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) if (!test_bit(HCI_RUNNING, &hdev->flags)) { /* If the parameter is wrong, the hdev isn't the correct * one. Then no HCI commands can be sent. @@ -946,13 +1269,15 @@ int btusb_send_frame(struct sk_buff *skb) RTKBT_ERR("HCI is not running"); 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) +#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) && \ + HCI_VERSION_CODE < KERNEL_VERSION(3, 18, 0) skb->dev = (void *)hdev; #endif @@ -963,6 +1288,14 @@ int btusb_send_frame(struct sk_buff *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; @@ -988,11 +1321,24 @@ int btusb_send_frame(struct sk_buff *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 - rtk_btcoex_parse_l2cap_data_tx(skb->data, skb->len); + 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; @@ -1009,7 +1355,22 @@ int btusb_send_frame(struct sk_buff *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; @@ -1035,6 +1396,7 @@ int btusb_send_frame(struct sk_buff *skb) default: return -EILSEQ; + } err = inc_tx(data); @@ -1057,12 +1419,14 @@ skip_waking: } else { usb_mark_last_busy(data->udev); } - usb_free_urb(urb); 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) { @@ -1181,6 +1545,9 @@ static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data, 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]; @@ -1221,7 +1588,14 @@ static void btusb_work(struct work_struct *work) new_alts = data->sco_num; } } else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) { - new_alts = btusb_find_altsetting(data, 6) ? 6 : 1; + if (btusb_find_altsetting(data, 6)) + new_alts = 6; + else if (btusb_find_altsetting(data, 3) && + hdev->sco_mtu >= 72 && + test_bit(BTUSB_USE_ALT3_FOR_WBS, &data->flags)) + new_alts = 3; + else + new_alts = 1; } if (btusb_switch_alt_setting(hdev, new_alts) < 0) @@ -1421,7 +1795,7 @@ static int rtkbt_pm_notify(struct notifier_block *notifier, result = __rtk_send_hci_cmd(udev, cmd, 3); kfree(cmd); msleep(100); /* From FW colleague's recommendation */ - result = download_lps_patch(intf); + result = download_special_patch(intf, "lps_"); #endif #ifdef RTKBT_TV_POWERON_WHITELIST @@ -1540,13 +1914,23 @@ static int btusb_probe(struct usb_interface *intf, struct usb_device *udev; udev = interface_to_usbdev(intf); - RTKBT_DBG("btusb_probe intf->cur_altsetting->desc.bInterfaceNumber %d", + RTKBT_INFO("btusb_probe intf->cur_altsetting->desc.bInterfaceNumber %d", intf->cur_altsetting->desc.bInterfaceNumber); /* 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); @@ -1636,6 +2020,13 @@ static int btusb_probe(struct usb_interface *intf, 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); @@ -1645,10 +2036,14 @@ static int btusb_probe(struct usb_interface *intf, 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); - RTKBT_DBG("set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);"); #endif /* Interface numbers are hardcoded in the specification */ diff --git a/drivers/bluetooth/realtek/rtk_bt.h b/drivers/bluetooth/realtek/rtk_bt.h index 9ed86dc5..c60307ad 100644 --- a/drivers/bluetooth/realtek/rtk_bt.h +++ b/drivers/bluetooth/realtek/rtk_bt.h @@ -1,12 +1,23 @@ -// SPDX-License-Identifier: GPL-2.0-only /* * * Realtek Bluetooth USB driver * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * */ -#ifndef __RTK_BT_H__ -#define __RTK_BT_H__ - #include #include #include @@ -30,14 +41,17 @@ /* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */ #define HCI_VERSION_CODE LINUX_VERSION_CODE -#ifdef CONFIG_BTCOEX +#define CONFIG_BTCOEX 1 +#define CONFIG_BTUSB_WAKEUP_HOST 0 + +#if CONFIG_BTCOEX #define BTCOEX #endif /*********************************** ** Realtek - For rtk_btusb driver ** ***********************************/ -#ifdef CONFIG_BTUSB_WAKEUP_HOST +#if CONFIG_BTUSB_WAKEUP_HOST #define BTUSB_WAKEUP_HOST #endif @@ -84,6 +98,7 @@ int btusb_send_frame(struct sk_buff *skb); #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; @@ -125,6 +140,7 @@ struct btusb_data { #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; @@ -136,6 +152,3 @@ struct btusb_data { struct notifier_block shutdown_notifier; void *context; }; - - -#endif /* __RTK_BT_H__ */ \ No newline at end of file diff --git a/drivers/bluetooth/realtek/rtk_coex.c b/drivers/bluetooth/realtek/rtk_coex.c index 85eb91e0..08155ee2 100644 --- a/drivers/bluetooth/realtek/rtk_coex.c +++ b/drivers/bluetooth/realtek/rtk_coex.c @@ -1,8 +1,22 @@ -// SPDX-License-Identifier: GPL-2.0-only /* * * 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 @@ -21,6 +35,15 @@ #include "rtk_coex.h" +#define DIRECT_A2DP_MEDIA_PACKET_PROCESSING + +#define HCI_CONN_HANDLE_UNSET_START (0x0eff + 1) +#define HCI_CONN_TYPE_BREDR_ACL 0x00 +#define HCI_CONN_TYPE_SCO 0x01 +#define HCI_CONN_TYPE_LE_ACL 0x02 +#define HCI_CONN_TYPE_CIS 0x04 +#define HCI_CONN_TYPE_BIS 0x05 + /* Software coex message can be sent to and receive from WiFi driver by * UDP socket or exported symbol */ /* #define RTK_COEX_OVER_SYMBOL */ @@ -45,8 +68,18 @@ #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_DEBUG "rtk_btcoex: " fmt "\n", ## arg) -#define RTKBT_ERR(fmt, arg...) printk(KERN_DEBUG "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_ERR "rtk_btcoex: " fmt "\n", ## arg) + +#ifndef HCI_OP_LE_REMOVE_ISO_PATH +#define HCI_OP_LE_REMOVE_ISO_PATH 0x206f +struct hci_cp_le_remove_iso_path { + u16 handle; + u8 direction; +} __attribute__((packed)); +#endif +#define HCI_DATA_PATH_INPUT (1 << 0) /* Host to Controller */ +#define HCI_DATA_PATH_OUTPUT (1 << 1) /* Controller to Host */ static struct rtl_coex_struct btrtl_coex; @@ -59,7 +92,7 @@ static u8 rtw_coex_on; #endif #endif -#define is_profile_connected(profile) ((btrtl_coex.profile_bitmap & BIT(profile)) > 0) +#define is_profile_connected(conn, profile) ((conn->profile_bitmap & BIT(profile)) > 0) #define is_profile_busy(conn, profile) ((conn->profile_status & BIT(profile)) > 0) #ifdef RTB_SOFTWARE_MAILBOX @@ -84,11 +117,9 @@ static int rtl_alloc_buff(struct rtl_coex_struct *coex) spin_lock_init(&coex->buff_lock); - INIT_LIST_HEAD(&coex->ev_used_list); INIT_LIST_HEAD(&coex->ev_free_list); - - INIT_LIST_HEAD(&coex->l2_used_list); INIT_LIST_HEAD(&coex->l2_free_list); + INIT_LIST_HEAD(&coex->hci_pkt_list); n = NUM_RTL_HCI_EV * sizeof(struct rtl_hci_ev); ev_size = ALIGN(n, sizeof(unsigned long)); @@ -129,14 +160,15 @@ static void rtl_free_buff(struct rtl_coex_struct *coex) { struct rtl_hci_ev *ev; struct rtl_l2_buff *l2; + struct rtl_hci_hdr *hdr; unsigned long flags; spin_lock_irqsave(&coex->buff_lock, flags); - while (!list_empty(&coex->ev_used_list)) { - ev = list_entry(coex->ev_used_list.next, struct rtl_hci_ev, - list); - list_del(&ev->list); + while (!list_empty(&coex->hci_pkt_list)) { + hdr = list_entry(coex->hci_pkt_list.next, struct rtl_hci_hdr, + list); + list_del(&hdr->list); } while (!list_empty(&coex->ev_free_list)) { @@ -145,12 +177,6 @@ static void rtl_free_buff(struct rtl_coex_struct *coex) list_del(&ev->list); } - while (!list_empty(&coex->l2_used_list)) { - l2 = list_entry(coex->l2_used_list.next, struct rtl_l2_buff, - list); - list_del(&l2->list); - } - while (!list_empty(&coex->l2_free_list)) { l2 = list_entry(coex->l2_free_list.next, struct rtl_l2_buff, list); @@ -190,8 +216,9 @@ static int rtl_ev_node_to_used(struct rtl_coex_struct *coex, { unsigned long flags; + ev->type = HCI_PT_EVT; spin_lock_irqsave(&coex->buff_lock, flags); - list_add_tail(&ev->list, &coex->ev_used_list); + list_add_tail(&ev->list, &coex->hci_pkt_list); spin_unlock_irqrestore(&coex->buff_lock, flags); return 0; @@ -223,8 +250,12 @@ static int rtl_l2_node_to_used(struct rtl_coex_struct *coex, { unsigned long flags; + if (l2->out) + l2->type = HCI_PT_L2SIG_TX; + else + l2->type = HCI_PT_L2SIG_RX; spin_lock_irqsave(&coex->buff_lock, flags); - list_add_tail(&l2->list, &coex->l2_used_list); + list_add_tail(&l2->list, &coex->hci_pkt_list); spin_unlock_irqrestore(&coex->buff_lock, flags); return 0; @@ -278,28 +309,31 @@ static void rtk_check_setup_timer(rtk_conn_prof * phci_conn, uint8_t profile_ind int delay = msecs_to_jiffies(1000); if (profile_index == profile_a2dp) { phci_conn->a2dp_packet_count = 0; - queue_delayed_work(btrtl_coex.timer_wq, &phci_conn->a2dp_count_work, delay); + queue_delayed_work(btrtl_coex.fw_wq, + &phci_conn->a2dp_count_work, delay); } if (profile_index == profile_pan) { phci_conn->pan_packet_count = 0; - queue_delayed_work(btrtl_coex.timer_wq, &phci_conn->pan_count_work, delay); + queue_delayed_work(btrtl_coex.fw_wq, + &phci_conn->pan_count_work, delay); } /* hogp & voice share one timer now */ if ((profile_index == profile_hogp) || (profile_index == profile_voice)) { - if ((0 == btrtl_coex.profile_refcount[profile_hogp]) - && (0 == btrtl_coex.profile_refcount[profile_voice])) { + if ((0 == phci_conn->profile_refcount[profile_hogp]) + && (0 == phci_conn->profile_refcount[profile_voice])) { phci_conn->hogp_packet_count = 0; phci_conn->voice_packet_count = 0; - queue_delayed_work(btrtl_coex.timer_wq, &phci_conn->hogp_count_work, delay); + queue_delayed_work(btrtl_coex.fw_wq, + &phci_conn->hogp_count_work, delay); } } } static void rtk_check_del_timer(uint8_t profile_index, rtk_conn_prof * phci_conn) { - RTKBT_DBG("%s: handle 0x%4x", __func__, phci_conn->handle); + RTKBT_DBG("%s: handle 0x%04x", __func__, phci_conn->handle); if (profile_a2dp == profile_index) { phci_conn->a2dp_packet_count = 0; cancel_delayed_work_sync(&phci_conn->a2dp_count_work); @@ -310,13 +344,13 @@ static void rtk_check_del_timer(uint8_t profile_index, rtk_conn_prof * phci_conn } if (profile_hogp == profile_index) { phci_conn->hogp_packet_count = 0; - if (btrtl_coex.profile_refcount[profile_voice] == 0) { + if (phci_conn->profile_refcount[profile_voice] == 0) { cancel_delayed_work_sync(&phci_conn->hogp_count_work); } } if (profile_voice == profile_index) { phci_conn->voice_packet_count = 0; - if (btrtl_coex.profile_refcount[profile_hogp] == 0) { + if (phci_conn->profile_refcount[profile_hogp] == 0) { cancel_delayed_work_sync(&phci_conn->hogp_count_work); } } @@ -343,9 +377,11 @@ static rtk_conn_prof *find_connection_by_handle(struct rtl_coex_struct * coex, static rtk_conn_prof *allocate_connection_by_handle(uint16_t handle) { rtk_conn_prof *phci_conn = NULL; - phci_conn = kmalloc(sizeof(rtk_conn_prof), GFP_ATOMIC); - if (phci_conn) + phci_conn = kzalloc(sizeof(rtk_conn_prof), GFP_ATOMIC); + if (phci_conn) { phci_conn->handle = handle; + phci_conn->direction = 0; + } return phci_conn; } @@ -361,23 +397,36 @@ static void add_connection_to_hash(struct rtl_coex_struct * coex, { struct list_head *head = &coex->conn_hash; list_add_tail(&desc->list, head); - INIT_DELAYED_WORK(&desc->a2dp_count_work, (void *)count_a2dp_packet_timeout); - INIT_DELAYED_WORK(&desc->pan_count_work, (void *)count_pan_packet_timeout); - INIT_DELAYED_WORK(&desc->hogp_count_work, (void *)count_hogp_packet_timeout); + INIT_DELAYED_WORK(&desc->a2dp_count_work, + count_a2dp_packet_timeout); + INIT_DELAYED_WORK(&desc->pan_count_work, + count_pan_packet_timeout); + INIT_DELAYED_WORK(&desc->hogp_count_work, + count_hogp_packet_timeout); } -static void delete_connection_from_hash(rtk_conn_prof * desc) +static void delete_connection_from_hash(rtk_conn_prof *desc) { if (desc) { cancel_delayed_work_sync(&desc->a2dp_count_work); cancel_delayed_work_sync(&desc->pan_count_work); cancel_delayed_work_sync(&desc->hogp_count_work); + + set_bit(RTL_COEX_CONN_REMOVING, &btrtl_coex.flags); + while (test_bit(RTL_COEX_PKT_COUNTING, &btrtl_coex.flags)) { + RTKBT_INFO("%s: counting packets", __func__); + schedule(); + } + mutex_lock(&btrtl_coex.conn_mutex); list_del(&desc->list); + mutex_unlock(&btrtl_coex.conn_mutex); + clear_bit(RTL_COEX_CONN_REMOVING, &btrtl_coex.flags); + kfree(desc); } } -static void flush_connection_hash(struct rtl_coex_struct * coex) +static void flush_connection_hash(struct rtl_coex_struct *coex) { struct list_head *head = &coex->conn_hash; struct list_head *iter = NULL, *temp = NULL; @@ -389,8 +438,7 @@ static void flush_connection_hash(struct rtl_coex_struct * coex) cancel_delayed_work_sync(&desc->a2dp_count_work); cancel_delayed_work_sync(&desc->pan_count_work); cancel_delayed_work_sync(&desc->hogp_count_work); - list_del(&desc->list); - kfree(desc); + delete_connection_from_hash(desc); } } //INIT_LIST_HEAD(head); @@ -409,7 +457,6 @@ static uint8_t list_allocate_add(uint16_t handle, uint16_t psm, rtk_prof_info *pprof_info = NULL; pprof_info = kmalloc(sizeof(rtk_prof_info), GFP_ATOMIC); - if (NULL == pprof_info) { RTKBT_ERR("list_allocate_add: allocate error"); return FALSE; @@ -439,26 +486,32 @@ static uint8_t list_allocate_add(uint16_t handle, uint16_t psm, return TRUE; } -static void delete_profile_from_hash(rtk_prof_info * desc) +static void delete_profile_from_hash(rtk_prof_info *desc) { if (desc) { RTKBT_DBG("Delete profile: hndl 0x%04x, psm 0x%04x, dcid 0x%04x, " "scid 0x%04x", desc->handle, desc->psm, desc->dcid, desc->scid); + set_bit(RTL_COEX_CONN_REMOVING, &btrtl_coex.flags); + while (test_bit(RTL_COEX_PKT_COUNTING, &btrtl_coex.flags)) { + RTKBT_INFO("%s: counting packets", __func__); + schedule(); + } list_del(&desc->list); + clear_bit(RTL_COEX_CONN_REMOVING, &btrtl_coex.flags); + kfree(desc); - desc = NULL; } } -static void flush_profile_hash(struct rtl_coex_struct * coex) +static void flush_profile_hash(struct rtl_coex_struct *coex) { struct list_head *head = &coex->profile_list; struct list_head *iter = NULL, *temp = NULL; rtk_prof_info *desc = NULL; - spin_lock(&btrtl_coex.spin_lock_profile); + mutex_lock(&btrtl_coex.profile_mutex); list_for_each_safe(iter, temp, head) { desc = list_entry(iter, rtk_prof_info, list); if (desc) { @@ -466,13 +519,11 @@ static void flush_profile_hash(struct rtl_coex_struct * coex) "dcid 0x%04x, scid 0x%04x", desc->handle, desc->psm, desc->dcid, desc->scid); - list_del(&desc->list); - kfree(desc); - desc = NULL; + delete_profile_from_hash(desc); } } //INIT_LIST_HEAD(head); - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); } static rtk_prof_info *find_profile_by_handle_scid(struct rtl_coex_struct * @@ -649,15 +700,16 @@ static void rtk_notify_profileinfo_to_fw(void) RTKBT_DBG("%s: handle 0x%04x", __func__, hci_conn->handle); *p++ = hci_conn->profile_bitmap; + btrtl_coex.profile_status |= hci_conn->profile_status; } else { UINT16_TO_STREAM(p, hci_conn->handle); UINT16_TO_STREAM(p, hci_conn->profile_bitmap); - RTKBT_DBG("%s: profile_bitmap 0x%02x", __func__, - hci_conn->profile_bitmap); UINT16_TO_STREAM(p, hci_conn->profile_status); - RTKBT_DBG("%s: profile_status 0x%02x", __func__, + RTKBT_DBG("%s: profile_status 0x%04x", __func__, hci_conn->profile_status); } + RTKBT_DBG("%s: profile_bitmap 0x%04x", __func__, + hci_conn->profile_bitmap); handle_number--; } if (0 == handle_number) @@ -666,6 +718,7 @@ static void rtk_notify_profileinfo_to_fw(void) if(!profileinfo_cmd) { *p++ = btrtl_coex.profile_status; + btrtl_coex.profile_status = 0; rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_PROFILE_REPORT_LEGACY_COMMAND, buffer_size, p_buf); } else { @@ -684,30 +737,34 @@ static void update_profile_state(rtk_conn_prof * phci_conn, RTKBT_DBG("%s: is_busy %d, profile_index %x", __func__, is_busy, profile_index); - - if ((btrtl_coex.profile_bitmap & BIT(profile_index)) == 0) { + if ((phci_conn->profile_bitmap & BIT(profile_index)) == 0) { RTKBT_ERR("%s: : ERROR!!! profile(Index: %x) does not exist", __func__, profile_index); return; } + if (is_busy) { if ((phci_conn->profile_status & BIT(profile_index)) == 0) { need_update = TRUE; phci_conn->profile_status |= BIT(profile_index); + + if(profile_index == profile_sink) + phci_conn->profile_status |= BIT(profile_a2dpsink); } } else { if ((phci_conn->profile_status & BIT(profile_index)) > 0) { need_update = TRUE; phci_conn->profile_status &= ~(BIT(profile_index)); + + if(profile_index == profile_sink) + phci_conn->profile_status &= ~(BIT(profile_a2dpsink)); } } if (need_update) { - RTKBT_DBG("%s: btrtl_coex.profie_bitmap = %x", - __func__, btrtl_coex.profile_bitmap); - RTKBT_DBG("%s: btrtl_coex.profile_status 0x%02x, phci_conn->profile_status 0x%02x", - __func__, btrtl_coex.profile_status, phci_conn->profile_status); + RTKBT_DBG("%s: phci_conn->profile_status 0x%02x", + __func__, phci_conn->profile_status); rtk_notify_profileinfo_to_fw(); } } @@ -716,53 +773,51 @@ static void update_profile_connection(rtk_conn_prof * phci_conn, uint8_t profile_index, uint8_t is_add) { uint8_t need_update = FALSE; - uint8_t kk; - RTKBT_DBG("%s: is_add %d, profile_index %x", __func__, - is_add, profile_index); + RTKBT_DBG("%s: is_add %d, profile_index %d", __func__, is_add, + profile_index); if (is_add) { - if (btrtl_coex.profile_refcount[profile_index] == 0) { - need_update = TRUE; - btrtl_coex.profile_bitmap |= BIT(profile_index); - - /* SCO is always busy */ - if (profile_index == profile_sco) - btrtl_coex.profile_status |= - BIT(profile_index); - } - btrtl_coex.profile_refcount[profile_index]++; if (0 == phci_conn->profile_refcount[profile_index]) { need_update = TRUE; phci_conn->profile_bitmap |= BIT(profile_index); + /* SCO is always busy */ + if (profile_index == profile_sco) + phci_conn->profile_status |= + BIT(profile_index); + + if (profile_index == profile_sink) + phci_conn->profile_bitmap |= BIT(profile_a2dpsink); + rtk_check_setup_timer(phci_conn, profile_index); } phci_conn->profile_refcount[profile_index]++; } else { - if (!btrtl_coex.profile_refcount[profile_index]) { + if (!phci_conn->profile_refcount[profile_index]) { RTKBT_WARN("profile %u refcount is already zero", profile_index); return; } - btrtl_coex.profile_refcount[profile_index]--; - RTKBT_DBG("%s: btrtl_coex.profile_refcount[%x] = %x", - __func__, profile_index, - btrtl_coex.profile_refcount[profile_index]); - if (btrtl_coex.profile_refcount[profile_index] == 0) { - need_update = TRUE; - btrtl_coex.profile_bitmap &= ~(BIT(profile_index)); - - /* if profile does not exist, status is meaningless */ - btrtl_coex.profile_status &= ~(BIT(profile_index)); - } phci_conn->profile_refcount[profile_index]--; + + if (profile_index == profile_a2dp && + phci_conn->profile_refcount[profile_index] == 1) { + phci_conn->profile_status &= ~(BIT(profile_index)); + RTKBT_DBG("%s: clear a2dp status",__func__); + } + if (0 == phci_conn->profile_refcount[profile_index]) { need_update = TRUE; phci_conn->profile_bitmap &= ~(BIT(profile_index)); phci_conn->profile_status &= ~(BIT(profile_index)); + + if (profile_index == profile_sink) { + phci_conn->profile_bitmap &= ~(BIT(profile_a2dpsink)); + phci_conn->profile_status &= ~(BIT(profile_a2dpsink)); + } rtk_check_del_timer(profile_index, phci_conn); /* clear profile_hid_interval if need */ if ((profile_hid == profile_index) @@ -770,18 +825,12 @@ static void update_profile_connection(rtk_conn_prof * phci_conn, profile_bitmap & (BIT(profile_hid_interval)))) { phci_conn->profile_bitmap &= ~(BIT(profile_hid_interval)); - phci_conn-> - profile_refcount[profile_hid_interval]--; } } } RTKBT_DBG("%s: phci_conn->profile_bitmap 0x%02x", __func__, phci_conn->profile_bitmap); - for (kk = 0; kk < 8; kk++) - RTKBT_DBG("%s: btrtl_coex.profile_refcount[%d] = %d", - __func__, kk, - btrtl_coex.profile_refcount[kk]); if (need_update) rtk_notify_profileinfo_to_fw(); @@ -844,7 +893,7 @@ static uint8_t handle_l2cap_con_req(uint16_t handle, uint16_t psm, return status; } - spin_lock(&btrtl_coex.spin_lock_profile); + mutex_lock(&btrtl_coex.profile_mutex); if (direction) //1: out prof_info = find_profile_by_handle_scid(&btrtl_coex, handle, scid); @@ -854,7 +903,7 @@ static uint8_t handle_l2cap_con_req(uint16_t handle, uint16_t psm, if (prof_info) { RTKBT_DBG("%s: this profile is already exist!", __func__); - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); return status; } @@ -863,7 +912,7 @@ static uint8_t handle_l2cap_con_req(uint16_t handle, uint16_t psm, else // 0:in status = list_allocate_add(handle, psm, profile_index, scid, 0); - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); if (!status) RTKBT_ERR("%s: list_allocate_add failed!", __func__); @@ -878,7 +927,7 @@ static uint8_t handle_l2cap_con_rsp(uint16_t handle, uint16_t dcid, rtk_prof_info *prof_info = NULL; rtk_conn_prof *phci_conn = NULL; - spin_lock(&btrtl_coex.spin_lock_profile); + mutex_lock(&btrtl_coex.profile_mutex); if (!direction) //0, in prof_info = find_profile_by_handle_scid(&btrtl_coex, handle, scid); @@ -888,7 +937,7 @@ static uint8_t handle_l2cap_con_rsp(uint16_t handle, uint16_t dcid, if (!prof_info) { //RTKBT_DBG("handle_l2cap_con_rsp: prof_info Not Find!!"); - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); return FALSE; } @@ -904,9 +953,11 @@ static uint8_t handle_l2cap_con_rsp(uint16_t handle, uint16_t dcid, update_profile_connection(phci_conn, prof_info->profile_index, TRUE); + } else if (result != 0x0001) { + delete_profile_from_hash(prof_info); } - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); return TRUE; } @@ -918,7 +969,7 @@ static uint8_t handle_l2cap_discon_req(uint16_t handle, uint16_t dcid, RTKBT_DBG("%s: handle 0x%04x, dcid 0x%04x, scid 0x%04x, dir %u", __func__, handle, dcid, scid, direction); - spin_lock(&btrtl_coex.spin_lock_profile); + mutex_lock(&btrtl_coex.profile_mutex); if (!direction) //0: in prof_info = find_profile_by_handle_dcid_scid(&btrtl_coex, handle, @@ -930,13 +981,13 @@ static uint8_t handle_l2cap_discon_req(uint16_t handle, uint16_t dcid, if (!prof_info) { //LogMsg("handle_l2cap_discon_req: prof_info Not Find!"); - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); return 0; } phci_conn = find_connection_by_handle(&btrtl_coex, handle); if (!phci_conn) { - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); return 0; } @@ -946,7 +997,7 @@ static uint8_t handle_l2cap_discon_req(uint16_t handle, uint16_t dcid, update_profile_connection(phci_conn, profile_sink, FALSE); delete_profile_from_hash(prof_info); - spin_unlock(&btrtl_coex.spin_lock_profile); + mutex_unlock(&btrtl_coex.profile_mutex); return 1; } @@ -978,80 +1029,124 @@ static void print_sbc_header(struct sbc_frame_hdr *hdr) RTKBT_DBG("subbands %u", subbands[hdr->subbands]); } -static void packets_count(uint16_t handle, uint16_t scid, uint16_t length, - uint8_t direction, u8 *user_data) +static void rtl_process_media_data(rtk_conn_prof *hci_conn, u8 *data, u16 len, + u8 out) { - rtk_prof_info *prof_info = NULL; + u8 *p = NULL; + struct sbc_frame_hdr *sbc_header; + struct rtp_header *rtph; + u8 bitpool; - rtk_conn_prof *hci_conn = - find_connection_by_handle(&btrtl_coex, handle); - if (NULL == hci_conn) + if (!hci_conn || !data || !len) { + RTKBT_ERR("%s: invalid parameters", __func__); return; + } - if (0 == hci_conn->type) { - if (!direction) //0: in - prof_info = - find_profile_by_handle_scid(&btrtl_coex, handle, - scid); - else //1: out - prof_info = - find_profile_by_handle_dcid(&btrtl_coex, handle, - scid); - - if (!prof_info) { - //RTKBT_DBG("packets_count: prof_info Not Find!"); - return; + /* avdtp media data */ + update_profile_state(hci_conn, profile_a2dp, TRUE); + if (!out) { + if (!(hci_conn->profile_bitmap & BIT(profile_sink))) { + hci_conn->profile_bitmap |= BIT(profile_sink); + update_profile_connection(hci_conn, profile_sink, TRUE); } + update_profile_state(hci_conn, profile_sink, TRUE); + } - /* avdtp media data */ - if (prof_info->profile_index == profile_a2dp && - prof_info->flags == A2DP_MEDIA) { - if (!is_profile_busy(hci_conn, profile_a2dp)) { - struct sbc_frame_hdr *sbc_header; - struct rtp_header *rtph; - u8 bitpool; + /* We assume it is SBC if the packet length + * is bigger than 100 bytes + */ + if (len > 100) { + RTKBT_INFO("%s: Length %u", __func__, len); - update_profile_state(hci_conn, profile_a2dp, TRUE); - if (!direction) { - if (!(hci_conn->profile_bitmap & BIT(profile_sink))) { - btrtl_coex.profile_bitmap |= BIT(profile_sink); - hci_conn->profile_bitmap |= BIT(profile_sink); - update_profile_connection(hci_conn, profile_sink, 1); - } - update_profile_state(hci_conn, profile_sink, TRUE); - } + p = data + sizeof(struct hci_acl_hdr) + + sizeof(struct l2cap_hdr); + rtph = (struct rtp_header *)p; - /* We assume it is SBC if the packet length - * is bigger than 100 bytes - */ - if (length > 100) { - RTKBT_INFO("Length %u", length); - rtph = (struct rtp_header *)user_data; + RTKBT_INFO("rtp: v %u, cc %u, pt %u", rtph->v, rtph->cc, + rtph->pt); + /* move forward */ + p += sizeof(struct rtp_header) + rtph->cc * 4 + 1; - RTKBT_DBG("rtp: v %u, cc %u, pt %u", - rtph->v, rtph->cc, rtph->pt); - /* move forward */ - user_data += sizeof(struct rtp_header) + - rtph->cc * 4 + 1; + /* point to the sbc frame header */ + sbc_header = (struct sbc_frame_hdr *)p; + bitpool = sbc_header->bitpool; - /* point to the sbc frame header */ - sbc_header = (struct sbc_frame_hdr *)user_data; - bitpool = sbc_header->bitpool; + print_sbc_header(sbc_header); - print_sbc_header(sbc_header); + RTKBT_INFO("%s: bitpool %u", __func__, bitpool); - RTKBT_DBG("bitpool %u", bitpool); + rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_BITPOOL, 1, &bitpool); + } + hci_conn->a2dp_packet_count++; +} - rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_BITPOOL, - 1, &bitpool); - } +static void packets_count(u16 handle, u16 scid, u8 out, u8 *data, u16 len) +{ + rtk_prof_info *prof = NULL; + rtk_conn_prof *hci_conn = NULL; + + set_bit(RTL_COEX_PKT_COUNTING, &btrtl_coex.flags); + if (test_bit(RTL_COEX_CONN_REMOVING, &btrtl_coex.flags)) { + RTKBT_INFO("%s: conn/prof is being removed", __func__); + goto done; + } + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (!hci_conn) + goto done; + if (hci_conn->type != HCI_CONN_TYPE_BREDR_ACL) + goto done; + + if (!out) + prof = find_profile_by_handle_scid(&btrtl_coex, handle, scid); + else + prof = find_profile_by_handle_dcid(&btrtl_coex, handle, scid); + if (!prof) + goto done; + + /* avdtp media data */ + if (prof->profile_index == profile_a2dp && + prof->flags == A2DP_MEDIA) { + if (!(hci_conn->profile_status & BIT(profile_a2dp))) { +#ifdef DIRECT_A2DP_MEDIA_PACKET_PROCESSING + rtl_process_media_data(hci_conn, data, len, out); +#else + u16 n; + unsigned long flags; + struct rtl_l2_buff *l2; + + l2 = rtl_l2_node_get(&btrtl_coex); + if (!l2) { + RTKBT_ERR("%s: No free l2 node", __func__); + goto done; } + + n = min_t(uint, len, L2_MAX_SUBSEC_LEN); + memcpy(l2->data, data, n); + + if (out) + l2->type = HCI_PT_L2DATA_TX; + else + l2->type = HCI_PT_L2DATA_RX; + spin_lock_irqsave(&btrtl_coex.buff_lock, flags); + list_add_tail(&l2->list, &btrtl_coex.hci_pkt_list); + spin_unlock_irqrestore(&btrtl_coex.buff_lock, flags); + + queue_delayed_work(btrtl_coex.fw_wq, + &btrtl_coex.fw_work, 0); +#endif + } else { hci_conn->a2dp_packet_count++; } - - if (prof_info->profile_index == profile_pan) - hci_conn->pan_packet_count++; } + if (prof->profile_index == profile_pan) + hci_conn->pan_packet_count++; + + if (prof->profile_index == profile_pan) + hci_conn->pan_packet_count++; +done: + clear_bit(RTL_COEX_PKT_COUNTING, &btrtl_coex.flags); + return; } static void count_a2dp_packet_timeout(struct work_struct *work) @@ -1064,14 +1159,21 @@ static void count_a2dp_packet_timeout(struct work_struct *work) if (hci_conn->a2dp_packet_count == 0) { if (is_profile_busy(hci_conn, profile_a2dp)) { RTKBT_DBG("%s: a2dp busy->idle!", __func__); + /* + * We should prevent any conn from being deleted when + * update_profile_state() traverses the conn list. + */ + mutex_lock(&btrtl_coex.conn_mutex); update_profile_state(hci_conn, profile_a2dp, FALSE); - if (btrtl_coex.profile_bitmap & BIT(profile_sink)) + if (hci_conn->profile_bitmap & BIT(profile_sink)) update_profile_state(hci_conn, profile_sink, FALSE); + mutex_unlock(&btrtl_coex.conn_mutex); } } hci_conn->a2dp_packet_count = 0; - queue_delayed_work(btrtl_coex.timer_wq, &hci_conn->a2dp_count_work, msecs_to_jiffies(1000)); + queue_delayed_work(btrtl_coex.fw_wq, &hci_conn->a2dp_count_work, + msecs_to_jiffies(1000)); } static void count_pan_packet_timeout(struct work_struct *work) @@ -1084,16 +1186,21 @@ static void count_pan_packet_timeout(struct work_struct *work) if (hci_conn->pan_packet_count < PAN_PACKET_COUNT) { if (is_profile_busy(hci_conn, profile_pan)) { RTKBT_DBG("%s: pan busy->idle!", __func__); + mutex_lock(&btrtl_coex.conn_mutex); update_profile_state(hci_conn, profile_pan, FALSE); + mutex_unlock(&btrtl_coex.conn_mutex); } } else { if (!is_profile_busy(hci_conn, profile_pan)) { RTKBT_DBG("timeout_handler: pan idle->busy!"); + mutex_lock(&btrtl_coex.conn_mutex); update_profile_state(hci_conn, profile_pan, TRUE); + mutex_unlock(&btrtl_coex.conn_mutex); } } hci_conn->pan_packet_count = 0; - queue_delayed_work(btrtl_coex.timer_wq, &hci_conn->pan_count_work, msecs_to_jiffies(1000)); + queue_delayed_work(btrtl_coex.fw_wq, &hci_conn->pan_count_work, + msecs_to_jiffies(1000)); } static void count_hogp_packet_timeout(struct work_struct *work) @@ -1106,7 +1213,9 @@ static void count_hogp_packet_timeout(struct work_struct *work) if (hci_conn->hogp_packet_count == 0) { if (is_profile_busy(hci_conn, profile_hogp)) { RTKBT_DBG("%s: hogp busy->idle!", __func__); + mutex_lock(&btrtl_coex.conn_mutex); update_profile_state(hci_conn, profile_hogp, FALSE); + mutex_unlock(&btrtl_coex.conn_mutex); } } hci_conn->hogp_packet_count = 0; @@ -1117,11 +1226,14 @@ static void count_hogp_packet_timeout(struct work_struct *work) if (hci_conn->voice_packet_count == 0) { if (is_profile_busy(hci_conn, profile_voice)) { RTKBT_DBG("%s: voice busy->idle!", __func__); + mutex_lock(&btrtl_coex.conn_mutex); update_profile_state(hci_conn, profile_voice, FALSE); + mutex_unlock(&btrtl_coex.conn_mutex); } } hci_conn->voice_packet_count = 0; - queue_delayed_work(btrtl_coex.timer_wq, &hci_conn->hogp_count_work, msecs_to_jiffies(1000)); + queue_delayed_work(btrtl_coex.fw_wq, &hci_conn->hogp_count_work, + msecs_to_jiffies(1000)); } #ifdef RTB_SOFTWARE_MAILBOX @@ -1507,6 +1619,112 @@ static void rtk_notify_regester_to_wifi(uint8_t * reg_value) #endif +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) +static void rtk_btcoex_handle_cmd_le_create_cis(u8 *buffer, int count) +{ + struct hci_cp_le_create_cis *cp; + struct hci_cis *cis; + rtk_conn_prof *hci_conn = NULL; + u16 cis_handle; + u8 i; + + cp = (void *)(buffer + sizeof(struct hci_command_hdr)); + cis = cp->cis; + for (i = 0; i < cp->num_cis; i++) { + cis_handle = get_unaligned_le16(&cis[i].cis_handle); + hci_conn = find_connection_by_handle(&btrtl_coex, cis_handle); + if (hci_conn) { + RTKBT_ERR("%s: conn %04x has existed", __func__, + cis_handle); + continue; + } + hci_conn = allocate_connection_by_handle(cis_handle); + if (!hci_conn) { + RTKBT_ERR("cis (0x%04x) allocation failed", cis_handle); + continue; + } + + hci_conn->profile_bitmap = 0; + hci_conn->profile_status = 0; + memset(hci_conn->profile_refcount, 0, profile_max); + hci_conn->type = HCI_CONN_TYPE_CIS; + + add_connection_to_hash(&btrtl_coex, hci_conn); + } +} + +static void rtk_btcoex_handle_cmd_le_setup_iso_path(u8 *buffer, int count) +{ + struct hci_cp_le_setup_iso_path *cp; + rtk_conn_prof *hci_conn = NULL; + u16 handle; + + cp = (void *)(buffer + sizeof(struct hci_command_hdr)); + handle = get_unaligned_le16(&cp->handle); + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (!hci_conn) + return; + + if (!cp->direction) + hci_conn->direction |= HCI_DATA_PATH_INPUT; + else + hci_conn->direction |= HCI_DATA_PATH_OUTPUT; + + RTKBT_INFO("data path direction 0x%02x", hci_conn->direction); +} + +static void rtk_btcoex_handle_cmd_le_remove_iso_path(u8 *buffer, int count) +{ + rtk_conn_prof *hci_conn = NULL; + struct hci_cp_le_remove_iso_path *cp; + u16 handle; + + cp = (void *)(buffer + sizeof(struct hci_command_hdr)); + handle = get_unaligned_le16(&cp->handle); + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (!hci_conn) + return; + + if (!cp->direction) + hci_conn->remove_path = HCI_DATA_PATH_INPUT; + else + hci_conn->remove_path = HCI_DATA_PATH_OUTPUT; + + RTKBT_INFO("Remove iso path handle %04x, direction %02x", handle, + cp->direction); +} + +static void rtk_btcoex_cmd_enqueue(u8 *buffer, int count) +{ + /* We use ev structure for cmd */ + struct rtl_hci_ev *cmd; + unsigned long flags; + + cmd = rtl_ev_node_get(&btrtl_coex); + if (!cmd) { + RTKBT_ERR("%s: No mem for saving cmd", __func__); + return; + } + + if (count > MAX_LEN_OF_HCI_EV) { + memcpy(cmd->data, buffer, MAX_LEN_OF_HCI_EV); + cmd->len = MAX_LEN_OF_HCI_EV; + } else { + memcpy(cmd->data, buffer, count); + cmd->len = count; + } + cmd->type = HCI_PT_CMD; + spin_lock_irqsave(&btrtl_coex.buff_lock, flags); + list_add_tail(&cmd->list, &btrtl_coex.hci_pkt_list); + spin_unlock_irqrestore(&btrtl_coex.buff_lock, flags); + + queue_delayed_work(btrtl_coex.fw_wq, &btrtl_coex.fw_work, 0); +} + +#endif + void rtk_btcoex_parse_cmd(uint8_t *buffer, int count) { u16 opcode = (buffer[0]) + (buffer[1] << 8); @@ -1558,9 +1776,16 @@ void rtk_btcoex_parse_cmd(uint8_t *buffer, int count) } break; case HCI_OP_DISCONNECT: - RTKBT_INFO("HCI Disconnect, handle %04x, reason 0x%02x", + RTKBT_INFO("HCI OP Disconnect, handle %04x, reason 0x%02x", ((u16)buffer[4] << 8 | buffer[3]), buffer[5]); break; +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + case HCI_OP_LE_CREATE_CIS: + case HCI_OP_LE_SETUP_ISO_PATH: + case HCI_OP_LE_REMOVE_ISO_PATH: + rtk_btcoex_cmd_enqueue(buffer, count); + break; +#endif default: break; } @@ -1747,6 +1972,99 @@ static void rtk_parse_vendor_mailbox_cmd_evt(u8 * p, u8 total_len) } #endif /* RTB_SOFTWARE_MAILBOX */ +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) +static void big_conn_remove(u16 big_handle) +{ + struct list_head *head = &btrtl_coex.conn_hash; + struct list_head *iter = NULL, *temp = NULL; + rtk_conn_prof *hci_conn = NULL; + + list_for_each_safe(iter, temp, head) { + hci_conn = list_entry(iter, rtk_conn_prof, list); + if ((big_handle & 0xFFF) != hci_conn->big_handle) + continue; + if (hci_conn->profile_bitmap & BIT(profile_lea_src)) + update_profile_connection(hci_conn, profile_lea_src, + FALSE); + else if (hci_conn->profile_bitmap & BIT(profile_lea_snk)) + update_profile_connection(hci_conn, profile_lea_snk, + FALSE); + + delete_connection_from_hash(hci_conn); + } +} + +static void rtk_handle_cc_le_remove_iso_data_path(u8 *p) +{ + rtk_conn_prof *hci_conn = NULL; + u16 handle = 0; + u8 status; + + status = *p++; + if (status) { + RTKBT_ERR("%s: status %02x", __func__, status); + return; + } + + handle = get_unaligned_le16(p); + RTKBT_DBG("BTCOEX conn handle 0x%04x", handle); + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (!hci_conn) + return; + + if (hci_conn->remove_path == HCI_DATA_PATH_INPUT && + is_profile_busy(hci_conn, profile_lea_src)) + update_profile_state(hci_conn, profile_lea_src, FALSE); + else if (hci_conn->remove_path == HCI_DATA_PATH_OUTPUT && + is_profile_busy(hci_conn, profile_lea_snk)) + update_profile_state(hci_conn, profile_lea_snk, FALSE); +} + +static void rtk_handle_cc_le_setup_iso_path(u8 *p) +{ + rtk_conn_prof *hci_conn = NULL; + u16 handle; + u8 status = *p++; + + if (status) { + RTKBT_ERR("setup iso path evt err, status 0x%02x", status); + return; + } + + handle = get_unaligned_le16(p); + + RTKBT_DBG("BTCOEX conn handle 0x%04x", handle); + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (!hci_conn) + return; + + if ((hci_conn->direction & HCI_DATA_PATH_INPUT) && + !is_profile_busy(hci_conn, profile_lea_src)) + update_profile_state(hci_conn, profile_lea_src, TRUE); + if ((hci_conn->direction & HCI_DATA_PATH_OUTPUT) && + !is_profile_busy(hci_conn, profile_lea_snk)) + update_profile_state(hci_conn, profile_lea_snk, TRUE); +} + +static void rtk_handle_cc_le_big_term_sync(u8 *data) +{ + u8 status; + u8 big_handle; + + status = *data++; + big_handle = *data++; + + if (!status) { + u16 handle = big_handle; + + handle += HCI_CONN_HANDLE_UNSET_START; + big_conn_remove(handle); + } +} +#endif + static void rtk_handle_cmd_complete_evt(u8 total_len, u8 * p) { u16 opcode; @@ -1781,7 +2099,6 @@ static void rtk_handle_cmd_complete_evt(u8 total_len, u8 * p) btrtl_coex.lmp_subversion); } } - #ifdef RTB_SOFTWARE_MAILBOX if (opcode == HCI_VENDOR_MAILBOX_CMD) { rtk_parse_vendor_mailbox_cmd_evt(p, total_len); @@ -1789,13 +2106,24 @@ static void rtk_handle_cmd_complete_evt(u8 total_len, u8 * p) #endif if (opcode == HCI_VENDOR_SET_PROFILE_REPORT_COMMAND) { //0x01-unknown hci command - if((*p++) == 0x01) { - RTKBT_DBG("unknown hci command"); + if ((*p++) == 0x01) { + //RTKBT_DBG("unknown hci command"); return; } else { profileinfo_cmd = 1; } } + +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + if (opcode == HCI_OP_LE_SETUP_ISO_PATH) + rtk_handle_cc_le_setup_iso_path(p); + + if (opcode == HCI_OP_LE_REMOVE_ISO_PATH) + rtk_handle_cc_le_remove_iso_data_path(p); + + if (opcode == HCI_OP_LE_BIG_TERM_SYNC) + rtk_handle_cc_le_big_term_sync(p); +#endif } static void rtk_handle_cmd_status_evt(u8 * p) @@ -1832,6 +2160,17 @@ static void rtk_handle_cmd_status_evt(u8 * p) #endif } } +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + if (opcode == HCI_OP_LE_CREATE_CIS) { + /* TODO: Should we remove the cis conn? + * Will an HCI_LE_CIS_Established event be generated for each + * CIS no matter the following status is 0 or non-zero? + */ + if (status) { + RTKBT_ERR("cs of HCI_OP_LE_CREATE_CIS"); + } + } +#endif } static void rtk_handle_connection_complete_evt(u8 * p) @@ -1863,17 +2202,19 @@ static void rtk_handle_connection_complete_evt(u8 * p) if (hci_conn == NULL) { hci_conn = allocate_connection_by_handle(handle); if (hci_conn) { - add_connection_to_hash(&btrtl_coex, - hci_conn); hci_conn->profile_bitmap = 0; + hci_conn->profile_status = 0; memset(hci_conn->profile_refcount, 0, 8); - if ((0 == link_type) || (2 == link_type)) { //sco or esco - hci_conn->type = 1; + hci_conn->type = HCI_CONN_TYPE_BREDR_ACL; + + add_connection_to_hash(&btrtl_coex, hci_conn); + if (0 == link_type || 2 == link_type) { + /* sco or esco */ + hci_conn->type = HCI_CONN_TYPE_SCO; update_profile_connection(hci_conn, profile_sco, TRUE); - } else - hci_conn->type = 0; + } } else { RTKBT_ERR("hci connection allocate fail"); } @@ -1882,12 +2223,14 @@ static void rtk_handle_connection_complete_evt(u8 * p) handle); hci_conn->profile_bitmap = 0; memset(hci_conn->profile_refcount, 0, 8); - if ((0 == link_type) || (2 == link_type)) { //sco or esco - hci_conn->type = 1; + if (0 == link_type || 2 == link_type) { + /* sco or esco */ + hci_conn->type = HCI_CONN_TYPE_SCO; update_profile_connection(hci_conn, profile_sco, TRUE); - } else - hci_conn->type = 0; + } else { + hci_conn->type = HCI_CONN_TYPE_BREDR_ACL; + } } } else if (btrtl_coex.ispaging) { btrtl_coex.ispaging = 0; @@ -1934,11 +2277,13 @@ static void rtk_handle_le_connection_complete_evt(u8 enhanced, u8 * p) if (hci_conn == NULL) { hci_conn = allocate_connection_by_handle(handle); if (hci_conn) { - add_connection_to_hash(&btrtl_coex, - hci_conn); hci_conn->profile_bitmap = 0; + hci_conn->profile_status = 0; memset(hci_conn->profile_refcount, 0, 8); - hci_conn->type = 2; + hci_conn->type = HCI_CONN_TYPE_LE_ACL; + + add_connection_to_hash(&btrtl_coex, hci_conn); + update_profile_connection(hci_conn, profile_hid, TRUE); //for coex, le is the same as hid update_hid_active_state(handle, interval); } else { @@ -1949,7 +2294,7 @@ static void rtk_handle_le_connection_complete_evt(u8 enhanced, u8 * p) handle); hci_conn->profile_bitmap = 0; memset(hci_conn->profile_refcount, 0, 8); - hci_conn->type = 2; + hci_conn->type = HCI_CONN_TYPE_LE_ACL; update_profile_connection(hci_conn, profile_hid, TRUE); update_hid_active_state(handle, interval); } @@ -1978,9 +2323,148 @@ static void rtk_handle_le_connection_update_complete_evt(u8 * p) update_hid_active_state(handle, interval); } +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) +static void rtk_handle_le_cis_established_evt(void * p) +{ + struct hci_evt_le_cis_established *ev = p; + u8 status; + u16 c_to_p, p_to_c; + rtk_conn_prof *hci_conn; + u16 handle = __le16_to_cpu(ev->handle); + u8 central = 1;; + + status = ev->status; + c_to_p = le16_to_cpu(ev->c_mtu); + p_to_c = le16_to_cpu(ev->p_mtu); + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (status) { + RTKBT_ERR("cis established evt err, status 0x%02x", status); + if (hci_conn) + delete_connection_from_hash(hci_conn); + goto done; + } + + if (!hci_conn) { + /* We're peripheral */ + central = 0; + hci_conn = allocate_connection_by_handle(handle); + if (!hci_conn) { + RTKBT_ERR("cis conn (0x%04x) allocation failed", handle); + goto done; + } + hci_conn->profile_bitmap = 0; + hci_conn->profile_status = 0; + memset(hci_conn->profile_refcount, 0, profile_max); + hci_conn->type = HCI_CONN_TYPE_CIS; + add_connection_to_hash(&btrtl_coex, hci_conn); + } + + RTKBT_DBG("central %u, c_to_p 0x%04x, p_to_c 0x%04x", central, c_to_p, + p_to_c); + + if ((central && c_to_p) || (!central && p_to_c)) + update_profile_connection(hci_conn, profile_lea_src, TRUE); + + if ((central && p_to_c) || (!central && c_to_p)) + update_profile_connection(hci_conn, profile_lea_snk, TRUE); + +done: + return; +} + +static void big_conn_add(u16 big_handle, u16 handle, u8 profile_index) +{ + rtk_conn_prof *hci_conn = NULL; + + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (hci_conn) { + RTKBT_DBG("bis handle 0x%04x has already existed!", handle); + return; + } + RTKBT_DBG("bis handle 0x%04x", handle); + hci_conn = allocate_connection_by_handle(handle); + if (hci_conn) { + hci_conn->profile_bitmap = 0; + hci_conn->profile_status = 0; + hci_conn->big_handle = big_handle; + memset(hci_conn->profile_refcount, 0, profile_max); + hci_conn->type = HCI_CONN_TYPE_BIS; + + add_connection_to_hash(&btrtl_coex, hci_conn); + + update_profile_connection(hci_conn, profile_index, TRUE); + } else { + RTKBT_DBG("bis conn allocation failed"); + } +} + +static void rtk_handle_le_create_big_complete_evt(void *p) +{ + struct hci_evt_le_create_big_complete *ev = p; + u16 big_handle; + u8 status; + + status = ev->status; + if (status) { + RTKBT_ERR("big complete evt err, status 0x%02x", status); + return; + } + big_handle = ev->handle; + big_handle += HCI_CONN_HANDLE_UNSET_START; + for (u8 i = 0; i < ev->num_bis; i++) { + u16 handle = le16_to_cpu(ev->bis_handle[i]); + big_conn_add(big_handle, handle, profile_lea_src); + } +} + +static void rtk_handle_le_terminate_big_complete_evt(u8 * p) +{ + u16 big_handle; + + big_handle = *p++; + big_handle += HCI_CONN_HANDLE_UNSET_START; + + big_conn_remove(big_handle); +} + +static void rtk_handle_le_big_sync_established_evt(void * p) +{ + struct hci_evt_le_big_sync_estabilished *ev = p; + u8 status; + u16 big_handle; + u16 bis_handle; + + status = ev->status; + big_handle = ev->handle; + big_handle += HCI_CONN_HANDLE_UNSET_START; + if (status) { + RTKBT_ERR("big complete evt err, status 0x%02x", status); + return; + } + + for (u8 i = 0; i < ev->num_bis; i++) { + bis_handle = le16_to_cpu(ev->bis[i]); + big_conn_add(big_handle, bis_handle, profile_lea_snk); + } +} + +static void rtk_handle_le_big_sync_lost_evt(u8 * p) +{ + u16 big_handle; + + big_handle = *p++; + big_handle += HCI_CONN_HANDLE_UNSET_START; + + big_conn_remove(big_handle); +} + +#endif + static void rtk_handle_le_meta_evt(u8 * p) { u8 sub_event = *p++; + switch (sub_event) { case HCI_EV_LE_CONN_COMPLETE: rtk_handle_le_connection_complete_evt(0, p); @@ -1988,11 +2472,26 @@ static void rtk_handle_le_meta_evt(u8 * p) case HCI_EV_LE_ENHANCED_CONN_COMPLETE: rtk_handle_le_connection_complete_evt(1, p); break; - case HCI_EV_LE_CONN_UPDATE_COMPLETE: rtk_handle_le_connection_update_complete_evt(p); break; - +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + case HCI_EV_LE_CIS_EST: + rtk_handle_le_cis_established_evt(p); + break; + case HCI_EV_LE_CREATE_BIG_CPL: + rtk_handle_le_create_big_complete_evt(p); + break; + case HCI_EV_LE_TERM_BIG_CPL: + rtk_handle_le_terminate_big_complete_evt(p); + break; + case HCI_EV_LE_BIG_SYNC_EST: + rtk_handle_le_big_sync_established_evt(p); + break; + case HCI_EV_LE_BIG_SYNC_LOST: + rtk_handle_le_big_sync_lost_evt(p); + break; +#endif default: break; } @@ -2002,23 +2501,13 @@ static u8 disconn_profile(struct rtl_hci_conn *conn, u8 pfe_index) { u8 need_update = 0; - if (!btrtl_coex.profile_refcount[pfe_index]) { + if (!conn->profile_refcount[pfe_index]) { RTKBT_WARN("profile %u ref is 0", pfe_index); return 0; } - btrtl_coex.profile_refcount[pfe_index]--; RTKBT_INFO("%s: profile_ref[%u] %u", __func__, pfe_index, - btrtl_coex.profile_refcount[pfe_index]); - - if (!btrtl_coex.profile_refcount[pfe_index]) { - need_update = 1; - btrtl_coex.profile_bitmap &= ~(BIT(pfe_index)); - - /* if profile does not exist, status is meaningless */ - btrtl_coex.profile_status &= ~(BIT(pfe_index)); - rtk_check_del_timer(pfe_index, conn); - } + conn->profile_refcount[pfe_index]); if (conn->profile_refcount[pfe_index]) conn->profile_refcount[pfe_index]--; @@ -2029,13 +2518,9 @@ static u8 disconn_profile(struct rtl_hci_conn *conn, u8 pfe_index) need_update = 1; conn->profile_bitmap &= ~(BIT(pfe_index)); - /* clear profile_hid_interval if need */ - if ((profile_hid == pfe_index) && - (conn->profile_bitmap & (BIT(profile_hid_interval)))) { - conn->profile_bitmap &= ~(BIT(profile_hid_interval)); - if (btrtl_coex.profile_refcount[profile_hid_interval]) - btrtl_coex.profile_refcount[profile_hid_interval]--; - } + /* if profile does not exist, status is meaningless */ + conn->profile_status &= ~(BIT(pfe_index)); + rtk_check_del_timer(pfe_index, conn); } return need_update; @@ -2048,7 +2533,7 @@ static void disconn_acl(u16 handle, struct rtl_hci_conn *conn) struct list_head *iter = NULL, *temp = NULL; u8 need_update = 0; - spin_lock(&coex->spin_lock_profile); + mutex_lock(&coex->profile_mutex); list_for_each_safe(iter, temp, &coex->profile_list) { prof_info = list_entry(iter, rtk_prof_info, list); @@ -2069,7 +2554,7 @@ static void disconn_acl(u16 handle, struct rtl_hci_conn *conn) } if (need_update) rtk_notify_profileinfo_to_fw(); - spin_unlock(&coex->spin_lock_profile); + mutex_unlock(&coex->profile_mutex); } static void rtk_handle_disconnect_complete_evt(u8 * p) @@ -2093,36 +2578,46 @@ static void rtk_handle_disconnect_complete_evt(u8 * p) STREAM_TO_UINT16(handle, p); reason = *p; - RTKBT_INFO("disconn cmpl evt: status 0x%02x, handle %04x, reason 0x%02x", + RTKBT_INFO("disconn cmpl evt: status %02x, handle %04x, reason %02x", status, handle, reason); if (status == 0) { RTKBT_DBG("process disconn complete event."); hci_conn = find_connection_by_handle(&btrtl_coex, handle); - if (hci_conn) { - switch (hci_conn->type) { - case 0: - /* FIXME: If this is interrupted by l2cap rx, - * there may be deadlock on spin_lock_profile */ - disconn_acl(handle, hci_conn); - break; - - case 1: - update_profile_connection(hci_conn, profile_sco, - FALSE); - break; - - case 2: - update_profile_connection(hci_conn, profile_hid, - FALSE); - break; - - default: - break; - } - delete_connection_from_hash(hci_conn); - } else + if (!hci_conn) { RTKBT_ERR("hci conn handle 0x%04x not found", handle); + return; + } + switch (hci_conn->type) { + case HCI_CONN_TYPE_BREDR_ACL: + /* FIXME: If this is interrupted by l2cap rx, + * there may be deadlock on profile_mutex */ + disconn_acl(handle, hci_conn); + break; + + case HCI_CONN_TYPE_SCO: + update_profile_connection(hci_conn, profile_sco, FALSE); + break; + + case HCI_CONN_TYPE_LE_ACL: + update_profile_connection(hci_conn, profile_hid, FALSE); + break; + + case HCI_CONN_TYPE_CIS: + if (hci_conn->direction & HCI_DATA_PATH_INPUT) + update_profile_connection(hci_conn, + profile_lea_src, + FALSE); + if (hci_conn->direction & HCI_DATA_PATH_OUTPUT) + update_profile_connection(hci_conn, + profile_lea_snk, + FALSE); + break; + + default: + break; + } + delete_connection_from_hash(hci_conn); } } @@ -2318,7 +2813,7 @@ static void rtl_l2_data_process(u8 *pp, u16 len, int dir) l2->out = dir; rtl_l2_node_to_used(&btrtl_coex, l2); queue_delayed_work(btrtl_coex.fw_wq, - &btrtl_coex.l2_work, 0); + &btrtl_coex.fw_work, 0); } else RTKBT_ERR("%s: failed to get l2 node", __func__); @@ -2329,61 +2824,146 @@ static void rtl_l2_data_process(u8 *pp, u16 len, int dir) break; } } else { - if ((flag != 0x01) && (is_profile_connected(profile_a2dp) || - is_profile_connected(profile_pan))) - /* Do not count the continuous packets */ - packets_count(handle, channel_id, pdu_len, dir, pp); + //RTKBT_DBG("%s: handle:%x, flag:%x, pan:%d, a2dp:%d", __func__, handle, flag, + // is_profile_connected(profile_a2dp), is_profile_connected(profile_pan)); + if (flag != 0x01) + packets_count(handle, channel_id, dir, hd, len); } return; } - -static void rtl_l2_work(struct work_struct *work) +static void rtl_process_cmd(struct rtl_coex_struct *coex, u8 *buffer, int count) { - struct rtl_coex_struct *coex; - struct rtl_l2_buff *l2; - unsigned long flags; + struct hci_command_hdr *hdr; + u16 opcode; - coex = container_of(work, struct rtl_coex_struct, l2_work.work); - - spin_lock_irqsave(&coex->buff_lock, flags); - while (!list_empty(&coex->l2_used_list)) { - l2 = list_entry(coex->l2_used_list.next, struct rtl_l2_buff, - list); - list_del(&l2->list); - - spin_unlock_irqrestore(&coex->buff_lock, flags); - - rtl_process_l2_sig(l2); - - spin_lock_irqsave(&coex->buff_lock, flags); - - list_add_tail(&l2->list, &coex->l2_free_list); + hdr = (void *)buffer; + opcode = get_unaligned_le16(&hdr->opcode); + switch (opcode) { +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + case HCI_OP_LE_CREATE_CIS: + rtk_btcoex_handle_cmd_le_create_cis(buffer, count); + break; + case HCI_OP_LE_SETUP_ISO_PATH: + rtk_btcoex_handle_cmd_le_setup_iso_path(buffer, count); + break; + case HCI_OP_LE_REMOVE_ISO_PATH: + rtk_btcoex_handle_cmd_le_remove_iso_path(buffer, count); + break; +#endif + default: + break; } - spin_unlock_irqrestore(&coex->buff_lock, flags); +} +static void rtl_process_l2_data(struct rtl_l2_buff *l2) +{ + rtk_prof_info *prof = NULL; + rtk_conn_prof *hci_conn = NULL; + struct hci_acl_hdr *hdr; + u16 handle; + struct l2cap_hdr *l2hdr; + u16 scid; + u8 out; + u16 dlen; + + hdr = (void *)l2->data; + handle = get_unaligned_le16(&hdr->handle); + handle &= 0x0fff; + dlen = get_unaligned_le16(&hdr->dlen); + hci_conn = find_connection_by_handle(&btrtl_coex, handle); + if (!hci_conn) + goto done; + if (hci_conn->type != HCI_CONN_TYPE_BREDR_ACL) + goto done; + + if (l2->type == HCI_PT_L2DATA_TX) + out = 1; + else + out = 0; + + l2hdr = (void *)(l2->data + sizeof(*hdr)); + scid = get_unaligned_le16(&l2hdr->cid); + if (!out) + prof = find_profile_by_handle_scid(&btrtl_coex, handle, scid); + else + prof = find_profile_by_handle_dcid(&btrtl_coex, handle, scid); + if (!prof) + goto done; + + if (prof->profile_index != profile_a2dp || prof->flags != A2DP_MEDIA || + hci_conn->profile_status & BIT(profile_a2dp)) + goto done; + + rtl_process_media_data(hci_conn, l2->data, sizeof(*hdr) + dlen, out); + +done: return; } -static void rtl_ev_work(struct work_struct *work) +static void rtl_hci_work_func(struct work_struct *work) { struct rtl_coex_struct *coex; struct rtl_hci_ev *ev; + struct rtl_l2_buff *l2; unsigned long flags; + struct rtl_hci_hdr *hdr; + u8 pkt_type; coex = container_of(work, struct rtl_coex_struct, fw_work.work); spin_lock_irqsave(&coex->buff_lock, flags); - while (!list_empty(&coex->ev_used_list)) { - ev = list_entry(coex->ev_used_list.next, struct rtl_hci_ev, - list); - list_del(&ev->list); + while (!list_empty(&coex->hci_pkt_list)) { + hdr = list_entry(coex->hci_pkt_list.next, struct rtl_hci_hdr, + list); + list_del(&hdr->list); + pkt_type = hdr->type; + spin_unlock_irqrestore(&coex->buff_lock, flags); - rtk_parse_event_data(coex, ev->data, ev->len); + switch (pkt_type) { + case HCI_PT_EVT: + ev = (void *)hdr; + rtk_parse_event_data(coex, ev->data, ev->len); + break; + case HCI_PT_CMD: + ev = (void *)hdr; + rtl_process_cmd(coex, ev->data, ev->len); + break; + case HCI_PT_L2SIG_RX: + case HCI_PT_L2SIG_TX: + l2 = (void *)hdr; + rtl_process_l2_sig(l2); + break; + case HCI_PT_L2DATA_RX: + case HCI_PT_L2DATA_TX: + l2 = (void *)hdr; + rtl_process_l2_data(l2); + break; + default: + break; + } spin_lock_irqsave(&coex->buff_lock, flags); - list_add_tail(&ev->list, &coex->ev_free_list); + + hdr->type = 0; + switch (pkt_type) { + case HCI_PT_EVT: + case HCI_PT_CMD: + list_add_tail(&hdr->list, &coex->ev_free_list); + break; + case HCI_PT_L2SIG_RX: + case HCI_PT_L2SIG_TX: + case HCI_PT_L2DATA_RX: + case HCI_PT_L2DATA_TX: + list_add_tail(&hdr->list, &coex->l2_free_list); + break; + default: + RTKBT_ERR("%s: invalid pkt type %u", __func__, + hdr->type); + list_add_tail(&hdr->list, &coex->ev_free_list); + break; + } } spin_unlock_irqrestore(&coex->buff_lock, flags); } @@ -2400,6 +2980,10 @@ static inline int cmd_cmplt_filter_out(u8 *buf) case HCI_VENDOR_MAILBOX_CMD: #endif case HCI_VENDOR_SET_PROFILE_REPORT_COMMAND: +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + case HCI_OP_LE_SETUP_ISO_PATH: + case HCI_OP_LE_BIG_TERM_SYNC: +#endif return 0; default: return 1; @@ -2414,6 +2998,9 @@ static inline int cmd_status_filter_out(u8 *buf) switch (opcode) { case HCI_OP_INQUIRY: case HCI_OP_CREATE_CONN: +#if HCI_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + case HCI_OP_LE_CREATE_CIS: +#endif return 0; default: return 1; @@ -2442,6 +3029,11 @@ static int ev_filter_out(u8 *buf) case HCI_EV_LE_CONN_COMPLETE: case HCI_EV_LE_ENHANCED_CONN_COMPLETE: case HCI_EV_LE_CONN_UPDATE_COMPLETE: + case HCI_EV_LE_CIS_EST: + case HCI_EV_LE_CREATE_BIG_CPL: + case HCI_EV_LE_TERM_BIG_CPL: + case HCI_EV_LE_BIG_SYNC_EST: + case HCI_EV_LE_BIG_SYNC_LOST: return 0; } return 1; @@ -2457,7 +3049,6 @@ static int ev_filter_out(u8 *buf) static void rtk_btcoex_evt_enqueue(__u8 *s, __u16 count) { struct rtl_hci_ev *ev; - if (ev_filter_out(s)) return; @@ -2927,8 +3518,15 @@ 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) { + unsigned long flags; + if (test_and_set_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { RTKBT_WARN("RTL COEX is already running."); return; @@ -2939,7 +3537,8 @@ void rtk_btcoex_open(struct hci_dev *hdev) /* Just for test */ //struct rtl_btinfo_ctl ctl; - INIT_DELAYED_WORK(&btrtl_coex.fw_work, (void *)rtl_ev_work); + INIT_DELAYED_WORK(&btrtl_coex.fw_work, (void *)rtl_hci_work_func); + 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); @@ -2950,7 +3549,6 @@ void rtk_btcoex_open(struct hci_dev *hdev) (void *)udpsocket_recv_data); #endif #endif /* RTB_SOFTWARE_MAILBOX */ - INIT_DELAYED_WORK(&btrtl_coex.l2_work, (void *)rtl_l2_work); btrtl_coex.hdev = hdev; #ifdef RTB_SOFTWARE_MAILBOX @@ -2960,10 +3558,12 @@ void rtk_btcoex_open(struct hci_dev *hdev) init_profile_hash(&btrtl_coex); init_connection_hash(&btrtl_coex); + spin_lock_irqsave(&btrtl_coex.rxlock, flags); btrtl_coex.pkt_type = 0; btrtl_coex.expect = 0; btrtl_coex.elen = 0; btrtl_coex.tbuff = NULL; + spin_unlock_irqrestore(&btrtl_coex.rxlock, flags); #ifdef RTB_SOFTWARE_MAILBOX #ifndef RTK_COEX_OVER_SYMBOL @@ -2971,7 +3571,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; @@ -2981,7 +3582,6 @@ void rtk_btcoex_open(struct hci_dev *hdev) void rtk_btcoex_close(void) { - int kk = 0; if (!test_and_clear_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { RTKBT_WARN("RTL COEX is already closed."); @@ -3015,14 +3615,15 @@ void rtk_btcoex_close(void) #endif /* RTB_SOFTWARE_MAILBOX */ cancel_delayed_work_sync(&btrtl_coex.fw_work); - cancel_delayed_work_sync(&btrtl_coex.l2_work); + cancel_delayed_work_sync(&btrtl_coex.cmd_work); + + /* Process all the remaining pkts */ + rtl_hci_work_func(&btrtl_coex.fw_work.work); flush_connection_hash(&btrtl_coex); flush_profile_hash(&btrtl_coex); btrtl_coex.profile_bitmap = 0; btrtl_coex.profile_status = 0; - for (kk = 0; kk < 8; kk++) - btrtl_coex.profile_refcount[kk] = 0; rtl_free_frags(&btrtl_coex); profileinfo_cmd = 0; @@ -3032,8 +3633,6 @@ void rtk_btcoex_close(void) void rtk_btcoex_probe(struct hci_dev *hdev) { btrtl_coex.hdev = hdev; - spin_lock_init(&btrtl_coex.spin_lock_sock); - spin_lock_init(&btrtl_coex.spin_lock_profile); } void rtk_btcoex_init(void) @@ -3051,9 +3650,11 @@ void rtk_btcoex_init(void) #endif #endif /* RTB_SOFTWARE_MAILBOX */ btrtl_coex.fw_wq = create_workqueue("btfwwork"); - btrtl_coex.timer_wq = create_workqueue("bttimerwork"); rtl_alloc_buff(&btrtl_coex); spin_lock_init(&btrtl_coex.rxlock); + mutex_init(&btrtl_coex.conn_mutex); + spin_lock_init(&btrtl_coex.spin_lock_sock); + mutex_init(&btrtl_coex.profile_mutex); } void rtk_btcoex_exit(void) @@ -3070,7 +3671,5 @@ void rtk_btcoex_exit(void) #endif flush_workqueue(btrtl_coex.fw_wq); destroy_workqueue(btrtl_coex.fw_wq); - flush_workqueue(btrtl_coex.timer_wq); - destroy_workqueue(btrtl_coex.timer_wq); rtl_free_buff(&btrtl_coex); } diff --git a/drivers/bluetooth/realtek/rtk_coex.h b/drivers/bluetooth/realtek/rtk_coex.h index 5d5912ff..d3d4fabd 100644 --- a/drivers/bluetooth/realtek/rtk_coex.h +++ b/drivers/bluetooth/realtek/rtk_coex.h @@ -1,12 +1,23 @@ -// SPDX-License-Identifier: GPL-2.0-only /* * * Realtek Bluetooth USB driver * +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* */ -#ifndef __RTK_COEX_H__ -#define __RTK_COEX_H__ - #include #include @@ -38,6 +49,13 @@ #define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 #define HCI_EV_LE_ENHANCED_CONN_COMPLETE 0x0a +#define HCI_EV_LE_CIS_EST 0x19 +#define HCI_EV_LE_CREATE_BIG_CPL 0x1b +#define HCI_EV_LE_TERM_BIG_CPL 0x1c +#define HCI_EV_LE_BIG_SYNC_EST 0x1d +#define HCI_EV_LE_BIG_SYNC_LOST 0x1e +#define HCI_EV_LE_REMOVE_ISO_DATA_PATH 0x23 + //vendor cmd to fw #define HCI_VENDOR_ENABLE_PROFILE_REPORT_COMMAND 0xfc18 #define HCI_VENDOR_SET_PROFILE_REPORT_LEGACY_COMMAND 0xfc19 @@ -140,7 +158,11 @@ enum { profile_hogp = 5, profile_voice = 6, profile_sink = 7, - profile_max = 8 + profile_lea_src = 8, + profile_opprx = 9, + profile_lea_snk = 10, + profile_a2dpsink = 11, + profile_max = 12 }; #define A2DP_SIGNAL 0x01 @@ -159,7 +181,10 @@ typedef struct { //profile info for each connection typedef struct rtl_hci_conn { struct list_head list; - uint16_t handle; + u16 big_handle; + u16 handle; + u8 direction; + u8 remove_path; struct delayed_work a2dp_count_work; struct delayed_work pan_count_work; struct delayed_work hogp_count_work; @@ -170,7 +195,7 @@ typedef struct rtl_hci_conn { uint8_t type; // 0:l2cap, 1:sco/esco, 2:le uint16_t profile_bitmap; uint16_t profile_status; - int8_t profile_refcount[8]; + int8_t profile_refcount[profile_max]; } rtk_conn_prof, *prtk_conn_prof; #ifdef RTB_SOFTWARE_MAILBOX @@ -204,21 +229,40 @@ struct rtl_btinfo_ctl { }; #endif /* RTB_SOFTWARE_MAILBOX */ +#define HCI_PT_CMD 0x01 +#define HCI_PT_EVT 0x02 +#define HCI_PT_L2SIG_RX 0x03 +#define HCI_PT_L2SIG_TX 0x04 +#define HCI_PT_L2DATA_RX 0x05 +#define HCI_PT_L2DATA_TX 0x06 + +struct rtl_hci_hdr { + struct list_head list; + u8 type; + u16 len; +}; + #define MAX_LEN_OF_HCI_EV 32 #define NUM_RTL_HCI_EV 32 struct rtl_hci_ev { - __u8 data[MAX_LEN_OF_HCI_EV]; - __u16 len; struct list_head list; + u8 type; + u16 len; + + /* private */ + __u8 data[MAX_LEN_OF_HCI_EV]; }; #define L2_MAX_SUBSEC_LEN 128 #define L2_MAX_PKTS 16 struct rtl_l2_buff { - __u8 data[L2_MAX_SUBSEC_LEN]; - __u16 len; - __u16 out; struct list_head list; + u8 type; + u16 len; + + /* private */ + __u8 data[L2_MAX_SUBSEC_LEN]; + __u16 out; }; struct rtl_coex_struct { @@ -236,18 +280,18 @@ struct rtl_coex_struct { struct delayed_work sock_work; #endif struct workqueue_struct *fw_wq; - 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 struct urb *urb; spinlock_t spin_lock_sock; - spinlock_t spin_lock_profile; + struct mutex profile_mutex; + struct mutex conn_mutex; uint16_t profile_bitmap; uint16_t profile_status; - int8_t profile_refcount[8]; + int8_t profile_refcount[profile_max]; uint8_t ispairing; uint8_t isinquirying; uint8_t ispaging; @@ -266,11 +310,8 @@ struct rtl_coex_struct { uint8_t wifi_on; uint8_t sock_open; #endif - unsigned long cmd_last_tx; - /* hci ev buff */ - struct list_head ev_used_list; - struct list_head ev_free_list; + unsigned long cmd_last_tx; spinlock_t rxlock; __u8 pkt_type; @@ -279,16 +320,18 @@ struct rtl_coex_struct { __u16 elen; __u8 back_buff[HCI_MAX_EVENT_SIZE]; - /* l2cap rx buff */ - struct list_head l2_used_list; + struct list_head ev_free_list; struct list_head l2_free_list; + struct list_head hci_pkt_list; /* buff addr and size */ spinlock_t buff_lock; unsigned long pages_addr; unsigned long buff_size; -#define RTL_COEX_RUNNING (1 << 0) +#define RTL_COEX_RUNNING 1 +#define RTL_COEX_PKT_COUNTING 2 +#define RTL_COEX_CONN_REMOVING 3 unsigned long flags; }; @@ -363,6 +406,3 @@ void rtk_btcoex_close(void); void rtk_btcoex_probe(struct hci_dev *hdev); void rtk_btcoex_init(void); void rtk_btcoex_exit(void); - - -#endif /* __RTK_COEX_H__ */ \ No newline at end of file diff --git a/drivers/bluetooth/realtek/rtk_misc.c b/drivers/bluetooth/realtek/rtk_misc.c index 5dec1a5f..eed8d0a9 100644 --- a/drivers/bluetooth/realtek/rtk_misc.c +++ b/drivers/bluetooth/realtek/rtk_misc.c @@ -1,8 +1,22 @@ -// SPDX-License-Identifier: GPL-2.0-only /* * * 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 @@ -30,6 +44,7 @@ #include #include #include +#include #include @@ -54,7 +69,7 @@ struct cfg_list_item { struct list_head list; u16 offset; u8 len; - u8 data[]; + u8 data[0]; }; static struct list_head list_configs; @@ -84,6 +99,7 @@ static struct list_head list_extracfgs; #define HCI_VENDOR_READ_RTK_ROM_VERISION 0xfc6d #define HCI_VENDOR_READ_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 @@ -92,6 +108,9 @@ static struct list_head list_extracfgs; #define ROM_LMP_8761a 0X8761 #define ROM_LMP_8822b 0X8822 #define ROM_LMP_8852a 0x8852 +#define ROM_LMP_8851b 0x8851 +#define ROM_LMP_8922a 0x8922 +#define ROM_LMP_8723c 0x8703 #define PATCH_SNIPPETS 0x01 #define PATCH_DUMMY_HEADER 0x02 @@ -163,41 +182,30 @@ static const uint8_t RTK_EPATCH_SIGNATURE_NEW[8] = //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 */ +static const struct { + __u16 lmp_subver; + __u8 id; +} project_id_to_lmp_subver[] = { + { ROM_LMP_8723a, 0 }, + { ROM_LMP_8723b, 1 }, + { ROM_LMP_8821a, 2 }, + { ROM_LMP_8761a, 3 }, + { ROM_LMP_8723c, 7 }, + { ROM_LMP_8822b, 8 }, /* 8822B */ + { ROM_LMP_8723b, 9 }, /* 8723D */ + { ROM_LMP_8821a, 10 }, /* 8821C */ + { ROM_LMP_8822b, 13 }, /* 8822C */ + { ROM_LMP_8761a, 14 }, /* 8761B */ + { ROM_LMP_8852a, 18 }, /* 8852A */ + { ROM_LMP_8723b, 19 }, /* 8733B */ + { ROM_LMP_8852a, 20 }, /* 8852B */ + { ROM_LMP_8852a, 25 }, /* 8852C */ + { ROM_LMP_8822b, 33 }, /* 8822E */ + { ROM_LMP_8851b, 36 }, /* 8851B */ + { ROM_LMP_8852a, 42 }, /* 8852D */ + { ROM_LMP_8922a, 44 }, /* 8922A */ + { ROM_LMP_8852a, 47 }, /* 8852BT */ + { ROM_LMP_8761a, 51 }, /* 8761C */ }; enum rtk_endpoit { @@ -215,10 +223,16 @@ enum rtk_endpoit { #define RTL8822CU 0x73 #define RTL8761BU 0x74 #define RTL8852AU 0x75 -#define RTL8723FU 0x76 +#define RTL8733BU 0x76 #define RTL8852BU 0x77 #define RTL8852CU 0x78 #define RTL8822EU 0x79 +#define RTL8851BU 0x7A +#define RTL8852DU 0x7B +#define RTL8922AU 0x7C +#define RTL8852BTU 0x7D +#define RTL8761CU 0x80 +#define RTL8723CU 0x81 typedef struct { uint16_t prod_id; @@ -279,7 +293,7 @@ static uint8_t g_key_id = 0; static dev_data *dev_data_find(struct usb_interface *intf); static patch_info *get_patch_entry(struct usb_device *udev); -static int load_firmware(dev_data * dev_entry, uint8_t ** buff); +static int load_firmware(dev_data *dev_entry, xchange_data *xdata); static void init_xdata(xchange_data * xdata, dev_data * dev_entry); static int check_fw_version(xchange_data * xdata); static int download_data(xchange_data * xdata); @@ -344,6 +358,8 @@ static patch_info fw_patch_table[] = { {0xb009, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU */ {0x0231, 0x8723, "mp_rtl8723du_fw", "rtl8723du_fw", "rtl8723du_config", RTL8723DU}, /* RTL8723DU for LiteOn */ + {0xb703, 0x8703, "mp_rtl8723cu_fw", "rtl8723cu_fw", "rtl8723cu_config", RTL8723CU}, /* RTL8723CU */ + {0xb820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CU */ {0xc820, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CU */ {0xc821, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */ @@ -377,6 +393,7 @@ static patch_info fw_patch_table[] = { {0xc82e, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */ {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 */ @@ -405,6 +422,7 @@ static patch_info fw_patch_table[] = { {0xc03f, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE-VS */ {0x8771, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", RTL8761BU}, /* RTL8761BU only */ + {0x876e, 0x8761, "mp_rtl8761b_fw", "rtl8761bu_fw", "rtl8761bu_config", RTL8761BU}, /* RTL8761BUE */ {0xa725, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU */ {0xa72A, 0x8761, "mp_rtl8761b_fw", "rtl8725au_fw", "rtl8725au_config", RTL8761BU}, /* RTL8725AU BT only */ @@ -425,14 +443,13 @@ static patch_info fw_patch_table[] = { {0xc125, 0x8852, "mp_rtl8852au_fw", "rtl8852au_fw", "rtl8852au_config", RTL8852AU}, /* RTL8852AE */ {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_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */ - {0xb73a, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */ - {0xf72b, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */ + {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 */ @@ -453,6 +470,7 @@ static patch_info fw_patch_table[] = { {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 */ @@ -464,6 +482,23 @@ static patch_info fw_patch_table[] = { {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 */ + + {0x892a, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AU */ + {0x8922, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */ + {0xa890, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */ + {0xa891, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */ + {0xa892, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */ + {0xd922, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */ + {0xb85f, 0x8922, "mp_rtl8922au_fw", "rtl8922au_fw", "rtl8922au_config", RTL8922AU}, /* RTL8922AE */ + + {0xc852, 0x8852, "mp_rtl8852btu_fw", "rtl8852btu_fw", "rtl8852btu_config", RTL8852BTU}, /* RTL8852BTU */ + {0x8520, 0x8852, "mp_rtl8852btu_fw", "rtl8852btu_fw", "rtl8852btu_config", RTL8852BTU}, /* RTL8852BTE */ + + {0xc761, 0x8761, "mp_rtl8761cu_fw", "rtl8761cu_mx_fw", "rtl8761cu_mx_config", RTL8761CU}, /* RTL8761CU */ + /* NOTE: must append patch entries above the null entry */ {0, 0, NULL, NULL, NULL, 0} }; @@ -746,6 +781,7 @@ static inline int get_max_patch_size(u8 chip_type) max_patch_size = 25 * 1024; break; case RTL8723DU: + case RTL8723CU: case RTL8822CU: case RTL8761BU: case RTL8821CU: @@ -754,10 +790,11 @@ static inline int get_max_patch_size(u8 chip_type) case RTL8852AU: max_patch_size = 0x114D0 + 529; /* 69.2KB */ break; - case RTL8723FU: + case RTL8733BU: max_patch_size = 0xC4Cf + 529; /* 49.2KB */ break; case RTL8852BU: + case RTL8851BU: max_patch_size = 0x104D0 + 529; /* 65KB */ break; case RTL8852CU: @@ -766,6 +803,18 @@ static inline int get_max_patch_size(u8 chip_type) case RTL8822EU: max_patch_size = 0x24620 + 529; /* 145KB */ break; + case RTL8852DU: + max_patch_size = 0x20D90 + 529; /* 131KB */ + break; + case RTL8922AU: + max_patch_size = 0x23810 + 529; /* 142KB */ + break; + case RTL8852BTU: + max_patch_size = 0x27E00 + 529; /* 159.5KB */ + break; + case RTL8761CU: + max_patch_size = 1024 * 1024; /* 1MB */ + break; default: max_patch_size = 40 * 1024; break; @@ -774,11 +823,64 @@ static inline int get_max_patch_size(u8 chip_type) 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) { @@ -801,9 +903,16 @@ static int check_fw_chip_ver(dev_data * dev_entry, xchange_data * xdata) 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 0; + return ret_val; } int download_patch(struct usb_interface *intf) @@ -836,7 +945,7 @@ int download_patch(struct usb_interface *intf) if (ret_val != 0 ) goto patch_end; - xdata->fw_len = load_firmware(dev_entry, &xdata->fw_data); + xdata->fw_len = load_firmware(dev_entry, xdata); if (xdata->fw_len <= 0) { RTKBT_ERR("load firmware failed!"); ret_val = -1; @@ -875,7 +984,7 @@ int download_patch(struct usb_interface *intf) ret_val = 0; patch_fail: - kfree(fw_buf); + vfree(fw_buf); patch_end: if (xdata != NULL) { if (xdata->send_pkt) @@ -893,7 +1002,7 @@ patch_end: * -1: error * 0: download patch successfully * >0: patch already exists */ -int download_lps_patch(struct usb_interface *intf) +int download_special_patch(struct usb_interface *intf, const char *special_name) { dev_data *dev_entry; patch_info *pinfo; @@ -935,15 +1044,18 @@ int download_lps_patch(struct usb_interface *intf) } 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; - snprintf(name1, sizeof(name1), "lps_%s", origin_name1); - snprintf(name2, sizeof(name2), "lps_%s", origin_name2); + 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); + xdata->fw_len = load_firmware(dev_entry, xdata); dev_entry->patch_entry->patch_name = origin_name1; dev_entry->patch_entry->config_name = origin_name2; if (xdata->fw_len <= 0) { @@ -955,11 +1067,13 @@ int download_lps_patch(struct usb_interface *intf) 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; @@ -998,6 +1112,39 @@ patch_end: } #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) { @@ -1045,7 +1192,7 @@ end: dev_data *dev_data_find(struct usb_interface * intf) { - dev_data *dev_entry; + dev_data *dev_entry = NULL; list_for_each_entry(dev_entry, &dev_data_list, list_node) { if (dev_entry->intf == intf) { @@ -1088,6 +1235,7 @@ static int is_mac(u8 chip_type, u16 offset) switch (chip_type) { case RTL8822BU: case RTL8723DU: + case RTL8723CU: case RTL8821CU: if (offset == 0x0044) return 1; @@ -1095,10 +1243,15 @@ static int is_mac(u8 chip_type, u16 offset) case RTL8822CU: case RTL8761BU: case RTL8852AU: - case RTL8723FU: + case RTL8733BU: case RTL8852BU: case RTL8852CU: case RTL8822EU: + case RTL8851BU: + case RTL8852DU: + case RTL8922AU: + case RTL8852BTU: + case RTL8761CU: if (offset == 0x0030) return 1; break; @@ -1116,15 +1269,21 @@ static uint16_t get_mac_offset(u8 chip_type) switch (chip_type) { case RTL8822BU: case RTL8723DU: + case RTL8723CU: case RTL8821CU: return 0x0044; case RTL8822CU: case RTL8761BU: case RTL8852AU: - case RTL8723FU: + case RTL8733BU: case RTL8852BU: case RTL8852CU: case RTL8822EU: + case RTL8851BU: + case RTL8852DU: + case RTL8922AU: + case RTL8852BTU: + case RTL8761CU: return 0x0030; case RTLPREVIOUS: return 0x003c; @@ -1452,14 +1611,12 @@ static uint8_t *rtb_get_patch_header(int *len, } break; default: - RTKBT_ERR("Wrong Opcode"); - goto wrong_opcode; + RTKBT_INFO("Unknown Opcode. Ignore"); } section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len); } *len = patch_len; -wrong_opcode: return NULL; } @@ -1743,7 +1900,7 @@ static int rtk_vendor_read(dev_data * dev_entry, uint8_t class) 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}; + unsigned char cmd_sec_buf[] = {0x10, 0xA4, 0xAD, 0x00, 0xb0}; xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL); if (NULL == xdata) { @@ -1811,7 +1968,6 @@ static int rtk_vendor_read(dev_data * dev_entry, uint8_t class) } } - read_end: if (xdata != NULL) { if (xdata->send_pkt) @@ -1823,7 +1979,57 @@ read_end: return ret_val; } -int load_firmware(dev_data * dev_entry, uint8_t ** buff) +static int needs_hci_upgrade(xchange_data *xdata, u8 *buf, u32 buf_len) +{ + struct { + u8 status; + u8 subopcode; + u8 ota; + } __attribute__((packed)) *evt_params; +#define UPG_DL_BLOCK_SIZE 128 +#define UPG_SUBCMD_CODE 0x01 + u8 len = UPG_DL_BLOCK_SIZE; + u8 *cmd_params; + int ret; + + cmd_params = xdata->req_para; + evt_params = (void *)xdata->rsp_para; + xdata->cmd_hdr->opcode = cpu_to_le16(0xfdbb); + if (buf_len < len) + len = buf_len; + xdata->cmd_hdr->plen = 1 + len; + xdata->pkt_len = sizeof(*xdata->cmd_hdr) + xdata->cmd_hdr->plen; + *cmd_params++ = UPG_SUBCMD_CODE; + memcpy(cmd_params, buf, len); + + ret = send_hci_cmd(xdata); + if (ret < 0) + return ret; + + ret = rcv_hci_evt(xdata); + if (ret < 0) + return ret; + + if (evt_params->status) { + RTKBT_ERR("needs_hci_upgrade: status %02x", evt_params->status); + return -1; + } + + if (evt_params->subopcode != UPG_SUBCMD_CODE) { + RTKBT_ERR("needs_hci_upgrade: return subopcode %02x", + evt_params->subopcode); + return -2; + } + + RTKBT_INFO("needs_hci_upgrade: state %02x", evt_params->ota); + + return evt_params->ota; +} + +/* buff: points to the allocated buffer that stores extracted fw and config + * This function returns the total length of extracted fw and config + */ +int load_firmware(dev_data *dev_entry, xchange_data *xdata) { const struct firmware *fw; struct usb_device *udev; @@ -1835,12 +2041,13 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff) 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; + int i; RTKBT_DBG("load_firmware start"); + udev = dev_entry->udev; patch_entry = dev_entry->patch_entry; lmp_version = patch_entry->lmp_sub; @@ -1853,16 +2060,13 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff) ret_val = request_firmware(&fw, fw_name, &udev->dev); 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) + epatch_buf = vzalloc(fw->size); + if (!epatch_buf) goto alloc_fail; memcpy(epatch_buf, fw->data, fw->size); @@ -1874,137 +2078,174 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff) 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); - } - } + goto sign_err; } - } 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; + buf = vzalloc(buf_len); + if (!buf) { + RTKBT_ERR("Can't alloc memory for fw&config"); + buf_len = -1; + goto alloc_buf_err; } - /* 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); + 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); - 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]); + goto done; + } - 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); + RTKBT_ERR("This is not 8723a, use new patch style!"); - if (current_entry.patch_length == 0) - goto alloc_fail; + /* 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; + goto alloc_fail; + } - buf_len = current_entry.patch_length + config_len; - RTKBT_DBG("buf_len = 0x%x", buf_len); - } + /* check Signature and Extension Section Field */ + if ((memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) && + memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8)) || + 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; + goto sign_err; + } - 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); - } - } - } - } + proj_id = rtk_get_fw_project_id(epatch_buf + buf_len - config_len - 5); + + for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) { + if (proj_id == project_id_to_lmp_subver[i].id && + lmp_version == project_id_to_lmp_subver[i].lmp_subver) { + break; } } + if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) { + RTKBT_ERR("lmp_version %04x, project_id %u, does not match!!!", + lmp_version, proj_id); + need_download_fw = 0; + goto proj_id_err; + } + + RTKBT_DBG("lmp_version is %04x, project_id is %u, match!", + lmp_version, proj_id); + + if (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) { + int key_id = rtk_vendor_read(dev_entry, READ_SEC_PROJ); + int tmp_len = 0; + + 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 extract_err; + } + rtb_get_patch_header(&buf_len, &patch_node_hdr, epatch_buf, + key_id); + if (!buf_len) + goto extract_err; + RTKBT_DBG("buf_len = 0x%x", buf_len); + buf_len += config_len; + + buf = vzalloc(buf_len); + if (!buf) { + RTKBT_ERR("Can't alloc memory for multi fw&config"); + buf_len = -1; + goto alloc_buf_err; + } + + 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 { + rtk_get_patch_entry(epatch_buf, ¤t_entry); + + if (current_entry.patch_length == 0) + goto extract_err; + + buf_len = current_entry.patch_length + config_len; + RTKBT_DBG("buf_len = 0x%x", buf_len); + + buf = vzalloc(buf_len); + if (!buf) { + RTKBT_ERR("Can't alloc memory for multi fw&config"); + buf_len = -1; + goto alloc_buf_err; + } + + memcpy(buf, epatch_buf + current_entry.start_offset, + current_entry.patch_length); + /* Copy fw version */ + memcpy(buf + current_entry.patch_length - 4, epatch_buf + 8, 4); + if (config_len) + memcpy(&buf[buf_len - config_len], config_file_buf, + config_len); + } + + if (patch_entry->chip_type == RTL8761CU) { + if (needs_hci_upgrade(xdata, buf, buf_len) <= 0) { + if (config_len > 0) { + memmove(buf, buf + buf_len - config_len, + config_len); + buf_len = config_len; + } else { +#define FAKE_SEG_LEN 16 + if (buf_len > FAKE_SEG_LEN) + buf_len = FAKE_SEG_LEN; + memset(buf, 0, buf_len); + } + } else { + /* It does not need to download config when upgrading */ + buf_len -= config_len; + } + } + +done: 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)) { + buf_len > 0 ? "" : "not", config_len > 0 ? "" : "not"); + if (buf && buf_len > 0 && need_download_fw) { fw_len = buf_len; - *buff = buf; + xdata->fw_data = buf; } RTKBT_DBG("load_firmware done"); - +alloc_buf_err: +extract_err: + /* Make sure all the patch nodes freed */ + list_for_each_safe(pos, next, &patch_node_hdr.list) { + tmp = list_entry(pos, struct patch_node, list); + list_del_init(pos); + kfree(tmp); + } +proj_id_err: +sign_err: alloc_fail: release_firmware(fw); if (epatch_buf) - kfree(epatch_buf); + vfree(epatch_buf); +fw_fail: if (config_file_buf) kfree(config_file_buf); -fw_fail: + if (fw_len == 0) - kfree(buf); + vfree(buf); return fw_len; } @@ -2149,7 +2390,7 @@ int download_data(xchange_data * xdata) uint8_t *pcur; int pkt_len, frag_num, frag_len; int i, ret_val; - int j; + int j = 0; RTKBT_DBG("download_data start"); @@ -2161,12 +2402,11 @@ int download_data(xchange_data * xdata) frag_len = PATCH_SEG_MAX; for (i = 0; i < frag_num; i++) { - if (i > 0x7f) - j = (i & 0x7f) + 1; - else - j = i; + cmd_para->index = j++; + + if(cmd_para->index == 0x7f) + j = 1; - cmd_para->index = j; if (i == (frag_num - 1)) { cmd_para->index |= DATA_END; frag_len = xdata->fw_len % PATCH_SEG_MAX; diff --git a/drivers/bluetooth/realtek/rtk_misc.h b/drivers/bluetooth/realtek/rtk_misc.h index 4e0ad091..4eccbfe6 100644 --- a/drivers/bluetooth/realtek/rtk_misc.h +++ b/drivers/bluetooth/realtek/rtk_misc.h @@ -1,11 +1,23 @@ -// SPDX-License-Identifier: GPL-2.0-only /* * * Realtek Bluetooth USB download firmware driver * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * */ -#ifndef __RTK_MISC_H__ -#define __RTK_MISC_H__ #include #include @@ -14,6 +26,8 @@ #include #include +#define CONFIG_BTUSB_AUTOSUSPEND 0 + /* Download LPS patch when host suspends or power off * LPS patch name: lps_rtl8xxx_fw * LPS config name: lps_rtl8xxx_config @@ -33,19 +47,21 @@ #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_DEBUG "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_DEBUG "rtk_btusb: " fmt "\n" , ## arg) +#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 +#define USB_RPM 1 +#else +#define USB_RPM 0 #endif #define CONFIG_NEEDS_BINDING @@ -56,7 +72,7 @@ #endif /* USB SS */ -#if (defined CONFIG_BTUSB_AUTOSUSPEND) && (defined USB_RPM) +#if (CONFIG_BTUSB_AUTOSUSPEND && USB_RPM) #define BTUSB_RPM #endif @@ -82,12 +98,41 @@ struct api_context { int status; }; -int download_lps_patch(struct usb_interface *intf); +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 - - -#endif /* __RTK_MISC_H__ */ \ No newline at end of file