summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/btusb.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-07-16 19:28:34 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-07-16 19:28:34 -0700
commit51835949dda3783d4639cfa74ce13a3c9829de00 (patch)
tree2b593de5eba6ecc73f7c58fc65fdaffae45c7323 /drivers/bluetooth/btusb.c
parent0434dbe32053d07d658165be681505120c6b1abc (diff)
parent77ae5e5b00720372af2860efdc4bc652ac682696 (diff)
Merge tag 'net-next-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextHEADmaster
Pull networking updates from Jakub Kicinski: "Not much excitement - a handful of large patchsets (devmem among them) did not make it in time. Core & protocols: - Use local_lock in addition to local_bh_disable() to protect per-CPU resources in networking, a step closer for local_bh_disable() not to act as a big lock on PREEMPT_RT - Use flex array for netdevice priv area, ensure its cache alignment - Add a sysctl knob to allow user to specify a default rto_min at socket init time. Bit of a big hammer but multiple companies were independently carrying such patch downstream so clearly it's useful - Support scheduling transmission of packets based on CLOCK_TAI - Un-pin TCP TIMEWAIT timer to avoid it firing on CPUs later cordoned off using cpusets - Support multiple L2TPv3 UDP tunnels using the same 5-tuple address - Allow configuration of multipath hash seed, to both allow synchronizing hashing of two routers, and preventing partial accidental sync - Improve TCP compliance with RFC 9293 for simultaneous connect() - Support sending NAT keepalives in IPsec ESP in UDP states. Userspace IKE daemon had to do this before, but the kernel can better keep track of it - Support sending supervision HSR frames with MAC addresses stored in ProxyNodeTable when RedBox (i.e. HSR-SAN) is enabled - Introduce IPPROTO_SMC for selecting SMC when socket is created - Allow UDP GSO transmit from devices with no checksum offload - openvswitch: add packet sampling via psample, separating the sampled traffic from "upcall" packets sent to user space for forwarding - nf_tables: shrink memory consumption for transaction objects Things we sprinkled into general kernel code: - Power Sequencing subsystem (used by Qualcomm Bluetooth driver for QCA6390) [ Already merged separately - Linus ] - Add IRQ information in sysfs for auxiliary bus - Introduce guard definition for local_lock - Add aligned flavor of __cacheline_group_{begin, end}() markings for grouping fields in structures BPF: - Notify user space (via epoll) when a struct_ops object is getting detached/unregistered - Add new kfuncs for a generic, open-coded bits iterator - Enable BPF programs to declare arrays of kptr, bpf_rb_root, and bpf_list_head - Support resilient split BTF which cuts down on duplication and makes BTF as compact as possible WRT BTF from modules - Add support for dumping kfunc prototypes from BTF which enables both detecting as well as dumping compilable prototypes for kfuncs - riscv64 BPF JIT improvements in particular to add 12-argument support for BPF trampolines and to utilize bpf_prog_pack for the latter - Add the capability to offload the netfilter flowtable in XDP layer through kfuncs Driver API: - Allow users to configure IRQ tresholds between which automatic IRQ moderation can choose - Expand Power Sourcing (PoE) status with power, class and failure reason. Support setting power limits - Track additional RSS contexts in the core, make sure configuration changes don't break them - Support IPsec crypto offload for IPv6 ESP and IPv4 UDP-encapsulated ESP data paths - Support updating firmware on SFP modules Tests and tooling: - mptcp: use net/lib.sh to manage netns - TCP-AO and TCP-MD5: replace debug prints used by tests with tracepoints - openvswitch: make test self-contained (don't depend on OvS CLI tools) Drivers: - Ethernet high-speed NICs: - Broadcom (bnxt): - increase the max total outstanding PTP TX packets to 4 - add timestamping statistics support - implement netdev_queue_mgmt_ops - support new RSS context API - Intel (100G, ice, idpf): - implement FEC statistics and dumping signal quality indicators - support E825C products (with 56Gbps PHYs) - nVidia/Mellanox: - support HW-GRO - mlx4/mlx5: support per-queue statistics via netlink - obey the max number of EQs setting in sub-functions - AMD/Solarflare: - support new RSS context API - AMD/Pensando: - ionic: rework fix for doorbell miss to lower overhead and skip it on new HW - Wangxun: - txgbe: support Flow Director perfect filters - Ethernet NICs consumer, embedded and virtual: - Add driver for Tehuti Networks TN40xx chips - Add driver for Meta's internal NIC chips - Add driver for Ethernet MAC on Airoha EN7581 SoCs - Add driver for Renesas Ethernet-TSN devices - Google cloud vNIC: - flow steering support - Microsoft vNIC: - support page sizes other than 4KB on ARM64 - vmware vNIC: - support latency measurement (update to version 9) - VirtIO net: - support for Byte Queue Limits - support configuring thresholds for automatic IRQ moderation - support for AF_XDP Rx zero-copy - Synopsys (stmmac): - support for STM32MP13 SoC - let platforms select the right PCS implementation - TI: - icssg-prueth: add multicast filtering support - icssg-prueth: enable PTP timestamping and PPS - Renesas: - ravb: improve Rx performance 30-400% by using page pool, theaded NAPI and timer-based IRQ coalescing - ravb: add MII support for R-Car V4M - Cadence (macb): - macb: add ARP support to Wake-On-LAN - Cortina: - use phylib for RX and TX pause configuration - Ethernet switches: - nVidia/Mellanox: - support configuration of multipath hash seed - report more accurate max MTU - use page_pool to improve Rx performance - MediaTek: - mt7530: add support for bridge port isolation - Qualcomm: - qca8k: add support for bridge port isolation - Microchip: - lan9371/2: add 100BaseTX PHY support - NXP: - vsc73xx: implement VLAN operations - Ethernet PHYs: - aquantia: enable support for aqr115c - aquantia: add support for PHY LEDs - realtek: add support for rtl8224 2.5Gbps PHY - xpcs: add memory-mapped device support - add BroadR-Reach link mode and support in Broadcom's PHY driver - CAN: - add document for ISO 15765-2 protocol support - mcp251xfd: workaround for erratum DS80000789E, use timestamps to catch when device returns incorrect FIFO status - WiFi: - mac80211/cfg80211: - parse Transmit Power Envelope (TPE) data in mac80211 instead of in drivers - improvements for 6 GHz regulatory flexibility - multi-link improvements - support multiple radios per wiphy - remove DEAUTH_NEED_MGD_TX_PREP flag - Intel (iwlwifi): - bump FW API to 91 for BZ/SC devices - report 64-bit radiotap timestamp - enable P2P low latency by default - handle Transmit Power Envelope (TPE) advertised by AP - remove support for older FW for new devices - fast resume (keeping the device configured) - mvm: re-enable Multi-Link Operation (MLO) - aggregation (A-MSDU) optimizations - MediaTek (mt76): - mt7925 Multi-Link Operation (MLO) support - Qualcomm (ath10k): - LED support for various chipsets - Qualcomm (ath12k): - remove unsupported Tx monitor handling - support channel 2 in 6 GHz band - support Spatial Multiplexing Power Save (SMPS) in 6 GHz band - supprt multiple BSSID (MBSSID) and Enhanced Multi-BSSID Advertisements (EMA) - support dynamic VLAN - add panic handler for resetting the firmware state - DebugFS support for datapath statistics - WCN7850: support for Wake on WLAN - Microchip (wilc1000): - read MAC address during probe to make it visible to user space - suspend/resume improvements - TI (wl18xx): - support newer firmware versions - RealTek (rtw89): - preparation for RTL8852BE-VT support - Wake on WLAN support for WiFi 6 chips - 36-bit PCI DMA support - RealTek (rtlwifi): - RTL8192DU support - Broadcom (brcmfmac): - Management Frame Protection support (to enable WPA3) - Bluetooth: - qualcomm: use the power sequencer for QCA6390 - btusb: mediatek: add ISO data transmission functions - hci_bcm4377: add BCM4388 support - btintel: add support for BlazarU core - btintel: add support for Whale Peak2 - btnxpuart: add support for AW693 A1 chipset - btnxpuart: add support for IW615 chipset - btusb: add Realtek RTL8852BE support ID 0x13d3:0x3591" * tag 'net-next-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1589 commits) eth: fbnic: Fix spelling mistake "tiggerring" -> "triggering" tcp: Replace strncpy() with strscpy() wifi: ath12k: fix build vs old compiler tcp: Don't access uninit tcp_rsk(req)->ao_keyid in tcp_create_openreq_child(). eth: fbnic: Write the TCAM tables used for RSS control and Rx to host eth: fbnic: Add L2 address programming eth: fbnic: Add basic Rx handling eth: fbnic: Add basic Tx handling eth: fbnic: Add link detection eth: fbnic: Add initial messaging to notify FW of our presence eth: fbnic: Implement Rx queue alloc/start/stop/free eth: fbnic: Implement Tx queue alloc/start/stop/free eth: fbnic: Allocate a netdevice and napi vectors with queues eth: fbnic: Add FW communication mechanism eth: fbnic: Add message parsing for FW messages eth: fbnic: Add register init to set PCIe/Ethernet device config eth: fbnic: Allocate core device specific structures and devlink interface eth: fbnic: Add scaffolding for Meta's NIC driver PCI: Add Meta Platforms vendor ID net/sched: cls_flower: propagate tca[TCA_OPTIONS] to NL_REQ_ATTR_CHECK ...
Diffstat (limited to 'drivers/bluetooth/btusb.c')
-rw-r--r--drivers/bluetooth/btusb.c735
1 files changed, 78 insertions, 657 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index e384ef6ff050..acdba5d77694 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -479,6 +479,7 @@ static const struct usb_device_id quirks_table[] = {
{ USB_DEVICE(0x8087, 0x0036), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0037), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x0038), .driver_info = BTUSB_INTEL_COMBINED },
+ { USB_DEVICE(0x8087, 0x0039), .driver_info = BTUSB_INTEL_COMBINED },
{ USB_DEVICE(0x8087, 0x07da), .driver_info = BTUSB_CSR },
{ USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL_COMBINED |
BTUSB_INTEL_NO_WBS_SUPPORT |
@@ -555,6 +556,10 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3572), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3591), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe125), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Realtek 8852BT/8852BE-VT Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x8520), .driver_info = BTUSB_REALTEK |
@@ -891,6 +896,9 @@ struct btusb_data {
int (*setup_on_usb)(struct hci_dev *hdev);
+ int (*suspend)(struct hci_dev *hdev);
+ int (*resume)(struct hci_dev *hdev);
+
int oob_wake_irq; /* irq for out-of-band wake-on-bt */
unsigned cmd_timeout_cnt;
@@ -2638,410 +2646,48 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
return hci_recv_frame(hdev, skb);
}
-/* UHW CR mapping */
-#define MTK_BT_MISC 0x70002510
-#define MTK_BT_SUBSYS_RST 0x70002610
-#define MTK_UDMA_INT_STA_BT 0x74000024
-#define MTK_UDMA_INT_STA_BT1 0x74000308
-#define MTK_BT_WDT_STATUS 0x740003A0
-#define MTK_EP_RST_OPT 0x74011890
-#define MTK_EP_RST_IN_OUT_OPT 0x00010001
-#define MTK_BT_RST_DONE 0x00000100
-#define MTK_BT_RESET_REG_CONNV3 0x70028610
-#define MTK_BT_READ_DEV_ID 0x70010200
-
-
-static void btusb_mtk_wmt_recv(struct urb *urb)
-{
- struct hci_dev *hdev = urb->context;
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct sk_buff *skb;
- int err;
-
- if (urb->status == 0 && urb->actual_length > 0) {
- hdev->stat.byte_rx += urb->actual_length;
-
- /* WMT event shouldn't be fragmented and the size should be
- * less than HCI_WMT_MAX_EVENT_SIZE.
- */
- skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC);
- if (!skb) {
- hdev->stat.err_rx++;
- kfree(urb->setup_packet);
- return;
- }
-
- hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
- skb_put_data(skb, urb->transfer_buffer, urb->actual_length);
-
- /* When someone waits for the WMT event, the skb is being cloned
- * and being processed the events from there then.
- */
- if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) {
- data->evt_skb = skb_clone(skb, GFP_ATOMIC);
- if (!data->evt_skb) {
- kfree_skb(skb);
- kfree(urb->setup_packet);
- return;
- }
- }
-
- err = hci_recv_frame(hdev, skb);
- if (err < 0) {
- kfree_skb(data->evt_skb);
- data->evt_skb = NULL;
- kfree(urb->setup_packet);
- return;
- }
-
- if (test_and_clear_bit(BTUSB_TX_WAIT_VND_EVT,
- &data->flags)) {
- /* Barrier to sync with other CPUs */
- smp_mb__after_atomic();
- wake_up_bit(&data->flags,
- BTUSB_TX_WAIT_VND_EVT);
- }
- kfree(urb->setup_packet);
- return;
- } else if (urb->status == -ENOENT) {
- /* Avoid suspend failed when usb_kill_urb */
- return;
- }
-
- usb_mark_last_busy(data->udev);
-
- /* The URB complete handler is still called with urb->actual_length = 0
- * when the event is not available, so we should keep re-submitting
- * URB until WMT event returns, Also, It's necessary to wait some time
- * between the two consecutive control URBs to relax the target device
- * to generate the event. Otherwise, the WMT event cannot return from
- * the device successfully.
- */
- udelay(500);
-
- usb_anchor_urb(urb, &data->ctrl_anchor);
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err < 0) {
- kfree(urb->setup_packet);
- /* -EPERM: urb is being killed;
- * -ENODEV: device got disconnected
- */
- if (err != -EPERM && err != -ENODEV)
- bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
- urb, -err);
- usb_unanchor_urb(urb);
- }
-}
-
-static int btusb_mtk_submit_wmt_recv_urb(struct hci_dev *hdev)
-{
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct usb_ctrlrequest *dr;
- unsigned char *buf;
- int err, size = 64;
- unsigned int pipe;
- struct urb *urb;
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
- return -ENOMEM;
-
- dr = kmalloc(sizeof(*dr), GFP_KERNEL);
- if (!dr) {
- usb_free_urb(urb);
- return -ENOMEM;
- }
-
- dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
- dr->bRequest = 1;
- dr->wIndex = cpu_to_le16(0);
- dr->wValue = cpu_to_le16(48);
- dr->wLength = cpu_to_le16(size);
-
- buf = kmalloc(size, GFP_KERNEL);
- if (!buf) {
- kfree(dr);
- usb_free_urb(urb);
- return -ENOMEM;
- }
-
- pipe = usb_rcvctrlpipe(data->udev, 0);
-
- usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
- buf, size, btusb_mtk_wmt_recv, hdev);
-
- urb->transfer_flags |= URB_FREE_BUFFER;
-
- usb_anchor_urb(urb, &data->ctrl_anchor);
- err = usb_submit_urb(urb, GFP_KERNEL);
- if (err < 0) {
- if (err != -EPERM && err != -ENODEV)
- bt_dev_err(hdev, "urb %p submission failed (%d)",
- urb, -err);
- usb_unanchor_urb(urb);
- }
-
- usb_free_urb(urb);
-
- return err;
-}
-
-static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
- struct btmtk_hci_wmt_params *wmt_params)
+static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
{
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
- u32 hlen, status = BTMTK_WMT_INVALID;
- struct btmtk_hci_wmt_evt *wmt_evt;
- struct btmtk_hci_wmt_cmd *wc;
- struct btmtk_wmt_hdr *hdr;
+ struct btmtk_data *btmtk_data = hci_get_priv(data->hdev);
int err;
- /* Send the WMT command and wait until the WMT event returns */
- hlen = sizeof(*hdr) + wmt_params->dlen;
- if (hlen > 255)
- return -EINVAL;
-
- wc = kzalloc(hlen, GFP_KERNEL);
- if (!wc)
- return -ENOMEM;
-
- hdr = &wc->hdr;
- hdr->dir = 1;
- hdr->op = wmt_params->op;
- hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
- hdr->flag = wmt_params->flag;
- memcpy(wc->data, wmt_params->data, wmt_params->dlen);
-
- set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
-
- /* WMT cmd/event doesn't follow up the generic HCI cmd/event handling,
- * it needs constantly polling control pipe until the host received the
- * WMT event, thus, we should require to specifically acquire PM counter
- * on the USB to prevent the interface from entering auto suspended
- * while WMT cmd/event in progress.
- */
- err = usb_autopm_get_interface(data->intf);
- if (err < 0)
- goto err_free_wc;
-
- err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
-
- if (err < 0) {
- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
- usb_autopm_put_interface(data->intf);
- goto err_free_wc;
- }
-
- /* Submit control IN URB on demand to process the WMT event */
- err = btusb_mtk_submit_wmt_recv_urb(hdev);
-
- usb_autopm_put_interface(data->intf);
-
- if (err < 0)
- goto err_free_wc;
-
- /* The vendor specific WMT commands are all answered by a vendor
- * specific event and will have the Command Status or Command
- * Complete as with usual HCI command flow control.
- *
- * After sending the command, wait for BTUSB_TX_WAIT_VND_EVT
- * state to be cleared. The driver specific event receive routine
- * will clear that state and with that indicate completion of the
- * WMT command.
- */
- err = wait_on_bit_timeout(&data->flags, BTUSB_TX_WAIT_VND_EVT,
- TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
- if (err == -EINTR) {
- bt_dev_err(hdev, "Execution of wmt command interrupted");
- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
- goto err_free_wc;
- }
-
- if (err) {
- bt_dev_err(hdev, "Execution of wmt command timed out");
- clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
- err = -ETIMEDOUT;
- goto err_free_wc;
- }
-
- if (data->evt_skb == NULL)
- goto err_free_wc;
-
- /* Parse and handle the return WMT event */
- wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data;
- if (wmt_evt->whdr.op != hdr->op) {
- bt_dev_err(hdev, "Wrong op received %d expected %d",
- wmt_evt->whdr.op, hdr->op);
- err = -EIO;
- goto err_free_skb;
- }
-
- switch (wmt_evt->whdr.op) {
- case BTMTK_WMT_SEMAPHORE:
- if (wmt_evt->whdr.flag == 2)
- status = BTMTK_WMT_PATCH_UNDONE;
- else
- status = BTMTK_WMT_PATCH_DONE;
- break;
- case BTMTK_WMT_FUNC_CTRL:
- wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
- if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
- status = BTMTK_WMT_ON_DONE;
- else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420)
- status = BTMTK_WMT_ON_PROGRESS;
- else
- status = BTMTK_WMT_ON_UNDONE;
- break;
- case BTMTK_WMT_PATCH_DWNLD:
- if (wmt_evt->whdr.flag == 2)
- status = BTMTK_WMT_PATCH_DONE;
- else if (wmt_evt->whdr.flag == 1)
- status = BTMTK_WMT_PATCH_PROGRESS;
- else
- status = BTMTK_WMT_PATCH_UNDONE;
- break;
- }
-
- if (wmt_params->status)
- *wmt_params->status = status;
-
-err_free_skb:
- kfree_skb(data->evt_skb);
- data->evt_skb = NULL;
-err_free_wc:
- kfree(wc);
- return err;
-}
-
-static int btusb_mtk_func_query(struct hci_dev *hdev)
-{
- struct btmtk_hci_wmt_params wmt_params;
- int status, err;
- u8 param = 0;
-
- /* Query whether the function is enabled */
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 4;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = &status;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to query function status (%d)", err);
- return err;
- }
-
- return status;
-}
-
-static int btusb_mtk_uhw_reg_write(struct btusb_data *data, u32 reg, u32 val)
-{
- struct hci_dev *hdev = data->hdev;
- int pipe, err;
- void *buf;
-
- buf = kzalloc(4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- put_unaligned_le32(val, buf);
-
- pipe = usb_sndctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x02,
- 0x5E,
- reg >> 16, reg & 0xffff,
- buf, 4, USB_CTRL_SET_TIMEOUT);
+ err = usb_driver_claim_interface(&btusb_driver,
+ btmtk_data->isopkt_intf, data);
if (err < 0) {
- bt_dev_err(hdev, "Failed to write uhw reg(%d)", err);
- goto err_free_buf;
+ btmtk_data->isopkt_intf = NULL;
+ bt_dev_err(data->hdev, "Failed to claim iso interface");
+ return;
}
-err_free_buf:
- kfree(buf);
-
- return err;
+ set_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
}
-static int btusb_mtk_uhw_reg_read(struct btusb_data *data, u32 reg, u32 *val)
+static void btusb_mtk_release_iso_intf(struct btusb_data *data)
{
- struct hci_dev *hdev = data->hdev;
- int pipe, err;
- void *buf;
+ struct btmtk_data *btmtk_data = hci_get_priv(data->hdev);
- buf = kzalloc(4, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ if (btmtk_data->isopkt_intf) {
+ usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
+ clear_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags);
- pipe = usb_rcvctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x01,
- 0xDE,
- reg >> 16, reg & 0xffff,
- buf, 4, USB_CTRL_GET_TIMEOUT);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to read uhw reg(%d)", err);
- goto err_free_buf;
+ dev_kfree_skb_irq(btmtk_data->isopkt_skb);
+ btmtk_data->isopkt_skb = NULL;
+ usb_set_intfdata(btmtk_data->isopkt_intf, NULL);
+ usb_driver_release_interface(&btusb_driver,
+ btmtk_data->isopkt_intf);
}
- *val = get_unaligned_le32(buf);
- bt_dev_dbg(hdev, "reg=%x, value=0x%08x", reg, *val);
-
-err_free_buf:
- kfree(buf);
-
- return err;
-}
-
-static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
-{
- int pipe, err, size = sizeof(u32);
- void *buf;
-
- buf = kzalloc(size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- pipe = usb_rcvctrlpipe(data->udev, 0);
- err = usb_control_msg(data->udev, pipe, 0x63,
- USB_TYPE_VENDOR | USB_DIR_IN,
- reg >> 16, reg & 0xffff,
- buf, size, USB_CTRL_GET_TIMEOUT);
- if (err < 0)
- goto err_free_buf;
-
- *val = get_unaligned_le32(buf);
-
-err_free_buf:
- kfree(buf);
-
- return err;
-}
-
-static int btusb_mtk_id_get(struct btusb_data *data, u32 reg, u32 *id)
-{
- return btusb_mtk_reg_read(data, reg, id);
-}
-
-static u32 btusb_mtk_reset_done(struct hci_dev *hdev)
-{
- struct btusb_data *data = hci_get_drvdata(hdev);
- u32 val = 0;
-
- btusb_mtk_uhw_reg_read(data, MTK_BT_MISC, &val);
-
- return val & MTK_BT_RST_DONE;
+ clear_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
}
static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
{
struct btusb_data *data = hci_get_drvdata(hdev);
- struct btmediatek_data *mediatek;
- u32 val;
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
int err;
/* It's MediaTek specific bluetooth reset mechanism via USB */
- if (test_and_set_bit(BTUSB_HW_RESET_ACTIVE, &data->flags)) {
+ if (test_and_set_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags)) {
bt_dev_err(hdev, "last reset failed? Not resetting again");
return -EBUSY;
}
@@ -3050,302 +2696,68 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
if (err < 0)
return err;
+ if (test_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags))
+ btusb_mtk_release_iso_intf(data);
+
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
- mediatek = hci_get_priv(hdev);
-
- if (mediatek->dev_id == 0x7925) {
- btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
- val |= (1 << 5);
- btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
- btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
- val &= 0xFFFF00FF;
- val |= (1 << 13);
- btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, 0x00010001);
- btusb_mtk_uhw_reg_read(data, MTK_BT_RESET_REG_CONNV3, &val);
- val |= (1 << 0);
- btusb_mtk_uhw_reg_write(data, MTK_BT_RESET_REG_CONNV3, val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
- msleep(100);
- } else {
- /* It's Device EndPoint Reset Option Register */
- bt_dev_dbg(hdev, "Initiating reset mechanism via uhw");
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
- btusb_mtk_uhw_reg_read(data, MTK_BT_WDT_STATUS, &val);
-
- /* Reset the bluetooth chip via USB interface. */
- btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 1);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT, &val);
- btusb_mtk_uhw_reg_write(data, MTK_UDMA_INT_STA_BT1, 0x000000FF);
- btusb_mtk_uhw_reg_read(data, MTK_UDMA_INT_STA_BT1, &val);
- /* MT7921 need to delay 20ms between toggle reset bit */
- msleep(20);
- btusb_mtk_uhw_reg_write(data, MTK_BT_SUBSYS_RST, 0);
- btusb_mtk_uhw_reg_read(data, MTK_BT_SUBSYS_RST, &val);
- }
- err = readx_poll_timeout(btusb_mtk_reset_done, hdev, val,
- val & MTK_BT_RST_DONE, 20000, 1000000);
- if (err < 0)
- bt_dev_err(hdev, "Reset timeout");
-
- btusb_mtk_id_get(data, 0x70010200, &val);
- if (!val)
- bt_dev_err(hdev, "Can't get device id, subsys reset fail.");
+ err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
usb_queue_reset_device(data->intf);
-
- clear_bit(BTUSB_HW_RESET_ACTIVE, &data->flags);
+ clear_bit(BTMTK_HW_RESET_ACTIVE, &btmtk_data->flags);
return err;
}
-static int btusb_mtk_setup(struct hci_dev *hdev)
+static int btusb_send_frame_mtk(struct hci_dev *hdev, struct sk_buff *skb)
{
- struct btusb_data *data = hci_get_drvdata(hdev);
- struct btmtk_hci_wmt_params wmt_params;
- ktime_t calltime, delta, rettime;
- struct btmtk_tci_sleep tci_sleep;
- unsigned long long duration;
- struct sk_buff *skb;
- const char *fwname;
- int err, status;
- u32 dev_id = 0;
- char fw_bin_name[64];
- u32 fw_version = 0, fw_flavor = 0;
- u8 param;
- struct btmediatek_data *mediatek;
-
- calltime = ktime_get();
-
- err = btusb_mtk_id_get(data, 0x80000008, &dev_id);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get device id (%d)", err);
- return err;
- }
-
- if (!dev_id || dev_id != 0x7663) {
- err = btusb_mtk_id_get(data, 0x70010200, &dev_id);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get device id (%d)", err);
- return err;
- }
- err = btusb_mtk_id_get(data, 0x80021004, &fw_version);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get fw version (%d)", err);
- return err;
- }
- err = btusb_mtk_id_get(data, 0x70010020, &fw_flavor);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to get fw flavor (%d)", err);
- return err;
- }
- fw_flavor = (fw_flavor & 0x00000080) >> 7;
- }
-
- mediatek = hci_get_priv(hdev);
- mediatek->dev_id = dev_id;
- mediatek->reset_sync = btusb_mtk_reset;
-
- err = btmtk_register_coredump(hdev, btusb_driver.name, fw_version);
- if (err < 0)
- bt_dev_err(hdev, "Failed to register coredump (%d)", err);
-
- switch (dev_id) {
- case 0x7663:
- fwname = FIRMWARE_MT7663;
- break;
- case 0x7668:
- fwname = FIRMWARE_MT7668;
- break;
- case 0x7922:
- case 0x7961:
- case 0x7925:
- if (dev_id == 0x7925)
- snprintf(fw_bin_name, sizeof(fw_bin_name),
- "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
- dev_id & 0xffff, dev_id & 0xffff, (fw_version & 0xff) + 1);
- else if (dev_id == 0x7961 && fw_flavor)
- snprintf(fw_bin_name, sizeof(fw_bin_name),
- "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
- dev_id & 0xffff, (fw_version & 0xff) + 1);
- else
- snprintf(fw_bin_name, sizeof(fw_bin_name),
- "mediatek/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
- dev_id & 0xffff, (fw_version & 0xff) + 1);
-
- err = btmtk_setup_firmware_79xx(hdev, fw_bin_name,
- btusb_mtk_hci_wmt_sync);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to set up firmware (%d)", err);
- return err;
- }
-
- /* It's Device EndPoint Reset Option Register */
- btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
-
- /* Enable Bluetooth protocol */
- param = 1;
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = NULL;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
-
- hci_set_msft_opcode(hdev, 0xFD30);
- hci_set_aosp_capable(hdev);
- goto done;
- default:
- bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
- dev_id);
- return -ENODEV;
- }
-
- /* Query whether the firmware is already download */
- wmt_params.op = BTMTK_WMT_SEMAPHORE;
- wmt_params.flag = 1;
- wmt_params.dlen = 0;
- wmt_params.data = NULL;
- wmt_params.status = &status;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
- return err;
- }
-
- if (status == BTMTK_WMT_PATCH_DONE) {
- bt_dev_info(hdev, "firmware already downloaded");
- goto ignore_setup_fw;
- }
-
- /* Setup a firmware which the device definitely requires */
- err = btmtk_setup_firmware(hdev, fwname,
- btusb_mtk_hci_wmt_sync);
- if (err < 0)
- return err;
-
-ignore_setup_fw:
- err = readx_poll_timeout(btusb_mtk_func_query, hdev, status,
- status < 0 || status != BTMTK_WMT_ON_PROGRESS,
- 2000, 5000000);
- /* -ETIMEDOUT happens */
- if (err < 0)
- return err;
-
- /* The other errors happen in btusb_mtk_func_query */
- if (status < 0)
- return status;
-
- if (status == BTMTK_WMT_ON_DONE) {
- bt_dev_info(hdev, "function already on");
- goto ignore_func_on;
- }
-
- /* Enable Bluetooth protocol */
- param = 1;
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = NULL;
+ struct urb *urb;
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
+ BT_DBG("%s", hdev->name);
-ignore_func_on:
- /* Apply the low power environment setup */
- tci_sleep.mode = 0x5;
- tci_sleep.duration = cpu_to_le16(0x640);
- tci_sleep.host_duration = cpu_to_le16(0x640);
- tci_sleep.host_wakeup_pin = 0;
- tci_sleep.time_compensation = 0;
+ if (hci_skb_pkt_type(skb) == HCI_ISODATA_PKT) {
+ urb = alloc_mtk_intr_urb(hdev, skb, btusb_tx_complete);
+ if (IS_ERR(urb))
+ return PTR_ERR(urb);
- skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
- HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
- return err;
+ return submit_or_queue_tx_urb(hdev, urb);
+ } else {
+ return btusb_send_frame(hdev, skb);
}
- kfree_skb(skb);
-
-done:
- rettime = ktime_get();
- delta = ktime_sub(rettime, calltime);
- duration = (unsigned long long)ktime_to_ns(delta) >> 10;
-
- bt_dev_info(hdev, "Device setup in %llu usecs", duration);
-
- return 0;
}
-static int btusb_mtk_shutdown(struct hci_dev *hdev)
+static int btusb_mtk_setup(struct hci_dev *hdev)
{
- struct btmtk_hci_wmt_params wmt_params;
- u8 param = 0;
- int err;
-
- /* Disable the device */
- wmt_params.op = BTMTK_WMT_FUNC_CTRL;
- wmt_params.flag = 0;
- wmt_params.dlen = sizeof(param);
- wmt_params.data = &param;
- wmt_params.status = NULL;
-
- err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
- if (err < 0) {
- bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
- return err;
- }
+ struct btusb_data *data = hci_get_drvdata(hdev);
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
- return 0;
+ /* MediaTek WMT vendor cmd requiring below USB resources to
+ * complete the handshake.
+ */
+ btmtk_data->drv_name = btusb_driver.name;
+ btmtk_data->intf = data->intf;
+ btmtk_data->udev = data->udev;
+ btmtk_data->ctrl_anchor = &data->ctrl_anchor;
+ btmtk_data->reset_sync = btusb_mtk_reset;
+
+ /* Claim ISO data interface and endpoint */
+ btmtk_data->isopkt_intf = usb_ifnum_to_if(data->udev, MTK_ISO_IFNUM);
+ if (btmtk_data->isopkt_intf)
+ btusb_mtk_claim_iso_intf(data);
+
+ return btmtk_usb_setup(hdev);
}
-static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
+static int btusb_mtk_shutdown(struct hci_dev *hdev)
{
struct btusb_data *data = hci_get_drvdata(hdev);
- u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
-
- switch (handle) {
- case 0xfc6f: /* Firmware dump from device */
- /* When the firmware hangs, the device can no longer
- * suspend and thus disable auto-suspend.
- */
- usb_disable_autosuspend(data->udev);
+ struct btmtk_data *btmtk_data = hci_get_priv(hdev);
- /* We need to forward the diagnostic packet to userspace daemon
- * for backward compatibility, so we have to clone the packet
- * extraly for the in-kernel coredump support.
- */
- if (IS_ENABLED(CONFIG_DEV_COREDUMP)) {
- struct sk_buff *skb_cd = skb_clone(skb, GFP_ATOMIC);
+ if (test_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags))
+ btusb_mtk_release_iso_intf(data);
- if (skb_cd)
- btmtk_process_coredump(hdev, skb_cd);
- }
-
- fallthrough;
- case 0x05ff: /* Firmware debug logging 1 */
- case 0x05fe: /* Firmware debug logging 2 */
- return hci_recv_diag(hdev, skb);
- }
-
- return hci_recv_frame(hdev, skb);
+ return btmtk_usb_shutdown(hdev);
}
#ifdef CONFIG_PM
@@ -4347,7 +3759,7 @@ static int btusb_probe(struct usb_interface *intf,
data->recv_event = btusb_recv_event_realtek;
} else if (id->driver_info & BTUSB_MEDIATEK) {
/* Allocate extra space for Mediatek device */
- priv_size += sizeof(struct btmediatek_data);
+ priv_size += sizeof(struct btmtk_data);
}
data->recv_acl = hci_recv_frame;
@@ -4451,9 +3863,12 @@ static int btusb_probe(struct usb_interface *intf,
hdev->manufacturer = 70;
hdev->cmd_timeout = btmtk_reset_sync;
hdev->set_bdaddr = btmtk_set_bdaddr;
+ hdev->send = btusb_send_frame_mtk;
set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
- data->recv_acl = btusb_recv_acl_mtk;
+ data->recv_acl = btmtk_usb_recv_acl;
+ data->suspend = btmtk_usb_suspend;
+ data->resume = btmtk_usb_resume;
}
if (id->driver_info & BTUSB_SWAVE) {
@@ -4694,6 +4109,9 @@ static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
cancel_work_sync(&data->work);
+ if (data->suspend)
+ data->suspend(data->hdev);
+
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
@@ -4797,6 +4215,9 @@ static int btusb_resume(struct usb_interface *intf)
btusb_submit_isoc_urb(hdev, GFP_NOIO);
}
+ if (data->resume)
+ data->resume(hdev);
+
spin_lock_irq(&data->txlock);
play_deferred(data);
clear_bit(BTUSB_SUSPENDING, &data->flags);