From 918e660a58b59632f1c61e7939f1e72f3f15c089 Mon Sep 17 00:00:00 2001 From: Robert Nelson Date: Fri, 28 Apr 2023 16:31:28 -0500 Subject: [PATCH] cleanup: remove random BT_HCIUART_RTL3WIRE driver, sync back with v5.10.113 Signed-off-by: Robert Nelson --- arch/riscv/configs/beaglev_defconfig | 1 - drivers/bluetooth/Kconfig | 8 - drivers/bluetooth/Makefile | 2 - drivers/bluetooth/hci_bcm.c | 1 - drivers/bluetooth/hci_h4.c | 351 ++- drivers/bluetooth/hci_ldisc.c | 1167 ++++------ drivers/bluetooth/hci_rtk_h5.c | 908 -------- drivers/bluetooth/hci_uart.h | 189 +- drivers/bluetooth/rtk_coex.c | 3068 -------------------------- drivers/bluetooth/rtk_coex.h | 373 ---- 10 files changed, 661 insertions(+), 5407 deletions(-) delete mode 100644 drivers/bluetooth/hci_rtk_h5.c delete mode 100644 drivers/bluetooth/rtk_coex.c delete mode 100644 drivers/bluetooth/rtk_coex.h diff --git a/arch/riscv/configs/beaglev_defconfig b/arch/riscv/configs/beaglev_defconfig index bca731907..783d5c1b6 100644 --- a/arch/riscv/configs/beaglev_defconfig +++ b/arch/riscv/configs/beaglev_defconfig @@ -1319,7 +1319,6 @@ CONFIG_BT_DEBUGFS=y # CONFIG_BT_HCIBTSDIO is not set CONFIG_BT_HCIUART=y CONFIG_BT_HCIUART_H4=y -CONFIG_BT_HCIUART_RTL3WIRE=y # CONFIG_BT_HCIUART_BCSP is not set # CONFIG_BT_HCIUART_ATH3K is not set # CONFIG_BT_HCIUART_INTEL is not set diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index adb99dd1d..4e73a531b 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -115,14 +115,6 @@ config BT_HCIUART_H4 Say Y here to compile support for HCI UART (H4) protocol. -config BT_HCIUART_RTL3WIRE - bool "Realtek Three-wire UART (H5) protocol support" - depends on BT_HCIUART - help - Realtek Three-wire UART (H5) transport layer makes it possible - to use Realtek Bluetooth controller with Three-wire UART. - Say Y here to compile support for Realtek Three-wire UART. - config BT_HCIUART_NOKIA tristate "UART Nokia H4+ protocol support" depends on BT_HCIUART diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 63b85bf1e..1a58a3ae1 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -45,6 +45,4 @@ hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o -hci_uart-y += rtk_coex.o -hci_uart-$(CONFIG_BT_HCIUART_RTL3WIRE) += hci_rtk_h5.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 3de0ce538..259a64337 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -654,7 +654,6 @@ static const struct h4_recv_pkt bcm_recv_pkts[] = { { H4_RECV_ACL, .recv = hci_recv_frame }, { H4_RECV_SCO, .recv = hci_recv_frame }, { H4_RECV_EVENT, .recv = hci_recv_frame }, - { H4_RECV_ISO, .recv = hci_recv_frame }, { BCM_RECV_LM_DIAG, .recv = hci_recv_diag }, { BCM_RECV_NULL, .recv = hci_recv_diag }, { BCM_RECV_TYPE49, .recv = hci_recv_diag }, diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 580bdad53..4b3b14a34 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * * Bluetooth HCI UART driver @@ -5,25 +6,10 @@ * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include + #include #include #include @@ -31,6 +17,7 @@ #include #include #include + #include #include #include @@ -38,32 +25,18 @@ #include #include #include +#include + #include #include -#include #include "hci_uart.h" -#ifdef BTCOEX -#include "rtk_coex.h" -#endif - -//#define VERSION "1.2" - struct h4_struct { - unsigned long rx_state; - unsigned long rx_count; struct sk_buff *rx_skb; struct sk_buff_head txq; }; -/* H4 receiver States */ -#define H4_W4_PACKET_TYPE 0 -#define H4_W4_EVENT_HDR 1 -#define H4_W4_ACL_HDR 2 -#define H4_W4_SCO_HDR 3 -#define H4_W4_DATA 4 - /* Initialize protocol */ static int h4_open(struct hci_uart *hu) { @@ -71,7 +44,7 @@ static int h4_open(struct hci_uart *hu) BT_DBG("hu %p", hu); - h4 = kzalloc(sizeof(*h4), GFP_ATOMIC); + h4 = kzalloc(sizeof(*h4), GFP_KERNEL); if (!h4) return -ENOMEM; @@ -98,8 +71,6 @@ static int h4_close(struct hci_uart *hu) { struct h4_struct *h4 = hu->priv; - hu->priv = NULL; - BT_DBG("hu %p", hu); skb_queue_purge(&h4->txq); @@ -112,7 +83,7 @@ static int h4_close(struct hci_uart *hu) return 0; } -/* Enqueue frame for transmittion (padding, crc, etc) */ +/* Enqueue frame for transmission (padding, crc, etc) */ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct h4_struct *h4 = hu->priv; @@ -120,174 +91,34 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); skb_queue_tail(&h4->txq, skb); return 0; } -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) -static inline int h4_check_data_len(struct h4_struct *h4, int len) -#else -static inline int h4_check_data_len(struct hci_dev *hdev, struct h4_struct *h4, int len) -#endif -{ - register int room = skb_tailroom(h4->rx_skb); - - BT_DBG("len %d room %d", len, room); - - if (!len) { -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - hci_recv_frame(h4->rx_skb); -#else - hci_recv_frame(hdev, h4->rx_skb); -#endif - } else if (len > room) { - BT_ERR("Data length is too large"); - kfree_skb(h4->rx_skb); - } else { - h4->rx_state = H4_W4_DATA; - h4->rx_count = len; - return len; - } - - h4->rx_state = H4_W4_PACKET_TYPE; - h4->rx_skb = NULL; - h4->rx_count = 0; - - return 0; -} +static const struct h4_recv_pkt h4_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { H4_RECV_ISO, .recv = hci_recv_frame }, +}; /* Recv data */ -static int h4_recv(struct hci_uart *hu, void *data, int count) +static int h4_recv(struct hci_uart *hu, const void *data, int count) { struct h4_struct *h4 = hu->priv; - register char *ptr; - struct hci_event_hdr *eh; - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - register int len, type, dlen; - BT_DBG("hu %p count %d rx_state %ld rx_count %ld", - hu, count, h4->rx_state, h4->rx_count); + if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) + return -EUNATCH; - ptr = data; - while (count) { - if (h4->rx_count) { - len = min_t(unsigned int, h4->rx_count, count); - memcpy(skb_put(h4->rx_skb, len), ptr, len); - h4->rx_count -= len; count -= len; ptr += len; - - if (h4->rx_count) - continue; - - switch (h4->rx_state) { - case H4_W4_DATA: - BT_DBG("Complete data"); -#ifdef BTCOEX - if(bt_cb(h4->rx_skb)->pkt_type == HCI_EVENT_PKT) - rtk_btcoex_parse_event( - h4->rx_skb->data, - h4->rx_skb->len); - - if(bt_cb(h4->rx_skb)->pkt_type == HCI_ACLDATA_PKT) - rtk_btcoex_parse_l2cap_data_rx( - h4->rx_skb->data, - h4->rx_skb->len); -#endif - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - hci_recv_frame(h4->rx_skb); -#else - hci_recv_frame(hu->hdev, h4->rx_skb); -#endif - - h4->rx_state = H4_W4_PACKET_TYPE; - h4->rx_skb = NULL; - continue; - - case H4_W4_EVENT_HDR: - eh = hci_event_hdr(h4->rx_skb); - - BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - h4_check_data_len(h4, eh->plen); -#else - h4_check_data_len(hu->hdev, h4, eh->plen); -#endif - continue; - - case H4_W4_ACL_HDR: - ah = hci_acl_hdr(h4->rx_skb); - dlen = __le16_to_cpu(ah->dlen); - - BT_DBG("ACL header: dlen %d", dlen); - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - h4_check_data_len(h4, dlen); -#else - h4_check_data_len(hu->hdev, h4, dlen); -#endif - continue; - - case H4_W4_SCO_HDR: - sh = hci_sco_hdr(h4->rx_skb); - - BT_DBG("SCO header: dlen %d", sh->dlen); - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - h4_check_data_len(h4, sh->dlen); -#else - h4_check_data_len(hu->hdev, h4, sh->dlen); -#endif - continue; - } - } - - /* H4_W4_PACKET_TYPE */ - switch (*ptr) { - case HCI_EVENT_PKT: - BT_DBG("Event packet"); - h4->rx_state = H4_W4_EVENT_HDR; - h4->rx_count = HCI_EVENT_HDR_SIZE; - type = HCI_EVENT_PKT; - break; - - case HCI_ACLDATA_PKT: - BT_DBG("ACL packet"); - h4->rx_state = H4_W4_ACL_HDR; - h4->rx_count = HCI_ACL_HDR_SIZE; - type = HCI_ACLDATA_PKT; - break; - - case HCI_SCODATA_PKT: - BT_DBG("SCO packet"); - h4->rx_state = H4_W4_SCO_HDR; - h4->rx_count = HCI_SCO_HDR_SIZE; - type = HCI_SCODATA_PKT; - break; - - default: - BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); - hu->hdev->stat.err_rx++; - ptr++; count--; - continue; - }; - - ptr++; count--; - - /* Allocate packet */ - h4->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!h4->rx_skb) { - BT_ERR("Can't allocate mem for new packet"); - h4->rx_state = H4_W4_PACKET_TYPE; - h4->rx_count = 0; - return -ENOMEM; - } - - h4->rx_skb->dev = (void *) hu->hdev; - bt_cb(h4->rx_skb)->pkt_type = type; + h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count, + h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); + if (IS_ERR(h4->rx_skb)) { + int err = PTR_ERR(h4->rx_skb); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); + h4->rx_skb = NULL; + return err; } return count; @@ -299,8 +130,9 @@ static struct sk_buff *h4_dequeue(struct hci_uart *hu) return skb_dequeue(&h4->txq); } -static struct hci_uart_proto h4p = { +static const struct hci_uart_proto h4p = { .id = HCI_UART_H4, + .name = "H4", .open = h4_open, .close = h4_close, .recv = h4_recv, @@ -311,17 +143,132 @@ static struct hci_uart_proto h4p = { int __init h4_init(void) { - int err = hci_uart_register_proto(&h4p); - - if (!err) - BT_INFO("HCI H4 protocol initialized"); - else - BT_ERR("HCI H4 protocol registration failed"); - - return err; + return hci_uart_register_proto(&h4p); } int __exit h4_deinit(void) { return hci_uart_unregister_proto(&h4p); } + +struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, + const unsigned char *buffer, int count, + const struct h4_recv_pkt *pkts, int pkts_count) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + u8 alignment = hu->alignment ? hu->alignment : 1; + + /* Check for error from previous call */ + if (IS_ERR(skb)) + skb = NULL; + + while (count) { + int i, len; + + /* remove padding bytes from buffer */ + for (; hu->padding && count > 0; hu->padding--) { + count--; + buffer++; + } + if (!count) + break; + + if (!skb) { + for (i = 0; i < pkts_count; i++) { + if (buffer[0] != (&pkts[i])->type) + continue; + + skb = bt_skb_alloc((&pkts[i])->maxlen, + GFP_ATOMIC); + if (!skb) + return ERR_PTR(-ENOMEM); + + hci_skb_pkt_type(skb) = (&pkts[i])->type; + hci_skb_expect(skb) = (&pkts[i])->hlen; + break; + } + + /* Check for invalid packet type */ + if (!skb) + return ERR_PTR(-EILSEQ); + + count -= 1; + buffer += 1; + } + + len = min_t(uint, hci_skb_expect(skb) - skb->len, count); + skb_put_data(skb, buffer, len); + + count -= len; + buffer += len; + + /* Check for partial packet */ + if (skb->len < hci_skb_expect(skb)) + continue; + + for (i = 0; i < pkts_count; i++) { + if (hci_skb_pkt_type(skb) == (&pkts[i])->type) + break; + } + + if (i >= pkts_count) { + kfree_skb(skb); + return ERR_PTR(-EILSEQ); + } + + if (skb->len == (&pkts[i])->hlen) { + u16 dlen; + + switch ((&pkts[i])->lsize) { + case 0: + /* No variable data length */ + dlen = 0; + break; + case 1: + /* Single octet variable length */ + dlen = skb->data[(&pkts[i])->loff]; + hci_skb_expect(skb) += dlen; + + if (skb_tailroom(skb) < dlen) { + kfree_skb(skb); + return ERR_PTR(-EMSGSIZE); + } + break; + case 2: + /* Double octet variable length */ + dlen = get_unaligned_le16(skb->data + + (&pkts[i])->loff); + hci_skb_expect(skb) += dlen; + + if (skb_tailroom(skb) < dlen) { + kfree_skb(skb); + return ERR_PTR(-EMSGSIZE); + } + break; + default: + /* Unsupported variable length */ + kfree_skb(skb); + return ERR_PTR(-EILSEQ); + } + + if (!dlen) { + hu->padding = (skb->len - 1) % alignment; + hu->padding = (alignment - hu->padding) % alignment; + + /* No more data, complete frame */ + (&pkts[i])->recv(hdev, skb); + skb = NULL; + } + } else { + hu->padding = (skb->len - 1) % alignment; + hu->padding = (alignment - hu->padding) % alignment; + + /* Complete frame */ + (&pkts[i])->recv(hdev, skb); + skb = NULL; + } + } + + return skb; +} +EXPORT_SYMBOL_GPL(h4_recv_buf); diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 9c3d76e5d..637c5b8c2 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * * Bluetooth HCI UART driver @@ -5,25 +6,10 @@ * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include + #include #include #include @@ -31,6 +17,7 @@ #include #include #include + #include #include #include @@ -38,51 +25,21 @@ #include #include #include -#include +#include +#include + #include #include +#include "btintel.h" +#include "btbcm.h" #include "hci_uart.h" -#define NEW_TX_SCHED_POLICY +#define VERSION "2.3" -#if WOBT_NOTIFY -#include -#endif +static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; -#ifdef BTCOEX -#include "rtk_coex.h" -#endif - -#define VERSION "2.2.0c90be4.20211102-175223" - -#if HCI_VERSION_CODE > KERNEL_VERSION(3, 4, 0) -#define GET_DRV_DATA(x) hci_get_drvdata(x) -#else -#define GET_DRV_DATA(x) (struct hci_uart *)(x->driver_data) -#endif - -#define SEMWAIT_TIMEOUT 50 - -#if WOBT_NOTIFY -struct hci_rsp_read_local { - __u8 status; - __u8 hci_ver; - __le16 hci_rev; - __u8 lmp_ver; - __le16 manufacturer; - __le16 lmp_subver; -} __packed; -#endif - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) -static int reset = 0; -#endif - -static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO]; -static int hci_uart_flush(struct hci_dev *hdev); - -int hci_uart_register_proto(struct hci_uart_proto *p) +int hci_uart_register_proto(const struct hci_uart_proto *p) { if (p->id >= HCI_UART_MAX_PROTO) return -EINVAL; @@ -92,10 +49,12 @@ int hci_uart_register_proto(struct hci_uart_proto *p) hup[p->id] = p; + BT_INFO("HCI UART protocol %s registered", p->name); + return 0; } -int hci_uart_unregister_proto(struct hci_uart_proto *p) +int hci_uart_unregister_proto(const struct hci_uart_proto *p) { if (p->id >= HCI_UART_MAX_PROTO) return -EINVAL; @@ -108,7 +67,7 @@ int hci_uart_unregister_proto(struct hci_uart_proto *p) return 0; } -static struct hci_uart_proto *hci_uart_get_proto(unsigned int id) +static const struct hci_uart_proto *hci_uart_get_proto(unsigned int id) { if (id >= HCI_UART_MAX_PROTO) return NULL; @@ -136,79 +95,17 @@ static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) } } -static inline void hci_proto_read_lock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - percpu_down_read(&hu->proto_lock); -#else - down_read(&hu->proto_lock); -#endif -} - -static inline int hci_proto_read_trylock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - return percpu_down_read_trylock(&hu->proto_lock); -#else - return down_read_trylock(&hu->proto_lock); -#endif -} - -static inline void hci_proto_read_unlock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - percpu_up_read(&hu->proto_lock); -#else - up_read(&hu->proto_lock); -#endif -} - -static inline void hci_proto_write_lock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - percpu_down_write(&hu->proto_lock); -#else - down_write(&hu->proto_lock); -#endif -} - -static inline void hci_proto_write_unlock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - percpu_up_write(&hu->proto_lock); -#else - up_write(&hu->proto_lock); -#endif -} - -static inline int hci_proto_init_rwlock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - return percpu_init_rwsem(&hu->proto_lock); -#else - init_rwsem(&hu->proto_lock); - return 0; -#endif -} - -static inline void hci_proto_free_rwlock(struct hci_uart *hu) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - percpu_free_rwsem(&hu->proto_lock); -#endif -} - static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) { struct sk_buff *skb = hu->tx_skb; if (!skb) { - hci_proto_read_lock(hu); + percpu_down_read(&hu->proto_lock); if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) skb = hu->proto->dequeue(hu); - hci_proto_read_unlock(hu); + percpu_up_read(&hu->proto_lock); } else { hu->tx_skb = NULL; } @@ -216,60 +113,34 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) return skb; } -/* This may be called in an IRQ context */ int hci_uart_tx_wakeup(struct hci_uart *hu) { - /* If acquiring lock fails we assume the tty is being closed because - * that is the only time the write lock is acquired. If, however, - * at some point in the future the write lock is also acquired in - * other situations, then this must be revisited. + /* This may be called in an IRQ context, so we can't sleep. Therefore + * we try to acquire the lock only, and if that fails we assume the + * tty is being closed because that is the only time the write lock is + * acquired. If, however, at some point in the future the write lock + * is also acquired in other situations, then this must be revisited. */ - if (!hci_proto_read_trylock(hu)) + if (!percpu_down_read_trylock(&hu->proto_lock)) return 0; - /* proto_lock is locked */ if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) goto no_schedule; -#ifdef NEW_TX_SCHED_POLICY set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) goto no_schedule; -#else - if (in_interrupt() || in_atomic()) { - if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { - set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); - goto no_schedule; - } - } else { - /* NOTE: proto_lock can't be spin lock, because it may - * schedule here. Schedule is not allowed while atomic - */ - if (down_timeout(&hu->tx_sem, - msecs_to_jiffies(SEMWAIT_TIMEOUT)) == -ETIME) { - pr_warn("%s: Something went wrong with wait\n", - __func__); - goto no_schedule; - } - /* semaphore is locked */ - if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { - set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); - up(&hu->tx_sem); - goto no_schedule; - } - up(&hu->tx_sem); - } -#endif BT_DBG(""); schedule_work(&hu->write_work); no_schedule: - hci_proto_read_unlock(hu); + percpu_up_read(&hu->proto_lock); return 0; } +EXPORT_SYMBOL_GPL(hci_uart_tx_wakeup); static void hci_uart_write_work(struct work_struct *work) { @@ -282,7 +153,7 @@ static void hci_uart_write_work(struct work_struct *work) * and error value ? */ - restart: +restart: clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); while ((skb = hci_uart_dequeue(hu))) { @@ -298,33 +169,84 @@ static void hci_uart_write_work(struct work_struct *work) break; } - hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type); + hci_uart_tx_complete(hu, hci_skb_pkt_type(skb)); kfree_skb(skb); } -#ifdef NEW_TX_SCHED_POLICY clear_bit(HCI_UART_SENDING, &hu->tx_state); if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) goto restart; -#else - if (down_timeout(&hu->tx_sem, msecs_to_jiffies(SEMWAIT_TIMEOUT))) { - pr_warn("%s: Something went wrong with wait\n", __func__); - goto restart; - } - /* semaphore is locked */ - if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state)) { - up(&hu->tx_sem); - goto restart; + + wake_up_bit(&hu->tx_state, HCI_UART_SENDING); +} + +void hci_uart_init_work(struct work_struct *work) +{ + struct hci_uart *hu = container_of(work, struct hci_uart, init_ready); + int err; + struct hci_dev *hdev; + + if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) + return; + + err = hci_register_dev(hu->hdev); + if (err < 0) { + BT_ERR("Can't register HCI device"); + clear_bit(HCI_UART_PROTO_READY, &hu->flags); + hu->proto->close(hu); + hdev = hu->hdev; + hu->hdev = NULL; + hci_free_dev(hdev); + return; } - clear_bit(HCI_UART_SENDING, &hu->tx_state); - up(&hu->tx_sem); -#endif + set_bit(HCI_UART_REGISTERED, &hu->flags); +} - return; +int hci_uart_init_ready(struct hci_uart *hu) +{ + if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) + return -EALREADY; + + schedule_work(&hu->init_ready); + + return 0; +} + +int hci_uart_wait_until_sent(struct hci_uart *hu) +{ + return wait_on_bit_timeout(&hu->tx_state, HCI_UART_SENDING, + TASK_INTERRUPTIBLE, + msecs_to_jiffies(2000)); } /* ------- Interface to HCI layer ------ */ +/* Reset device */ +static int hci_uart_flush(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct tty_struct *tty = hu->tty; + + BT_DBG("hdev %p tty %p", hdev, tty); + + if (hu->tx_skb) { + kfree_skb(hu->tx_skb); hu->tx_skb = NULL; + } + + /* Flush any pending characters in the driver and discipline. */ + tty_ldisc_flush(tty); + tty_driver_flush_buffer(tty); + + percpu_down_read(&hu->proto_lock); + + if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) + hu->proto->flush(hu); + + percpu_up_read(&hu->proto_lock); + + return 0; +} + /* Initialize device */ static int hci_uart_open(struct hci_dev *hdev) { @@ -333,499 +255,218 @@ static int hci_uart_open(struct hci_dev *hdev) /* Undo clearing this from hci_uart_close() */ hdev->flush = hci_uart_flush; -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - set_bit(HCI_RUNNING, &hdev->flags); -#endif - -#ifdef BTCOEX - rtk_btcoex_open(hdev); -#endif - return 0; } -/* static void hci_flush_sync(struct hci_dev *hdev) - * { - * #if HCI_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) - * u8 buf[2] = { 0, 0 }; - * struct sk_buff *skb; - * - * BT_INFO("hci flush sync"); - * - * set_bit(HCI_INIT, &hdev->flags); - * skb = __hci_cmd_sync(hdev, 0xfc19, 2, buf, msecs_to_jiffies(2000)); - * clear_bit(HCI_INIT, &hdev->flags); - * - * if (IS_ERR(skb)) { - * BT_ERR("command 0xfc19 tx failed (%ld)\n", PTR_ERR(skb)); - * return; - * } - * - * if (skb->len == 1) - * BT_INFO("hci flush sync status %u", skb->data[0]); - * - * kfree_skb(skb); - * #endif - * } - */ - -static int __hci_uart_flush(struct hci_dev *hdev, u8 sync) -{ - struct hci_uart *hu = GET_DRV_DATA(hdev); //(struct hci_uart *) hdev->driver_data; - struct tty_struct *tty = hu->tty; - - BT_INFO("%s: hdev %p tty %p", __func__, hdev, tty); - - /* Make sure all HCI packets has been transmitted */ - /* if (sync && test_bit(HCI_RUNNING, &hdev->flags)) - * hci_flush_sync(hdev); - */ - - if (hu->tx_skb) { - kfree_skb(hu->tx_skb); - hu->tx_skb = NULL; - } - - /* Flush any pending characters in the driver and discipline. */ - /* tty_ldisc_flush(tty); - * tty_driver_flush_buffer(tty); - */ - /* Don't flush the tty. Sometime, the hdev is closed abnormally. - * There may be cmd complete event in rx buf or the sent ack in tx buf. - * tty flush will result in hciX: command 0xXXXX tx timeout - */ - tty_wait_until_sent(tty, msecs_to_jiffies(500)); - - hci_proto_read_lock(hu); - - if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) - hu->proto->flush(hu); - - hci_proto_read_unlock(hu); - - return 0; -} - -/* Reset device */ -static int hci_uart_flush(struct hci_dev *hdev) -{ - return __hci_uart_flush(hdev, 1); -} - /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { - BT_INFO("%s: hdev %p", __func__, hdev); - - /* When in kernel 4.4.0 and greater, the HCI_RUNNING bit is - * cleared in hci_dev_do_close(). */ -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) - return 0; -#else - if (test_bit(HCI_RUNNING, &hdev->flags)) - BT_ERR("HCI_RUNNING is not cleared before."); -#endif - - if (test_bit(HCI_RUNNING, &hdev->flags)) - __hci_uart_flush(hdev, 0); - else - __hci_uart_flush(hdev, 1); + BT_DBG("hdev %p", hdev); + hci_uart_flush(hdev); hdev->flush = NULL; - -#ifdef BTCOEX - rtk_btcoex_close(); -#endif - return 0; } /* Send frames from HCI layer */ -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) -int hci_uart_send_frame(struct sk_buff *skb) -#else -int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) -#endif +static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - struct hci_dev *hdev = (struct hci_dev *)skb->dev; -#endif - struct hci_uart *hu; + struct hci_uart *hu = hci_get_drvdata(hdev); - if (!hdev) { - BT_ERR("Frame for unknown device (hdev=NULL)"); - return -ENODEV; - } - -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return -EBUSY; -#endif - - hu = GET_DRV_DATA(hdev); //(struct hci_uart *) hdev->driver_data; - - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, + BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb), skb->len); -#ifdef BTCOEX - if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) - rtk_btcoex_parse_cmd(skb->data, skb->len); - if (bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT) - rtk_btcoex_parse_l2cap_data_tx(skb->data, skb->len); -#endif - - hci_proto_read_lock(hu); + percpu_down_read(&hu->proto_lock); if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) { - hci_proto_read_unlock(hu); + percpu_up_read(&hu->proto_lock); return -EUNATCH; } hu->proto->enqueue(hu, skb); - hci_proto_read_unlock(hu); + percpu_up_read(&hu->proto_lock); hci_uart_tx_wakeup(hu); return 0; } -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) -static void hci_uart_destruct(struct hci_dev *hdev) +/* Check the underlying device or tty has flow control support */ +bool hci_uart_has_flow_control(struct hci_uart *hu) { - if (!hdev) + /* serdev nodes check if the needed operations are present */ + if (hu->serdev) + return true; + + if (hu->tty->driver->ops->tiocmget && hu->tty->driver->ops->tiocmset) + return true; + + return false; +} + +/* Flow control or un-flow control the device */ +void hci_uart_set_flow_control(struct hci_uart *hu, bool enable) +{ + struct tty_struct *tty = hu->tty; + struct ktermios ktermios; + int status; + unsigned int set = 0; + unsigned int clear = 0; + + if (hu->serdev) { + serdev_device_set_flow_control(hu->serdev, !enable); + serdev_device_set_rts(hu->serdev, !enable); return; - - BT_DBG("%s", hdev->name); - kfree(hdev->driver_data); -} -#endif - -#if WOBT_NOTIFY -static int hci_uart_async_send(struct hci_uart *hu, u16 opcode, - u32 plen, const void *param) -{ - int len = HCI_COMMAND_HDR_SIZE + plen; - struct hci_command_hdr *hdr; - struct sk_buff *skb; - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE); - hdr->opcode = cpu_to_le16(opcode); - hdr->plen = plen; - - if (plen) - memcpy(skb_put(skb, plen), param, plen); - - BT_INFO("rtl: skb len %d", skb->len); - - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - bt_cb(skb)->opcode = opcode; -#else - bt_cb(skb)->hci.opcode = opcode; -#endif -#endif - - /* Stand-alone HCI commands must be flagged as - * single-command requests. - */ -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - bt_cb(skb)->req.start = true; -#else - -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 5, 0) - bt_cb(skb)->hci.req_start = true; -#else - - bt_cb(skb)->hci.req_flags |= HCI_REQ_START; -#endif -#endif /* 4.4.0 */ -#endif /* 3.10.0 */ - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - hci_uart_send_frame(skb); -#else - hci_uart_send_frame(hu->hdev, skb); -#endif - - /* hci_proto_read_lock(hu); - - * if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) { - * hci_proto_read_unlock(hu); - * BT_ERR("rtl send: proto not ready"); - * return -EUNATCH; - * } - - * hu->proto->enqueue(hu, skb); - * hci_proto_read_unlock(hu); - - * hci_uart_tx_wakeup(hu); - */ - - return 0; -} - -static int rtl_read_local_version(struct hci_dev *hdev, u8 *hci_ver, - u16 *hci_rev, u16 *lmp_subver) -{ - struct hci_rsp_read_local *ver; - struct sk_buff *skb; - - skb = __hci_cmd_sync(hdev, 0x1001, 0, NULL, HCI_INIT_TIMEOUT); - if (IS_ERR(skb)) { - BT_ERR("rtl: Could not read lmp subversion"); - return PTR_ERR(skb); } - if (skb->len != sizeof(struct hci_rsp_read_local)) { - BT_ERR("%s: rtl: Local version length mismatch", hdev->name); - kfree_skb(skb); - return -EIO; - } + if (enable) { + /* Disable hardware flow control */ + ktermios = tty->termios; + ktermios.c_cflag &= ~CRTSCTS; + status = tty_set_termios(tty, &ktermios); + BT_DBG("Disabling hardware flow control: %s", + status ? "failed" : "success"); - ver = (struct hci_rsp_read_local *)skb->data; - *hci_ver = ver->hci_ver; - *hci_rev = le16_to_cpu(ver->hci_rev); - *lmp_subver = le16_to_cpu(ver->lmp_subver); + /* Clear RTS to prevent the device from sending */ + /* Most UARTs need OUT2 to enable interrupts */ + status = tty->driver->ops->tiocmget(tty); + BT_DBG("Current tiocm 0x%x", status); - kfree_skb(skb); - - return 0; -} - -#if RTKBT_TV_POWERON_WHITELIST -static int rtkbt_lookup_le_device_poweron_whitelist(struct hci_uart *hu) -{ - struct hci_conn_params *p; - u8 *params; - int result = 0; - - hci_dev_lock(hu->hdev); - list_for_each_entry(p, &hu->hdev->le_conn_params, list) { -#if 0 // for debug message - BT_INFO("%s(): auto_connect = %d", __FUNCTION__, p->auto_connect); - BT_INFO("%s(): addr_type = 0x%02x", __FUNCTION__, p->addr_type); - BT_INFO("%s(): addr=%02x:%02x:%02x:%02x:%02x:%02x", __FUNCTION__, - p->addr.b[5], p->addr.b[4], p->addr.b[3], - p->addr.b[2], p->addr.b[1], p->addr.b[0]); -#endif - if ( p->auto_connect == HCI_AUTO_CONN_ALWAYS && - p->addr_type == ADDR_LE_DEV_PUBLIC ) { - - BT_INFO("%s(): Set RTKBT LE Power-on Whitelist for " - "%02x:%02x:%02x:%02x:%02x:%02x", __FUNCTION__, - p->addr.b[5], p->addr.b[4], p->addr.b[3], - p->addr.b[2], p->addr.b[1], p->addr.b[0]); - - params = kzalloc(8, GFP_ATOMIC); - if (!params) { - BT_ERR("Can't allocate memory for params"); - return -ENOMEM; - } - - params[0] = 0x00; - params[1] = p->addr.b[0]; - params[2] = p->addr.b[1]; - params[3] = p->addr.b[2]; - params[4] = p->addr.b[3]; - params[5] = p->addr.b[4]; - params[6] = p->addr.b[5]; - - result = hci_uart_async_send(hu, 0xfc7b, 7, params); - if (result) - BT_ERR("rtl: Command failed for power-on whitelist"); - - msleep(500); - - kfree(params); - } - } - hci_dev_unlock(hu->hdev); - - return result; -} -#endif - -static int rtkbt_notify_suspend(struct hci_uart *hu) -{ - struct hci_conn *conn; - struct sk_buff *rx_skb; - u8 params_suspend_notify[1] = { 0x01 }; - u8 event_params[6] = { 0x05, 0x04, 0x00, 0x10, 0x00, 0x13 }; - int result = 0; - - result = hci_uart_async_send(hu, 0xfc28, 1, params_suspend_notify); - if (result) - BT_ERR("Realtek suspend h5-bt failed"); - - msleep(500); - - hci_dev_lock(hu->hdev); - - conn = hci_conn_hash_lookup_state(hu->hdev, LE_LINK, BT_CONNECTED); - if (conn && (conn->state == BT_CONNECTED)){ - rx_skb = alloc_skb(6, GFP_ATOMIC); - if (!rx_skb) - return -1; - - event_params[3] = (u8)(conn->handle); - event_params[4] = (u8)(conn->handle >> 8); - hci_skb_pkt_type(rx_skb) = HCI_EVENT_PKT; - skb_put_data(rx_skb, event_params, 6); - - BT_INFO("Send Disconnect Complete EVENT to upper stack"); - hci_recv_frame(hu->hdev, rx_skb); - } - - hci_dev_unlock(hu->hdev); - - return result; -} - -static void le_scan_disable(struct hci_uart *hu) -{ -#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) - if (use_ext_scan(hu->hdev)) { - u8 ext_enable_cp[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - hci_uart_async_send(hu, HCI_OP_LE_SET_EXT_SCAN_ENABLE, 6, ext_enable_cp); + set &= ~(TIOCM_OUT2 | TIOCM_RTS); + clear = ~set; + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | + TIOCM_OUT2 | TIOCM_LOOP; + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | + TIOCM_OUT2 | TIOCM_LOOP; + status = tty->driver->ops->tiocmset(tty, set, clear); + BT_DBG("Clearing RTS: %s", status ? "failed" : "success"); } else { - u8 enable_cp[2] = {0x00, 0x00}; + /* Set RTS to allow the device to send again */ + status = tty->driver->ops->tiocmget(tty); + BT_DBG("Current tiocm 0x%x", status); - hci_uart_async_send(hu, HCI_OP_LE_SET_SCAN_ENABLE, 2, enable_cp); + set |= (TIOCM_OUT2 | TIOCM_RTS); + clear = ~set; + set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | + TIOCM_OUT2 | TIOCM_LOOP; + clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 | + TIOCM_OUT2 | TIOCM_LOOP; + status = tty->driver->ops->tiocmset(tty, set, clear); + BT_DBG("Setting RTS: %s", status ? "failed" : "success"); + + /* Re-enable hardware flow control */ + ktermios = tty->termios; + ktermios.c_cflag |= CRTSCTS; + status = tty_set_termios(tty, &ktermios); + BT_DBG("Enabling hardware flow control: %s", + status ? "failed" : "success"); } -#else - u8 enable_cp[2] = {0x00, 0x00}; - - hci_uart_async_send(hu, HCI_OP_LE_SET_SCAN_ENABLE, 2, enable_cp); -#endif - - return; } -static void le_scan_restart(struct hci_uart *hu) +void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed, + unsigned int oper_speed) { - int result; -#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) - if (use_ext_scan(hu->hdev)) { - u8 ext_enable_cp[6] = { 0x01, 0x01, 0x00, 0x00, 0x00, 0x00}; - - BT_INFO("LE Extended Scan Restart..."); - le_scan_disable(hu); - result = hci_uart_async_send(hu, HCI_OP_LE_SET_EXT_SCAN_ENABLE, 6, ext_enable_cp); - if (result) - BT_ERR("LE Extended Scan Restart: Failed"); - } else { - u8 enable_cp[2] = {0x01, 0x01}; - - BT_INFO("LE Scan Restart..."); - le_scan_disable(hu); - result = hci_uart_async_send(hu, HCI_OP_LE_SET_SCAN_ENABLE, 2, enable_cp); - if (result) - BT_ERR("LE Scan Restart: Failed"); - } -#else - u8 enable_cp[2] = {0x01, 0x01}; - - BT_INFO("LE Scan Restart"); - le_scan_disable(hu); - result = hci_uart_async_send(hu, HCI_OP_LE_SET_SCAN_ENABLE, 2, enable_cp); - if (result) - BT_ERR("LE Scan Restart: Failed"); -#endif - return; + hu->init_speed = init_speed; + hu->oper_speed = oper_speed; } -static bool le_aoto_conn_always_exist(struct hci_uart *hu) +void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed) { - struct hci_conn_params *p; - bool ret = false; + struct tty_struct *tty = hu->tty; + struct ktermios ktermios; - hci_dev_lock(hu->hdev); - list_for_each_entry(p, &hu->hdev->le_conn_params, list) { - if ( p->auto_connect == HCI_AUTO_CONN_ALWAYS && - p->addr_type == ADDR_LE_DEV_PUBLIC ) { + ktermios = tty->termios; + ktermios.c_cflag &= ~CBAUD; + tty_termios_encode_baud_rate(&ktermios, speed, speed); - ret = true; - } + /* tty_set_termios() return not checked as it is always 0 */ + tty_set_termios(tty, &ktermios); + + BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name, + tty->termios.c_ispeed, tty->termios.c_ospeed); +} + +static int hci_uart_setup(struct hci_dev *hdev) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct hci_rp_read_local_version *ver; + struct sk_buff *skb; + unsigned int speed; + int err; + + /* Init speed if any */ + if (hu->init_speed) + speed = hu->init_speed; + else if (hu->proto->init_speed) + speed = hu->proto->init_speed; + else + speed = 0; + + if (speed) + hci_uart_set_baudrate(hu, speed); + + /* Operational speed if any */ + if (hu->oper_speed) + speed = hu->oper_speed; + else if (hu->proto->oper_speed) + speed = hu->proto->oper_speed; + else + speed = 0; + + if (hu->proto->set_baudrate && speed) { + err = hu->proto->set_baudrate(hu, speed); + if (!err) + hci_uart_set_baudrate(hu, speed); } - hci_dev_unlock(hu->hdev); - return ret; -} + if (hu->proto->setup) + return hu->proto->setup(hu); -static int hci_uart_pm_notifier(struct notifier_block *b, unsigned long v, void *d) -{ - int result; - struct hci_uart *hu = container_of(b, struct hci_uart, pm_notify_block); - u8 hci_ver = 0; - u16 hci_rev = 0; - u16 lmp_subver = 0; -#if WOBT_NOTIFY_BG_SCAN_LE_WHITELIST_ONLY - u8 params_bg_scan[5] = { 0x60, 0x01, 0x10, 0x00, 0x01 }; -#endif + if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags)) + return 0; - BT_INFO("%s: %lu", __func__, v); - switch (v) { - case PM_SUSPEND_PREPARE: - BT_INFO("rtl: bt suspending"); -#if WOBT_NOTIFY_BG_SCAN_LE_WHITELIST_ONLY - /* Send set back ground scan parameters to Controller for power-on mode */ - result = hci_uart_async_send(hu, 0xfc7a, 5, params_bg_scan); - if (result) - BT_ERR("Realtek bg-scan h5-bt failed"); - /* FIXME: Ensure the above vendor command is sent to Controller - * and we received the h5 ack from Controller - * */ - msleep(500); + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Reading local version information failed (%ld)", + hdev->name, PTR_ERR(skb)); + return 0; + } -#endif + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: Event length mismatch for version information", + hdev->name); + goto done; + } -#if RTKBT_TV_POWERON_WHITELIST - result = rtkbt_lookup_le_device_poweron_whitelist(hu); - if (result < 0) { - BT_ERR("rtkbt_lookup_le_device_poweron_whitelist error: %d", result); - } -#endif - - result = rtkbt_notify_suspend(hu); - if (result < 0) { - BT_ERR("rtkbt_notify_suspend error: %d", result); - } + ver = (struct hci_rp_read_local_version *)skb->data; + switch (le16_to_cpu(ver->manufacturer)) { +#ifdef CONFIG_BT_HCIUART_INTEL + case 2: + hdev->set_bdaddr = btintel_set_bdaddr; + btintel_check_bdaddr(hdev); break; - case PM_POST_SUSPEND: - result = rtl_read_local_version(hu->hdev, &hci_ver, &hci_rev, - &lmp_subver); - if (result) - break; - BT_INFO("rtl resume: hci ver %u, hci rev %04x, lmp subver %04x", - hci_ver, hci_rev, lmp_subver); - - if (le_aoto_conn_always_exist(hu)) - le_scan_restart(hu); - +#endif +#ifdef CONFIG_BT_HCIUART_BCM + case 15: + hdev->set_bdaddr = btbcm_set_bdaddr; + btbcm_check_bdaddr(hdev); break; +#endif default: - BT_INFO("Caught msg %lu other than SUSPEND_PREPARE", v); break; } +done: + kfree_skb(skb); return 0; } -#endif /* ------ LDISC part ------ */ /* hci_uart_tty_open * - * Called when line discipline changed to HCI_UART. + * Called when line discipline changed to HCI_UART. * * Arguments: * tty pointer to tty info structure @@ -834,33 +475,18 @@ static int hci_uart_pm_notifier(struct notifier_block *b, unsigned long v, void */ static int hci_uart_tty_open(struct tty_struct *tty) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu; BT_DBG("tty %p", tty); - /* But nothing ensures disc_data to be NULL. And since ld->ops->open - * shall be called only once, we do not need the check at all. - * So remove it. - * - * Note that this is not an issue now, but n_tty will start using the - * disc_data pointer and this invalid 'if' would trigger then rendering - * TTYs over BT unusable. - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) - /* FIXME: This btw is bogus, nothing requires the old ldisc to clear - * the pointer - */ - if (hu) - return -EEXIST; -#endif - /* Error if the tty has no write op instead of leaving an exploitable * hole */ if (tty->ops->write == NULL) return -EOPNOTSUPP; - if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) { + hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL); + if (!hu) { BT_ERR("Can't allocate control structure"); return -ENFILE; } @@ -869,39 +495,32 @@ static int hci_uart_tty_open(struct tty_struct *tty) hu->tty = tty; tty->receive_room = 65536; + /* disable alignment support by default */ + hu->alignment = 1; + hu->padding = 0; + + INIT_WORK(&hu->init_ready, hci_uart_init_work); INIT_WORK(&hu->write_work, hci_uart_write_work); - hci_proto_init_rwlock(hu); - sema_init(&hu->tx_sem, 1); + percpu_init_rwsem(&hu->proto_lock); - /* Flush any pending characters in the driver and line discipline. */ - - /* FIXME: why is this needed. Note don't use ldisc_ref here as the - open path is before the ldisc is referencable */ - - if (tty->ldisc->ops->flush_buffer) - tty->ldisc->ops->flush_buffer(tty); + /* Flush any pending characters in the driver */ tty_driver_flush_buffer(tty); -#if WOBT_NOTIFY - hu->pm_notify_block.notifier_call = hci_uart_pm_notifier; - register_pm_notifier(&hu->pm_notify_block); -#endif - return 0; } /* hci_uart_tty_close() * - * Called when the line discipline is changed to something - * else, the tty is closed, or the tty detects a hangup. + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. */ static void hci_uart_tty_close(struct tty_struct *tty) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; struct hci_dev *hdev; - BT_INFO("%s: tty %p", __func__, tty); + BT_DBG("tty %p", tty); /* Detach from the tty */ tty->disc_data = NULL; @@ -914,10 +533,11 @@ static void hci_uart_tty_close(struct tty_struct *tty) hci_uart_close(hdev); if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { - hci_proto_write_lock(hu); + percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); - hci_proto_write_unlock(hu); + percpu_up_write(&hu->proto_lock); + cancel_work_sync(&hu->init_ready); cancel_work_sync(&hu->write_work); if (hdev) { @@ -929,25 +549,22 @@ static void hci_uart_tty_close(struct tty_struct *tty) } clear_bit(HCI_UART_PROTO_SET, &hu->flags); - hci_proto_free_rwlock(hu); -#if WOBT_NOTIFY - unregister_pm_notifier(&hu->pm_notify_block); -#endif + percpu_free_rwsem(&hu->proto_lock); kfree(hu); } /* hci_uart_tty_wakeup() * - * Callback for transmit wakeup. Called when low level - * device driver can accept more send data. + * Callback for transmit wakeup. Called when low level + * device driver can accept more send data. * * Arguments: tty pointer to associated tty instance data * Return Value: None */ static void hci_uart_tty_wakeup(struct tty_struct *tty) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; BT_DBG(""); @@ -965,8 +582,8 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) /* hci_uart_tty_receive() * - * Called by tty low level driver when receive data is - * available. + * Called by tty low level driver when receive data is + * available. * * Arguments: tty pointer to tty isntance data * data pointer to received data @@ -975,33 +592,26 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty) * * Return Value: None */ -static void hci_uart_tty_receive(struct tty_struct *tty, const u8 * data, +static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count) { - struct hci_uart *hu = (void *)tty->disc_data; - int (*proto_receive)(struct hci_uart *hu, void *data, int len); + struct hci_uart *hu = tty->disc_data; if (!hu || tty != hu->tty) return; - hci_proto_read_lock(hu); + percpu_down_read(&hu->proto_lock); if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) { - hci_proto_read_unlock(hu); + percpu_up_read(&hu->proto_lock); return; } - proto_receive = hu->proto->recv; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0) - proto_receive(hu, (void *)data, count); - hci_proto_read_unlock(hu); -#else - hci_proto_read_unlock(hu); /* It does not need a lock here as it is already protected by a mutex in * tty caller */ - proto_receive(hu, (void *)data, count); -#endif + hu->proto->recv(hu, data, count); + percpu_up_read(&hu->proto_lock); if (hu->hdev) hu->hdev->stat.byte_rx += count; @@ -1012,8 +622,9 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 * data, static int hci_uart_register_dev(struct hci_uart *hu) { struct hci_dev *hdev; + int err; - BT_INFO("hci_uart_register_dev"); + BT_DBG(""); /* Initialize and register HCI device */ hdev = hci_alloc_dev(); @@ -1024,124 +635,91 @@ static int hci_uart_register_dev(struct hci_uart *hu) hu->hdev = hdev; -#if HCI_VERSION_CODE > KERNEL_VERSION(2, 6, 33) hdev->bus = HCI_UART; -#else - hdev->type = HCI_UART; -#endif - -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) hci_set_drvdata(hdev, hu); -#else - hdev->driver_data = hu; -#endif - hdev->open = hci_uart_open; + /* Only when vendor specific setup callback is provided, consider + * the manufacturer information valid. This avoids filling in the + * value for Ericsson when nothing is specified. + */ + if (hu->proto->setup) + hdev->manufacturer = hu->proto->manufacturer; + + hdev->open = hci_uart_open; hdev->close = hci_uart_close; hdev->flush = hci_uart_flush; - hdev->send = hci_uart_send_frame; - - /* NOTE: No hdev->setup setting for Realtek BTUART because - * the download procedure is done with rtk_hciattach in userspace - * before this function called in hci_uart_set_proto() - */ - + hdev->send = hci_uart_send_frame; + hdev->setup = hci_uart_setup; SET_HCIDEV_DEV(hdev, hu->tty->dev); -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - hdev->destruct = hci_uart_destruct; - hdev->owner = THIS_MODULE; -#endif - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) - if (!reset) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); -#endif - -#if HCI_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); -#endif -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); -#endif -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); -#else - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); -#endif -#endif -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) hdev->dev_type = HCI_AMP; else -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 8, 0) - hdev->dev_type = HCI_BREDR; -#else hdev->dev_type = HCI_PRIMARY; -#endif -#endif -#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) - set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); -#endif + /* Only call open() for the protocol after hdev is fully initialized as + * open() (or a timer/workqueue it starts) may attempt to reference it. + */ + err = hu->proto->open(hu); + if (err) { + hu->hdev = NULL; + hci_free_dev(hdev); + return err; + } + + if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags)) + return 0; if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); + hu->proto->close(hu); + hu->hdev = NULL; hci_free_dev(hdev); return -ENODEV; } set_bit(HCI_UART_REGISTERED, &hu->flags); -#ifdef BTCOEX - rtk_btcoex_probe(hdev); -#endif - return 0; } static int hci_uart_set_proto(struct hci_uart *hu, int id) { - struct hci_uart_proto *p; + const struct hci_uart_proto *p; int err; p = hci_uart_get_proto(id); if (!p) return -EPROTONOSUPPORT; - err = p->open(hu); - if (err) - return err; - hu->proto = p; - set_bit(HCI_UART_PROTO_READY, &hu->flags); - /* Initialize and register HCI dev */ err = hci_uart_register_dev(hu); if (err) { - clear_bit(HCI_UART_PROTO_READY, &hu->flags); - p->close(hu); return err; } + set_bit(HCI_UART_PROTO_READY, &hu->flags); return 0; } -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) { - /* TODO: Add HCI_UART_INIT_PENDING, HCI_UART_VND_DETECT check */ unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) | BIT(HCI_UART_RESET_ON_INIT) | BIT(HCI_UART_CREATE_AMP) | - BIT(HCI_UART_EXT_CONFIG); + BIT(HCI_UART_INIT_PENDING) | + BIT(HCI_UART_EXT_CONFIG) | + BIT(HCI_UART_VND_DETECT); if (flags & ~valid_flags) return -EINVAL; @@ -1150,7 +728,6 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) return 0; } -#endif /* hci_uart_tty_ioctl() * @@ -1168,7 +745,7 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - struct hci_uart *hu = (void *)tty->disc_data; + struct hci_uart *hu = tty->disc_data; int err = 0; BT_DBG(""); @@ -1181,43 +758,41 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, case HCIUARTSETPROTO: if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) { err = hci_uart_set_proto(hu, arg); - if (err) { + if (err) clear_bit(HCI_UART_PROTO_SET, &hu->flags); - return err; - } } else - return -EBUSY; + err = -EBUSY; break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return hu->proto->id; - return -EUNATCH; + err = hu->proto->id; + else + err = -EUNATCH; + break; case HCIUARTGETDEVICE: if (test_bit(HCI_UART_REGISTERED, &hu->flags)) - return hu->hdev->id; - return -EUNATCH; + err = hu->hdev->id; + else + err = -EUNATCH; + break; case HCIUARTSETFLAGS: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return -EBUSY; -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) - err = hci_uart_set_flags(hu, arg); - if (err) - return err; -#else - hu->hdev_flags = arg; -#endif + err = -EBUSY; + else + err = hci_uart_set_flags(hu, arg); break; case HCIUARTGETFLAGS: - return hu->hdev_flags; + err = hu->hdev_flags; + break; default: err = n_tty_ioctl_helper(tty, file, cmd, arg); break; - }; + } return err; } @@ -1225,16 +800,9 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, /* * We don't provide read/write/poll interface for user space. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 20) && \ - ((LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)) || \ - (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 3))) static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr, void **cookie, unsigned long offset) -#else -static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, - unsigned char __user * buf, size_t nr) -#endif { return 0; } @@ -1245,27 +813,25 @@ static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, return 0; } -static unsigned int hci_uart_tty_poll(struct tty_struct *tty, - struct file *filp, poll_table * wait) +static __poll_t hci_uart_tty_poll(struct tty_struct *tty, + struct file *filp, poll_table *wait) { return 0; } static struct tty_ldisc_ops hci_uart_ldisc = { - .owner = THIS_MODULE, - .magic = TTY_LDISC_MAGIC, - .name = "n_hci", - .open = hci_uart_tty_open, - .close = hci_uart_tty_close, - .read = hci_uart_tty_read, - .write = hci_uart_tty_write, - .ioctl = hci_uart_tty_ioctl, -#if HCI_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) - .compat_ioctl = hci_uart_tty_ioctl, -#endif - .poll = hci_uart_tty_poll, - .receive_buf = hci_uart_tty_receive, - .write_wakeup = hci_uart_tty_wakeup, + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_hci", + .open = hci_uart_tty_open, + .close = hci_uart_tty_close, + .read = hci_uart_tty_read, + .write = hci_uart_tty_write, + .ioctl = hci_uart_tty_ioctl, + .compat_ioctl = hci_uart_tty_ioctl, + .poll = hci_uart_tty_poll, + .receive_buf = hci_uart_tty_receive, + .write_wakeup = hci_uart_tty_wakeup, }; static int __init hci_uart_init(void) @@ -1275,18 +841,41 @@ static int __init hci_uart_init(void) BT_INFO("HCI UART driver ver %s", VERSION); /* Register the tty discipline */ - if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) { + err = tty_register_ldisc(N_HCI, &hci_uart_ldisc); + if (err) { BT_ERR("HCI line discipline registration failed. (%d)", err); return err; } + #ifdef CONFIG_BT_HCIUART_H4 h4_init(); #endif - /* Add realtek h5 support */ +#ifdef CONFIG_BT_HCIUART_BCSP + bcsp_init(); +#endif +#ifdef CONFIG_BT_HCIUART_LL + ll_init(); +#endif +#ifdef CONFIG_BT_HCIUART_ATH3K + ath_init(); +#endif +#ifdef CONFIG_BT_HCIUART_3WIRE h5_init(); - -#ifdef BTCOEX - rtk_btcoex_init(); +#endif +#ifdef CONFIG_BT_HCIUART_INTEL + intel_init(); +#endif +#ifdef CONFIG_BT_HCIUART_BCM + bcm_init(); +#endif +#ifdef CONFIG_BT_HCIUART_QCA + qca_init(); +#endif +#ifdef CONFIG_BT_HCIUART_AG6XX + ag6xx_init(); +#endif +#ifdef CONFIG_BT_HCIUART_MRVL + mrvl_init(); #endif return 0; @@ -1299,25 +888,43 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_H4 h4_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_BCSP + bcsp_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_LL + ll_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_ATH3K + ath_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_3WIRE h5_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_INTEL + intel_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_BCM + bcm_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_QCA + qca_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_AG6XX + ag6xx_deinit(); +#endif +#ifdef CONFIG_BT_HCIUART_MRVL + mrvl_deinit(); +#endif /* Release tty registration of line discipline */ - if ((err = tty_unregister_ldisc(N_HCI))) + err = tty_unregister_ldisc(N_HCI); + if (err) BT_ERR("Can't unregister HCI line discipline (%d)", err); - -#ifdef BTCOEX - rtk_btcoex_exit(); -#endif } module_init(hci_uart_init); module_exit(hci_uart_exit); -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 4, 0) -module_param(reset, bool, 0644); -MODULE_PARM_DESC(reset, "Send HCI reset command on initialization"); -#endif - MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/bluetooth/hci_rtk_h5.c b/drivers/bluetooth/hci_rtk_h5.c deleted file mode 100644 index 7fbce6689..000000000 --- a/drivers/bluetooth/hci_rtk_h5.c +++ /dev/null @@ -1,908 +0,0 @@ -/* - * - * Bluetooth HCI UART driver - * - * Copyright (C) 2011-2014 wifi_fae - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hci_uart.h" - -#ifdef BTCOEX -#include "rtk_coex.h" -#endif - -//#define VERSION "1.0" - -static int txcrc = 1; -//static int hciextn = 1; - -#define H5_TXWINSIZE 4 -#define H5_ACK_PKT 0x00 -#define H5_LE_PKT 0x0F -#define H5_VDRSPEC_PKT 0x0E - -struct h5_struct { - struct sk_buff_head unack; /* Unack'ed packets queue */ - struct sk_buff_head rel; /* Reliable packets queue */ - struct sk_buff_head unrel; /* Unreliable packets queue */ - - unsigned long rx_count; - struct sk_buff *rx_skb; - struct delayed_work retrans_work; - struct hci_uart *hu; /* Parent HCI UART */ - - enum { - H5_W4_PKT_DELIMITER, - H5_W4_PKT_START, - H5_W4_HDR, - H5_W4_DATA, - H5_W4_CRC - } rx_state; - - enum { - H5_ESCSTATE_NOESC, - H5_ESCSTATE_ESC - } rx_esc_state; - - u16 message_crc; - u8 use_crc; - u8 rxack; /* Last packet sent by us that the peer ack'ed */ - - u8 rxseq_txack; /* rxseq == txack. */ - u8 txack_req; /* Do we need to send ack's to the peer? */ - /* Reliable packet sequence number - used to assign seq to each rel pkt. */ - u8 msgq_txseq; - - /* The spin lock protects seq, ack and ack req */ - spinlock_t lock; -}; - -/* ---- H5 CRC calculation ---- */ - -/* Table for calculating CRC for polynomial 0x1021, LSB processed first, -initial value 0xffff, bits shifted in reverse order. */ - -static const u16 crc_table[] = { - 0x0000, 0x1081, 0x2102, 0x3183, - 0x4204, 0x5285, 0x6306, 0x7387, - 0x8408, 0x9489, 0xa50a, 0xb58b, - 0xc60c, 0xd68d, 0xe70e, 0xf78f -}; - -/* Initialise the crc calculator */ -#define H5_CRC_INIT(x) x = 0xffff - -/* - Update crc with next data byte - - Implementation note - The data byte is treated as two nibbles. The crc is generated - in reverse, i.e., bits are fed into the register from the top. -*/ -static void h5_crc_update(u16 * crc, u8 d) -{ - u16 reg = *crc; - - reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f]; - reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f]; - - *crc = reg; -} - -/* ---- H5 core ---- */ - -static void h5_slip_msgdelim(struct sk_buff *skb) -{ - const char pkt_delim = 0xc0; - - memcpy(skb_put(skb, 1), &pkt_delim, 1); -} - -static void h5_slip_one_byte(struct sk_buff *skb, u8 c) -{ - const char esc_c0[2] = { 0xdb, 0xdc }; - const char esc_db[2] = { 0xdb, 0xdd }; - const char esc_11[2] = { 0xdb, 0xde }; - const char esc_13[2] = { 0xdb, 0xdf }; - - switch (c) { - case 0xc0: - memcpy(skb_put(skb, 2), &esc_c0, 2); - break; - case 0xdb: - memcpy(skb_put(skb, 2), &esc_db, 2); - break; - case 0x11: - memcpy(skb_put(skb, 2), &esc_11, 2); - break; - case 0x13: - memcpy(skb_put(skb, 2), &esc_13, 2); - break; - default: - memcpy(skb_put(skb, 1), &c, 1); - } -} - -static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) -{ - struct h5_struct *h5 = hu->priv; - - if (skb->len > 0xFFF) { //Pkt length must be less than 4095 bytes - BT_ERR("Packet too long"); - kfree_skb(skb); - return 0; - } - - switch (bt_cb(skb)->pkt_type) { - case HCI_ACLDATA_PKT: - case HCI_COMMAND_PKT: - skb_queue_tail(&h5->rel, skb); - break; - - case HCI_SCODATA_PKT: - skb_queue_tail(&h5->unrel, skb); - break; - case H5_LE_PKT: - case H5_ACK_PKT: - case H5_VDRSPEC_PKT: - skb_queue_tail(&h5->unrel, skb); /* 3-wire LinkEstablishment */ - break; - - default: - BT_ERR("Unknown packet type"); - kfree_skb(skb); - break; - } - - return 0; -} - -static struct sk_buff *h5_prepare_pkt(struct h5_struct *h5, u8 * data, - int len, int pkt_type) -{ - struct sk_buff *nskb; - u8 hdr[4], chan; - u16 H5_CRC_INIT(h5_txmsg_crc); - int rel, i; - u8 tmp; - unsigned long flags; - - switch (pkt_type) { - case HCI_ACLDATA_PKT: - chan = 2; /* 3-wire ACL channel */ - rel = 1; /* reliable channel */ - break; - case HCI_COMMAND_PKT: - chan = 1; /* 3-wire cmd channel */ - rel = 1; /* reliable channel */ - break; - case HCI_EVENT_PKT: - chan = 4; /* 3-wire cmd channel */ - rel = 1; /* reliable channel */ - break; - case HCI_SCODATA_PKT: - chan = 3; /* 3-wire SCO channel */ - rel = 0; /* unreliable channel */ - break; - case H5_LE_PKT: - chan = 15; /* 3-wire LinkEstablishment channel */ - rel = 0; /* unreliable channel */ - break; - case H5_ACK_PKT: - chan = 0; /* 3-wire ACK channel */ - rel = 0; /* unreliable channel */ - break; - case H5_VDRSPEC_PKT: - chan = 14; /* 3-wire Vendor Specific channel */ - rel = 0; /* unreliable channel */ - break; - default: - BT_ERR("Unknown packet type"); - return NULL; - } - - /* Max len of packet: (original len +4(h5 hdr) +2(crc))*2 - (because bytes 0xc0 and 0xdb are escaped, worst case is - when the packet is all made of 0xc0 and 0xdb :) ) - + 2 (0xc0 delimiters at start and end). */ - - nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC); - if (!nskb) - return NULL; - - bt_cb(nskb)->pkt_type = pkt_type; - - h5_slip_msgdelim(nskb); - - spin_lock_irqsave(&h5->lock, flags); - tmp = h5->rxseq_txack; - hdr[0] = h5->rxseq_txack << 3; - h5->txack_req = 0; - spin_unlock_irqrestore(&h5->lock, flags); - BT_DBG("We request packet no %u to card", tmp); - - if (rel) { - spin_lock_irqsave(&h5->lock, flags); - tmp = h5->msgq_txseq; - hdr[0] |= 0x80 + h5->msgq_txseq; - h5->msgq_txseq = (h5->msgq_txseq + 1) & 0x07; - spin_unlock_irqrestore(&h5->lock, flags); - BT_DBG("Sending packet with seqno %u", tmp); - } - - if (h5->use_crc) - hdr[0] |= 0x40; - - hdr[1] = ((len << 4) & 0xff) | chan; - hdr[2] = len >> 4; - hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]); - - /* Put H5 header */ - for (i = 0; i < 4; i++) { - h5_slip_one_byte(nskb, hdr[i]); - - if (h5->use_crc) - h5_crc_update(&h5_txmsg_crc, hdr[i]); - } - - /* Put payload */ - for (i = 0; i < len; i++) { - h5_slip_one_byte(nskb, data[i]); - - if (h5->use_crc) - h5_crc_update(&h5_txmsg_crc, data[i]); - } - - /* Put CRC */ - if (h5->use_crc) { - h5_txmsg_crc = bitrev16(h5_txmsg_crc); - h5_slip_one_byte(nskb, (u8) ((h5_txmsg_crc >> 8) & 0x00ff)); - h5_slip_one_byte(nskb, (u8) (h5_txmsg_crc & 0x00ff)); - } - - h5_slip_msgdelim(nskb); - return nskb; -} - -/* This is a rewrite of pkt_avail in AH5 */ -static struct sk_buff *h5_dequeue(struct hci_uart *hu) -{ - struct h5_struct *h5 = hu->priv; - unsigned long flags; - struct sk_buff *skb; - - /* First of all, check for unreliable messages in the queue, - since they have priority */ - - if ((skb = skb_dequeue(&h5->unrel)) != NULL) { - struct sk_buff *nskb = - h5_prepare_pkt(h5, skb->data, skb->len, - bt_cb(skb)->pkt_type); - if (nskb) { - kfree_skb(skb); - return nskb; - } else { - skb_queue_head(&h5->unrel, skb); - BT_ERR - ("Could not dequeue pkt because alloc_skb failed"); - } - } - - /* Now, try to send a reliable pkt. We can only send a - reliable packet if the number of packets sent but not yet ack'ed - is < than the winsize */ - - spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); - - if (h5->unack.qlen < H5_TXWINSIZE - && (skb = skb_dequeue(&h5->rel)) != NULL) { - struct sk_buff *nskb = - h5_prepare_pkt(h5, skb->data, skb->len, - bt_cb(skb)->pkt_type); - if (nskb) { - __skb_queue_tail(&h5->unack, skb); - schedule_delayed_work(&h5->retrans_work, HZ / 4); - spin_unlock_irqrestore(&h5->unack.lock, flags); - return nskb; - } else { - skb_queue_head(&h5->rel, skb); - BT_ERR - ("Could not dequeue pkt because alloc_skb failed"); - } - } - - spin_unlock_irqrestore(&h5->unack.lock, flags); - - /* We could not send a reliable packet, either because there are - none or because there are too many unack'ed pkts. Did we receive - any packets we have not acknowledged yet ? */ - - if (h5->txack_req) { - /* if so, craft an empty ACK pkt and send it on H5 unreliable - channel 0 */ - struct sk_buff *nskb = h5_prepare_pkt(h5, NULL, 0, H5_ACK_PKT); - return nskb; - } - - /* We have nothing to send */ - return NULL; -} - -static int h5_flush(struct hci_uart *hu) -{ - BT_DBG("hu %p", hu); - return 0; -} - -/* Remove ack'ed packets */ -static void h5_pkt_cull(struct h5_struct *h5) -{ - struct sk_buff *skb, *tmp; - unsigned long flags; - int i, pkts_to_be_removed; - u8 seqno; - - spin_lock_irqsave(&h5->unack.lock, flags); - - pkts_to_be_removed = skb_queue_len(&h5->unack); - seqno = h5->msgq_txseq; - - while (pkts_to_be_removed) { - if (h5->rxack == seqno) - break; - pkts_to_be_removed--; - seqno = (seqno - 1) & 0x07; - } - - if (h5->rxack != seqno) - BT_ERR("Peer acked invalid packet"); - - BT_DBG("Removing %u pkts out of %u, up to seqno %u", - pkts_to_be_removed, skb_queue_len(&h5->unack), - (seqno - 1) & 0x07); - - i = 0; - skb_queue_walk_safe(&h5->unack, skb, tmp) { - if (i >= pkts_to_be_removed) - break; - i++; - - __skb_unlink(skb, &h5->unack); - kfree_skb(skb); - } - - if (skb_queue_empty(&h5->unack)) - cancel_delayed_work(&h5->retrans_work); - - spin_unlock_irqrestore(&h5->unack.lock, flags); - - if (i != pkts_to_be_removed) - BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed); -} - -/* Handle H5 link-establishment packets. When we - detect a "sync" packet, symptom that the BT module has reset, - we do nothing :) (yet) */ -#if 0 -static void h5_handle_le_pkt(struct hci_uart *hu) -{ - struct h5_struct *h5 = hu->priv; - u8 conf_pkt[2] = { 0x03, 0xfc }; - u8 conf_rsp_pkt[3] = { 0x04, 0x7b, 0x00 }; - u8 sync_pkt[2] = { 0x01, 0x7e }; - u8 sync_rsp_pkt[2] = { 0x02, 0x7d }; - - u8 wakeup_pkt[2] = { 0x05, 0xfa }; - u8 woken_pkt[2] = { 0x06, 0xf9 }; - u8 sleep_pkt[2] = { 0x07, 0x78 }; - - /* spot "conf" pkts and reply with a "conf rsp" pkt */ - if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_skb->data[4], conf_pkt, 2)) { - struct sk_buff *nskb = alloc_skb(3, GFP_ATOMIC); - - BT_DBG("Found a LE conf pkt"); - if (!nskb) - return; - - conf_rsp_pkt[2] |= txcrc << 0x4; //crc check enable, version no = 0. needed to be as avariable. - memcpy(skb_put(nskb, 3), conf_rsp_pkt, 3); - bt_cb(nskb)->pkt_type = H5_LE_PKT; - - skb_queue_head(&h5->unrel, nskb); - hci_uart_tx_wakeup(hu); - } - /* spot "conf resp" pkts */ - else if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_skb->data[4], conf_rsp_pkt, 2)) { - BT_DBG("Found a LE conf resp pkt, device go into active state"); - txcrc = (h5->rx_skb->data[6] >> 0x4) & 0x1; - } - - /* Spot "sync" pkts. If we find one...disaster! */ - else if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_skb->data[4], sync_pkt, 2)) { - BT_ERR("Found a LE sync pkt, card has reset"); - //DO Something here - } - /* Spot "sync resp" pkts. If we find one...disaster! */ - else if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_skb->data[4], sync_rsp_pkt, 2)) { - BT_ERR - ("Found a LE sync resp pkt, device go into initialized state"); - // DO Something here - } - /* Spot "wakeup" pkts. reply woken message when in active mode */ - else if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_skb->data[4], wakeup_pkt, 2)) { - struct sk_buff *nskb = alloc_skb(2, GFP_ATOMIC); - - BT_ERR("Found a LE Wakeup pkt, and reply woken message"); - // DO Something here - - memcpy(skb_put(nskb, 2), woken_pkt, 2); - bt_cb(nskb)->pkt_type = H5_LE_PKT; - - skb_queue_head(&h5->unrel, nskb); - hci_uart_tx_wakeup(hu); - } - /* Spot "woken" pkts. receive woken message from device */ - else if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_skb->data[4], woken_pkt, 2)) { - BT_ERR("Found a LE woken pkt from device"); - // DO Something here - } - /* Spot "Sleep" pkts */ - else if (h5->rx_skb->data[1] >> 4 == 2 && h5->rx_skb->data[2] == 0 && - !memcmp(&h5->rx_indent: Standard input:620: Error:Unmatched 'else' -skb->data[4], sleep_pkt, 2)) { - BT_ERR("Found a LE Sleep pkt"); - // DO Something here - } -} -#endif - -static inline void h5_unslip_one_byte(struct h5_struct *h5, unsigned char byte) -{ - const u8 c0 = 0xc0, db = 0xdb; - const u8 oof1 = 0x11, oof2 = 0x13; - - switch (h5->rx_esc_state) { - case H5_ESCSTATE_NOESC: - switch (byte) { - case 0xdb: - h5->rx_esc_state = H5_ESCSTATE_ESC; - break; - default: - memcpy(skb_put(h5->rx_skb, 1), &byte, 1); - if ((h5->rx_skb->data[0] & 0x40) != 0 && - h5->rx_state != H5_W4_CRC) - h5_crc_update(&h5->message_crc, byte); - h5->rx_count--; - } - break; - - case H5_ESCSTATE_ESC: - switch (byte) { - case 0xdc: - memcpy(skb_put(h5->rx_skb, 1), &c0, 1); - if ((h5->rx_skb->data[0] & 0x40) != 0 && - h5->rx_state != H5_W4_CRC) - h5_crc_update(&h5->message_crc, 0xc0); - h5->rx_esc_state = H5_ESCSTATE_NOESC; - h5->rx_count--; - break; - - case 0xdd: - memcpy(skb_put(h5->rx_skb, 1), &db, 1); - if ((h5->rx_skb->data[0] & 0x40) != 0 && - h5->rx_state != H5_W4_CRC) - h5_crc_update(&h5->message_crc, 0xdb); - h5->rx_esc_state = H5_ESCSTATE_NOESC; - h5->rx_count--; - break; - - case 0xde: - memcpy(skb_put(h5->rx_skb, 1), &oof1, 1); - if ((h5->rx_skb->data[0] & 0x40) != 0 - && h5->rx_state != H5_W4_CRC) - h5_crc_update(&h5->message_crc, oof1); - h5->rx_esc_state = H5_ESCSTATE_NOESC; - h5->rx_count--; - break; - - case 0xdf: - memcpy(skb_put(h5->rx_skb, 1), &oof2, 1); - if ((h5->rx_skb->data[0] & 0x40) != 0 - && h5->rx_state != H5_W4_CRC) - h5_crc_update(&h5->message_crc, oof2); - h5->rx_esc_state = H5_ESCSTATE_NOESC; - h5->rx_count--; - break; - - default: - BT_ERR("Invalid byte %02x after esc byte", byte); - kfree_skb(h5->rx_skb); - h5->rx_skb = NULL; - h5->rx_state = H5_W4_PKT_DELIMITER; - h5->rx_count = 0; - } - } -} - -static void h5_complete_rx_pkt(struct hci_uart *hu) -{ - struct h5_struct *h5 = hu->priv; - int pass_up; - - if (h5->rx_skb->data[0] & 0x80) { /* reliable pkt */ - unsigned long flags; - u8 rxseq; - - spin_lock_irqsave(&h5->lock, flags); - rxseq = h5->rxseq_txack; - h5->rxseq_txack++; - h5->rxseq_txack %= 0x8; - h5->txack_req = 1; - spin_unlock_irqrestore(&h5->lock, flags); - - BT_DBG("Received seqno %u from card", rxseq); - } - - h5->rxack = (h5->rx_skb->data[0] >> 3) & 0x07; - BT_DBG("Request for pkt %u from card", h5->rxack); - - h5_pkt_cull(h5); - - hci_uart_tx_wakeup(hu); - - if ((h5->rx_skb->data[1] & 0x0f) == 2 && h5->rx_skb->data[0] & 0x80) { - bt_cb(h5->rx_skb)->pkt_type = HCI_ACLDATA_PKT; - pass_up = 1; - } else if ((h5->rx_skb->data[1] & 0x0f) == 4 && - h5->rx_skb->data[0] & 0x80) { - bt_cb(h5->rx_skb)->pkt_type = HCI_EVENT_PKT; - pass_up = 1; - } else if ((h5->rx_skb->data[1] & 0x0f) == 3) { - bt_cb(h5->rx_skb)->pkt_type = HCI_SCODATA_PKT; - pass_up = 1; - } else if ((h5->rx_skb->data[1] & 0x0f) == 15 && - !(h5->rx_skb->data[0] & 0x80)) { - //h5_handle_le_pkt(hu);//Link Establishment Pkt - pass_up = 0; - } else if ((h5->rx_skb->data[1] & 0x0f) == 1 && - h5->rx_skb->data[0] & 0x80) { - bt_cb(h5->rx_skb)->pkt_type = HCI_COMMAND_PKT; - pass_up = 1; - } else if ((h5->rx_skb->data[1] & 0x0f) == 14) { - bt_cb(h5->rx_skb)->pkt_type = H5_VDRSPEC_PKT; - pass_up = 1; - } else - pass_up = 0; - - if (!pass_up) { - /* struct hci_event_hdr hdr; */ - u8 desc = (h5->rx_skb->data[1] & 0x0f); - - if (desc != H5_ACK_PKT && desc != H5_LE_PKT) { - /* if (hciextn) { - * desc |= 0xc0; - * skb_pull(h5->rx_skb, 4); - * memcpy(skb_push(h5->rx_skb, 1), &desc, 1); - - * hdr.evt = 0xff; - * hdr.plen = h5->rx_skb->len; - * memcpy(skb_push(h5->rx_skb, HCI_EVENT_HDR_SIZE), - * &hdr, HCI_EVENT_HDR_SIZE); - * bt_cb(h5->rx_skb)->pkt_type = HCI_EVENT_PKT; - - * hci_recv_frame(h5->rx_skb); - * } else { */ - BT_ERR("Packet for unknown channel (%u %s)", - h5->rx_skb->data[1] & 0x0f, - h5->rx_skb->data[0] & 0x80 ? - "reliable" : "unreliable"); - kfree_skb(h5->rx_skb); - /* } */ - } else - kfree_skb(h5->rx_skb); - } else { - /* Pull out H5 hdr */ - skb_pull(h5->rx_skb, 4); - -#ifdef BTCOEX - if (bt_cb(h5->rx_skb)->pkt_type == HCI_EVENT_PKT) - rtk_btcoex_parse_event(h5->rx_skb->data, - h5->rx_skb->len); - - if (bt_cb(h5->rx_skb)->pkt_type == HCI_ACLDATA_PKT) - rtk_btcoex_parse_l2cap_data_rx(h5->rx_skb->data, - h5->rx_skb->len); -#endif - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - hci_recv_frame(h5->rx_skb); -#else - hci_recv_frame(hu->hdev, h5->rx_skb); -#endif - } - - h5->rx_state = H5_W4_PKT_DELIMITER; - h5->rx_skb = NULL; -} - -static u16 bscp_get_crc(struct h5_struct *h5) { - return get_unaligned_be16(&h5->rx_skb-> - data[h5->rx_skb->len - 2]); -} - -/* Recv data */ -static int h5_recv(struct hci_uart *hu, void *data, int count) -{ - struct h5_struct *h5 = hu->priv; - register unsigned char *ptr; - u8 rxseq; - unsigned long flags; - - BT_DBG("hu %p count %d rx_state %d rx_count %ld", - hu, count, h5->rx_state, h5->rx_count); - - ptr = data; - while (count) { - if (h5->rx_count) { - if (*ptr == 0xc0) { - BT_ERR("Short H5 packet"); - kfree_skb(h5->rx_skb); - h5->rx_state = H5_W4_PKT_START; - h5->rx_count = 0; - } else - h5_unslip_one_byte(h5, *ptr); - - ptr++; - count--; - continue; - } - - switch (h5->rx_state) { - case H5_W4_HDR: - if ((0xff & (u8) ~ - (h5->rx_skb->data[0] + - h5->rx_skb->data[1] + - h5->rx_skb->data[2])) != h5->rx_skb->data[3]) { - BT_ERR("Error in H5 hdr checksum"); - kfree_skb(h5->rx_skb); - h5->rx_state = H5_W4_PKT_DELIMITER; - h5->rx_count = 0; - continue; - } - rxseq = h5->rxseq_txack; - if (h5->rx_skb->data[0] & 0x80 /* reliable pkt */ - && (h5->rx_skb->data[0] & 0x07) != rxseq) { - BT_ERR("Out-of-order packet arrived, got %u expected %u", - h5->rx_skb->data[0] & 0x07, rxseq); - - spin_lock_irqsave(&h5->lock, flags); - h5->txack_req = 1; - spin_unlock_irqrestore(&h5->lock, flags); - hci_uart_tx_wakeup(hu); - kfree_skb(h5->rx_skb); - h5->rx_state = H5_W4_PKT_DELIMITER; - h5->rx_count = 0; - continue; - } - h5->rx_state = H5_W4_DATA; - h5->rx_count = (h5->rx_skb->data[1] >> 4) + (h5->rx_skb->data[2] << 4); /* May be 0 */ - continue; - - case H5_W4_DATA: - if (h5->rx_skb->data[0] & 0x40) { /* pkt with crc */ - h5->rx_state = H5_W4_CRC; - h5->rx_count = 2; - } else - h5_complete_rx_pkt(hu); - continue; - - case H5_W4_CRC: - if (bitrev16(h5->message_crc) != bscp_get_crc(h5)) { - BT_ERR - ("Checksum failed: computed %04x received %04x", - bitrev16(h5->message_crc), - bscp_get_crc(h5)); - - kfree_skb(h5->rx_skb); - h5->rx_state = H5_W4_PKT_DELIMITER; - h5->rx_count = 0; - continue; - } - skb_trim(h5->rx_skb, h5->rx_skb->len - 2); - h5_complete_rx_pkt(hu); - continue; - - case H5_W4_PKT_DELIMITER: - switch (*ptr) { - case 0xc0: - h5->rx_state = H5_W4_PKT_START; - break; - default: - /*BT_ERR("Ignoring byte %02x", *ptr); */ - break; - } - ptr++; - count--; - break; - - case H5_W4_PKT_START: - switch (*ptr) { - case 0xc0: - ptr++; - count--; - break; - - default: - h5->rx_state = H5_W4_HDR; - h5->rx_count = 4; - h5->rx_esc_state = H5_ESCSTATE_NOESC; - H5_CRC_INIT(h5->message_crc); - - /* Do not increment ptr or decrement count - * Allocate packet. Max len of a H5 pkt= - * 0xFFF (payload) +4 (header) +2 (crc) */ - - h5->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC); - if (!h5->rx_skb) { - BT_ERR - ("Can't allocate mem for new packet"); - h5->rx_state = H5_W4_PKT_DELIMITER; - h5->rx_count = 0; - return 0; - } - h5->rx_skb->dev = (void *)hu->hdev; - break; - } - break; - } - } - return count; -} - -/* Arrange to retransmit all messages in the relq. */ -static void h5_timed_event(struct work_struct *work) -{ - struct h5_struct *h5; - struct hci_uart *hu; - unsigned long flags; - unsigned long flags2; - struct sk_buff *skb; - - h5 = container_of(work, struct h5_struct, retrans_work.work); - hu = h5->hu; - - BT_INFO("hu %p retransmitting %u pkts", hu, h5->unack.qlen); - - spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); - - /* Move the pkt from unack queue to the head of reliable tx queue and - * roll back the tx seq number - */ - while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) { - spin_lock_irqsave(&h5->lock, flags2); - h5->msgq_txseq = (h5->msgq_txseq - 1) & 0x07; - spin_unlock_irqrestore(&h5->lock, flags2); - skb_queue_head(&h5->rel, skb); - } - - spin_unlock_irqrestore(&h5->unack.lock, flags); - - hci_uart_tx_wakeup(hu); -} - -static int h5_open(struct hci_uart *hu) -{ - struct h5_struct *h5; - - BT_DBG("hu %p", hu); - - BT_INFO("h5_open"); - h5 = kzalloc(sizeof(*h5), GFP_ATOMIC); - if (!h5) - return -ENOMEM; - - hu->priv = h5; - skb_queue_head_init(&h5->unack); - skb_queue_head_init(&h5->rel); - skb_queue_head_init(&h5->unrel); - spin_lock_init(&h5->lock); - - h5->hu = hu; - INIT_DELAYED_WORK(&h5->retrans_work, (void *)h5_timed_event); - - h5->rx_state = H5_W4_PKT_DELIMITER; - - if (txcrc) - h5->use_crc = 1; - - return 0; -} - -static int h5_close(struct hci_uart *hu) -{ - struct h5_struct *h5 = hu->priv; - - BT_INFO("h5_close"); - - cancel_delayed_work_sync(&h5->retrans_work); - - hu->priv = NULL; - - skb_queue_purge(&h5->unack); - skb_queue_purge(&h5->rel); - skb_queue_purge(&h5->unrel); - - kfree(h5); - - return 0; -} - -static struct hci_uart_proto h5 = { - .id = HCI_UART_3WIRE, - .open = h5_open, - .close = h5_close, - .enqueue = h5_enqueue, - .dequeue = h5_dequeue, - .recv = h5_recv, - .flush = h5_flush -}; - -int h5_init(void) -{ - int err = hci_uart_register_proto(&h5); - - if (!err) - BT_INFO("HCI Realtek H5 protocol initialized"); - else - BT_ERR("HCI Realtek H5 protocol registration failed"); - - return err; -} - -int h5_deinit(void) -{ - return hci_uart_unregister_proto(&h5); -} diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 598ec10cf..4e039d7a1 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * * Bluetooth HCI UART driver @@ -5,50 +6,12 @@ * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2004-2005 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ -#include -#include -#include - -/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */ -#define HCI_VERSION_CODE LINUX_VERSION_CODE #ifndef N_HCI #define N_HCI 15 #endif -#ifndef CONFIG_BT_HCIUART_H4 -#define CONFIG_BT_HCIUART_H4 -#endif - -#define BTCOEX - -/* Send host sleep notification to Controller */ -#define WOBT_NOTIFY 0 /* 1 enable; 0 disable */ - -/* Send LE whitelist only for Background scan parameters */ -#define WOBT_NOTIFY_BG_SCAN_LE_WHITELIST_ONLY (0 * WOBT_NOTIFY) /* 1 enable; 0 disable */ - -/* RTKBT Power-on Whitelist for sideband wake-up by LE Advertising from Remote. -* Note that it's necessary to apply TV FW Patch. */ -#define RTKBT_TV_POWERON_WHITELIST (0 * WOBT_NOTIFY) /* 1 enable; 0 disable */ - /* Ioctls */ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) @@ -57,7 +20,7 @@ #define HCIUARTGETFLAGS _IOR('U', 204, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 6 +#define HCI_UART_MAX_PROTO 12 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 @@ -65,6 +28,12 @@ #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 #define HCI_UART_ATH3K 5 +#define HCI_UART_INTEL 6 +#define HCI_UART_BCM 7 +#define HCI_UART_QCA 8 +#define HCI_UART_AG6XX 9 +#define HCI_UART_NOKIA 10 +#define HCI_UART_MRVL 11 #define HCI_UART_RAW_DEVICE 0 #define HCI_UART_RESET_ON_INIT 1 @@ -74,42 +43,46 @@ #define HCI_UART_VND_DETECT 5 struct hci_uart; +struct serdev_device; struct hci_uart_proto { unsigned int id; + const char *name; + unsigned int manufacturer; + unsigned int init_speed; + unsigned int oper_speed; int (*open)(struct hci_uart *hu); int (*close)(struct hci_uart *hu); int (*flush)(struct hci_uart *hu); - int (*recv)(struct hci_uart *hu, void *data, int len); + int (*setup)(struct hci_uart *hu); + int (*set_baudrate)(struct hci_uart *hu, unsigned int speed); + int (*recv)(struct hci_uart *hu, const void *data, int len); int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb); struct sk_buff *(*dequeue)(struct hci_uart *hu); }; struct hci_uart { struct tty_struct *tty; + struct serdev_device *serdev; struct hci_dev *hdev; unsigned long flags; unsigned long hdev_flags; + struct work_struct init_ready; struct work_struct write_work; - struct workqueue_struct *hci_uart_wq; - struct hci_uart_proto *proto; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - struct percpu_rw_semaphore proto_lock; /* Stop work for proto close */ -#else - struct rw_semaphore proto_lock; -#endif + const struct hci_uart_proto *proto; + struct percpu_rw_semaphore proto_lock; /* Stop work for proto close */ void *priv; - struct semaphore tx_sem; /* semaphore for tx */ - struct sk_buff *tx_skb; unsigned long tx_state; -#if WOBT_NOTIFY - struct notifier_block pm_notify_block; -#endif + unsigned int init_speed; + unsigned int oper_speed; + + u8 alignment; + u8 padding; }; /* HCI_UART proto flag bits */ @@ -121,20 +94,108 @@ struct hci_uart { #define HCI_UART_SENDING 1 #define HCI_UART_TX_WAKEUP 2 -extern int hci_uart_register_proto(struct hci_uart_proto *p); -extern int hci_uart_unregister_proto(struct hci_uart_proto *p); -extern int hci_uart_tx_wakeup(struct hci_uart *hu); +int hci_uart_register_proto(const struct hci_uart_proto *p); +int hci_uart_unregister_proto(const struct hci_uart_proto *p); +int hci_uart_register_device(struct hci_uart *hu, const struct hci_uart_proto *p); +void hci_uart_unregister_device(struct hci_uart *hu); + +int hci_uart_tx_wakeup(struct hci_uart *hu); +int hci_uart_wait_until_sent(struct hci_uart *hu); +int hci_uart_init_ready(struct hci_uart *hu); +void hci_uart_init_work(struct work_struct *work); +void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed); +bool hci_uart_has_flow_control(struct hci_uart *hu); +void hci_uart_set_flow_control(struct hci_uart *hu, bool enable); +void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed, + unsigned int oper_speed); #ifdef CONFIG_BT_HCIUART_H4 -extern int h4_init(void); -extern int h4_deinit(void); +int h4_init(void); +int h4_deinit(void); + +struct h4_recv_pkt { + u8 type; /* Packet type */ + u8 hlen; /* Header length */ + u8 loff; /* Data length offset in header */ + u8 lsize; /* Data length field size */ + u16 maxlen; /* Max overall packet length */ + int (*recv)(struct hci_dev *hdev, struct sk_buff *skb); +}; + +#define H4_RECV_ACL \ + .type = HCI_ACLDATA_PKT, \ + .hlen = HCI_ACL_HDR_SIZE, \ + .loff = 2, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE \ + +#define H4_RECV_SCO \ + .type = HCI_SCODATA_PKT, \ + .hlen = HCI_SCO_HDR_SIZE, \ + .loff = 2, \ + .lsize = 1, \ + .maxlen = HCI_MAX_SCO_SIZE + +#define H4_RECV_EVENT \ + .type = HCI_EVENT_PKT, \ + .hlen = HCI_EVENT_HDR_SIZE, \ + .loff = 1, \ + .lsize = 1, \ + .maxlen = HCI_MAX_EVENT_SIZE + +#define H4_RECV_ISO \ + .type = HCI_ISODATA_PKT, \ + .hlen = HCI_ISO_HDR_SIZE, \ + .loff = 2, \ + .lsize = 2, \ + .maxlen = HCI_MAX_FRAME_SIZE \ + +struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, + const unsigned char *buffer, int count, + const struct h4_recv_pkt *pkts, int pkts_count); #endif -extern int h5_init(void); -extern int h5_deinit(void); - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) -extern int hci_uart_send_frame(struct sk_buff *skb); -#else -extern int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb); +#ifdef CONFIG_BT_HCIUART_BCSP +int bcsp_init(void); +int bcsp_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_LL +int ll_init(void); +int ll_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_ATH3K +int ath_init(void); +int ath_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_3WIRE +int h5_init(void); +int h5_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_INTEL +int intel_init(void); +int intel_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_BCM +int bcm_init(void); +int bcm_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_QCA +int qca_init(void); +int qca_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_AG6XX +int ag6xx_init(void); +int ag6xx_deinit(void); +#endif + +#ifdef CONFIG_BT_HCIUART_MRVL +int mrvl_init(void); +int mrvl_deinit(void); #endif diff --git a/drivers/bluetooth/rtk_coex.c b/drivers/bluetooth/rtk_coex.c deleted file mode 100644 index a46b722b5..000000000 --- a/drivers/bluetooth/rtk_coex.c +++ /dev/null @@ -1,3068 +0,0 @@ -/* -* -* Realtek Bluetooth USB driver -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rtk_coex.h" - -/* Software coex message can be sent to and receive from WiFi driver by - * UDP socket or exported symbol */ -/* #define RTK_COEX_OVER_SYMBOL */ - -#if BTRTL_HCI_IF == BTRTL_HCIUSB -#include -#include "rtk_bt.h" -#undef RTKBT_DBG -#undef RTKBT_INFO -#undef RTKBT_WARN -#undef RTKBT_ERR - -#elif BTRTL_HCI_IF == BTRTL_HCIUART -/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */ -#define HCI_VERSION_CODE LINUX_VERSION_CODE - -#else -#error "Please set type of HCI interface" -#endif - -#define RTK_VERSION "1.2" - -#define RTKBT_DBG(fmt, arg...) printk(KERN_INFO "rtk_btcoex: " fmt "\n" , ## arg) -#define RTKBT_INFO(fmt, arg...) printk(KERN_INFO "rtk_btcoex: " fmt "\n" , ## arg) -#define RTKBT_WARN(fmt, arg...) printk(KERN_WARNING "rtk_btcoex: " fmt "\n", ## arg) -#define RTKBT_ERR(fmt, arg...) printk(KERN_WARNING "rtk_btcoex: " fmt "\n", ## arg) - -static struct rtl_coex_struct btrtl_coex; - -#ifdef RTB_SOFTWARE_MAILBOX -#ifdef RTK_COEX_OVER_SYMBOL -static struct sk_buff_head rtw_q; -static struct workqueue_struct *rtw_wq; -static struct work_struct rtw_work; -static u8 rtw_coex_on; -#endif -#endif - -#define is_profile_connected(profile) ((btrtl_coex.profile_bitmap & BIT(profile)) > 0) -#define is_profile_busy(profile) ((btrtl_coex.profile_status & BIT(profile)) > 0) - -#ifdef RTB_SOFTWARE_MAILBOX -static void rtk_handle_event_from_wifi(uint8_t * msg); -#endif - -static int rtl_alloc_buff(struct rtl_coex_struct *coex) -{ - struct rtl_hci_ev *ev; - struct rtl_l2_buff *l2; - int i; - int order; - unsigned long addr; - unsigned long addr2; - int ev_size; - int l2_size; - int n; - - 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); - - n = NUM_RTL_HCI_EV * sizeof(struct rtl_hci_ev); - ev_size = ALIGN(n, sizeof(unsigned long)); - - n = L2_MAX_PKTS * sizeof(struct rtl_l2_buff); - l2_size = ALIGN(n, sizeof(unsigned long)); - - RTKBT_DBG("alloc buffers %d, %d for ev and l2", ev_size, l2_size); - - order = get_order(ev_size + l2_size); - addr = __get_free_pages(GFP_KERNEL, order); - if (!addr) { - RTKBT_ERR("failed to alloc buffers for ev and l2."); - return -ENOMEM; - } - memset((void *)addr, 0, ev_size + l2_size); - - coex->pages_addr = addr; - coex->buff_size = ev_size + l2_size; - - ev = (struct rtl_hci_ev *)addr; - for (i = 0; i < NUM_RTL_HCI_EV; i++) { - list_add_tail(&ev->list, &coex->ev_free_list); - ev++; - } - - addr2 = addr + ev_size; - l2 = (struct rtl_l2_buff *)addr2; - for (i = 0; i < L2_MAX_PKTS; i++) { - list_add_tail(&l2->list, &coex->l2_free_list); - l2++; - } - - return 0; -} - -static void rtl_free_buff(struct rtl_coex_struct *coex) -{ - struct rtl_hci_ev *ev; - struct rtl_l2_buff *l2; - 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->ev_free_list)) { - ev = list_entry(coex->ev_free_list.next, struct rtl_hci_ev, - list); - 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); - list_del(&l2->list); - } - - spin_unlock_irqrestore(&coex->buff_lock, flags); - - if (coex->buff_size > 0) { - free_pages(coex->pages_addr, get_order(coex->buff_size)); - coex->pages_addr = 0; - coex->buff_size = 0; - } -} - -static struct rtl_hci_ev *rtl_ev_node_get(struct rtl_coex_struct *coex) -{ - struct rtl_hci_ev *ev; - unsigned long flags; - - if (!coex->buff_size) - return NULL; - - spin_lock_irqsave(&coex->buff_lock, flags); - if (!list_empty(&coex->ev_free_list)) { - ev = list_entry(coex->ev_free_list.next, struct rtl_hci_ev, - list); - list_del(&ev->list); - } else - ev = NULL; - spin_unlock_irqrestore(&coex->buff_lock, flags); - return ev; -} - -static int rtl_ev_node_to_used(struct rtl_coex_struct *coex, - struct rtl_hci_ev *ev) -{ - unsigned long flags; - - spin_lock_irqsave(&coex->buff_lock, flags); - list_add_tail(&ev->list, &coex->ev_used_list); - spin_unlock_irqrestore(&coex->buff_lock, flags); - - return 0; -} - -static struct rtl_l2_buff *rtl_l2_node_get(struct rtl_coex_struct *coex) -{ - struct rtl_l2_buff *l2; - unsigned long flags; - - if (!coex->buff_size) - return NULL; - - spin_lock_irqsave(&coex->buff_lock, flags); - - if(!list_empty(&coex->l2_free_list)) { - l2 = list_entry(coex->l2_free_list.next, struct rtl_l2_buff, - list); - list_del(&l2->list); - } else - l2 = NULL; - - spin_unlock_irqrestore(&coex->buff_lock, flags); - return l2; -} - -static int rtl_l2_node_to_used(struct rtl_coex_struct *coex, - struct rtl_l2_buff *l2) -{ - unsigned long flags; - - spin_lock_irqsave(&coex->buff_lock, flags); - list_add_tail(&l2->list, &coex->l2_used_list); - spin_unlock_irqrestore(&coex->buff_lock, flags); - - return 0; -} - -static int8_t psm_to_profile_index(uint16_t psm) -{ - switch (psm) { - case PSM_AVCTP: - case PSM_SDP: - return -1; //ignore - - case PSM_HID: - case PSM_HID_INT: - return profile_hid; - - case PSM_AVDTP: - return profile_a2dp; - - case PSM_PAN: - case PSM_OPP: - case PSM_FTP: - case PSM_BIP: - case PSM_RFCOMM: - return profile_pan; - - default: - return profile_pan; - } -} - -static rtk_prof_info *find_by_psm(u16 psm) -{ - struct list_head *head = &btrtl_coex.profile_list; - struct list_head *iter = NULL; - struct list_head *temp = NULL; - rtk_prof_info *desc = NULL; - - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_prof_info, list); - if (desc->psm == psm) - return desc; - } - - return NULL; -} - -static void rtk_check_setup_timer(int8_t profile_index) -{ - if (profile_index == profile_a2dp) { - btrtl_coex.a2dp_packet_count = 0; - btrtl_coex.a2dp_count_timer.expires = - jiffies + msecs_to_jiffies(1000); - mod_timer(&btrtl_coex.a2dp_count_timer, - btrtl_coex.a2dp_count_timer.expires); - } - - if (profile_index == profile_pan) { - btrtl_coex.pan_packet_count = 0; - btrtl_coex.pan_count_timer.expires = - jiffies + msecs_to_jiffies(1000); - mod_timer(&btrtl_coex.pan_count_timer, - btrtl_coex.pan_count_timer.expires); - } - - /* 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])) { - btrtl_coex.hogp_packet_count = 0; - btrtl_coex.voice_packet_count = 0; - btrtl_coex.hogp_count_timer.expires = - jiffies + msecs_to_jiffies(1000); - mod_timer(&btrtl_coex.hogp_count_timer, - btrtl_coex.hogp_count_timer.expires); - } - } -} - -static void rtk_check_del_timer(int8_t profile_index) -{ - if (profile_a2dp == profile_index) { - btrtl_coex.a2dp_packet_count = 0; - del_timer_sync(&btrtl_coex.a2dp_count_timer); - } - if (profile_pan == profile_index) { - btrtl_coex.pan_packet_count = 0; - del_timer_sync(&btrtl_coex.pan_count_timer); - } - if (profile_hogp == profile_index) { - btrtl_coex.hogp_packet_count = 0; - if (btrtl_coex.profile_refcount[profile_voice] == 0) { - del_timer_sync(&btrtl_coex.hogp_count_timer); - } - } - if (profile_voice == profile_index) { - btrtl_coex.voice_packet_count = 0; - if (btrtl_coex.profile_refcount[profile_hogp] == 0) { - del_timer_sync(&btrtl_coex.hogp_count_timer); - } - } -} - - - -static rtk_conn_prof *find_connection_by_handle(struct rtl_coex_struct * coex, - uint16_t handle) -{ - struct list_head *head = &coex->conn_hash; - struct list_head *iter = NULL, *temp = NULL; - rtk_conn_prof *desc = NULL; - - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_conn_prof, list); - if ((handle & 0xEFF) == desc->handle) { - return desc; - } - } - return NULL; -} - -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->handle = handle; - - return phci_conn; -} - -static void init_connection_hash(struct rtl_coex_struct * coex) -{ - struct list_head *head = &coex->conn_hash; - INIT_LIST_HEAD(head); -} - -static void add_connection_to_hash(struct rtl_coex_struct * coex, - rtk_conn_prof * desc) -{ - struct list_head *head = &coex->conn_hash; - list_add_tail(&desc->list, head); -} - -static void delete_connection_from_hash(rtk_conn_prof * desc) -{ - if (desc) { - list_del(&desc->list); - kfree(desc); - } -} - -static void flush_connection_hash(struct rtl_coex_struct * coex) -{ - struct list_head *head = &coex->conn_hash; - struct list_head *iter = NULL, *temp = NULL; - rtk_conn_prof *desc = NULL; - - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_conn_prof, list); - if (desc) { - list_del(&desc->list); - kfree(desc); - } - } - //INIT_LIST_HEAD(head); -} - -static void init_profile_hash(struct rtl_coex_struct * coex) -{ - struct list_head *head = &coex->profile_list; - INIT_LIST_HEAD(head); -} - -static uint8_t list_allocate_add(uint16_t handle, uint16_t psm, - int8_t profile_index, uint16_t dcid, - uint16_t scid) -{ - rtk_prof_info *pprof_info = NULL; - - if (profile_index < 0) { - RTKBT_ERR("PSM 0x%x do not need parse", psm); - return FALSE; - } - - pprof_info = kmalloc(sizeof(rtk_prof_info), GFP_ATOMIC); - - if (NULL == pprof_info) { - RTKBT_ERR("list_allocate_add: allocate error"); - return FALSE; - } - - /* Check if it is the second l2cap connection for a2dp - * a2dp signal channel will be created first than media channel. - */ - if (psm == PSM_AVDTP) { - rtk_prof_info *pinfo = find_by_psm(psm); - if (!pinfo) { - pprof_info->flags = A2DP_SIGNAL; - RTKBT_INFO("%s: Add a2dp signal channel", __func__); - } else { - pprof_info->flags = A2DP_MEDIA; - RTKBT_INFO("%s: Add a2dp media channel", __func__); - } - } - - pprof_info->handle = handle; - pprof_info->psm = psm; - pprof_info->scid = scid; - pprof_info->dcid = dcid; - pprof_info->profile_index = profile_index; - list_add_tail(&(pprof_info->list), &(btrtl_coex.profile_list)); - - return TRUE; -} - -static void delete_profile_from_hash(rtk_prof_info * 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); - if (desc) { - list_del(&desc->list); - kfree(desc); - desc = NULL; - } -} - -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); - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_prof_info, list); - delete_profile_from_hash(desc); - } - //INIT_LIST_HEAD(head); - spin_unlock(&btrtl_coex.spin_lock_profile); -} - -static rtk_prof_info *find_profile_by_handle_scid(struct rtl_coex_struct * - coex, uint16_t handle, - uint16_t scid) -{ - struct list_head *head = &coex->profile_list; - struct list_head *iter = NULL, *temp = NULL; - rtk_prof_info *desc = NULL; - - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_prof_info, list); - if (((handle & 0xFFF) == desc->handle) && (scid == desc->scid)) { - return desc; - } - } - return NULL; -} - -static rtk_prof_info *find_profile_by_handle_dcid(struct rtl_coex_struct * - coex, uint16_t handle, - uint16_t dcid) -{ - struct list_head *head = &coex->profile_list; - struct list_head *iter = NULL, *temp = NULL; - rtk_prof_info *desc = NULL; - - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_prof_info, list); - if (((handle & 0xFFF) == desc->handle) && (dcid == desc->dcid)) { - return desc; - } - } - return NULL; -} - -static rtk_prof_info *find_profile_by_handle_dcid_scid(struct rtl_coex_struct - * coex, uint16_t handle, - uint16_t dcid, - uint16_t scid) -{ - struct list_head *head = &coex->profile_list; - struct list_head *iter = NULL, *temp = NULL; - rtk_prof_info *desc = NULL; - - list_for_each_safe(iter, temp, head) { - desc = list_entry(iter, rtk_prof_info, list); - if (((handle & 0xFFF) == desc->handle) && (dcid == desc->dcid) - && (scid == desc->scid)) { - return desc; - } - } - return NULL; -} - -static void rtk_vendor_cmd_to_fw(uint16_t opcode, uint8_t parameter_len, - uint8_t * parameter) -{ - int len = HCI_CMD_PREAMBLE_SIZE + parameter_len; - uint8_t *p; - struct sk_buff *skb; - struct hci_dev *hdev = btrtl_coex.hdev; - - if (!hdev) { - RTKBT_ERR("No HCI device"); - return; - } else if (!test_bit(HCI_UP, &hdev->flags)) { - RTKBT_WARN("HCI device is down"); - return; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - RTKBT_DBG("there is no room for cmd 0x%x", opcode); - return; - } - - p = (uint8_t *) skb_put(skb, HCI_CMD_PREAMBLE_SIZE); - UINT16_TO_STREAM(p, opcode); - *p++ = parameter_len; - - if (parameter_len) - memcpy(skb_put(skb, parameter_len), parameter, parameter_len); - - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - bt_cb(skb)->opcode = opcode; -#else - bt_cb(skb)->hci.opcode = opcode; -#endif -#endif - - /* Stand-alone HCI commands must be flagged as - * single-command requests. - */ -#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 4, 0) - bt_cb(skb)->req.start = true; -#else - -#if HCI_VERSION_CODE < KERNEL_VERSION(4, 5, 0) - bt_cb(skb)->hci.req_start = true; -#else - - bt_cb(skb)->hci.req_flags |= HCI_REQ_START; -#endif - -#endif /* 4.4.0 */ -#endif /* 3.10.0 */ - RTKBT_DBG("%s: opcode 0x%x", __func__, opcode); - - /* It is harmless if set skb->dev twice. The dev will be used in - * btusb_send_frame() after or equal to kernel/hci 3.13.0, - * the hdev will not come from skb->dev. */ -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 13, 0) - skb->dev = (void *)btrtl_coex.hdev; -#endif - /* Put the skb to the global hdev->cmd_q */ - skb_queue_tail(&hdev->cmd_q, skb); - -#if HCI_VERSION_CODE < KERNEL_VERSION(3, 3, 0) - tasklet_schedule(&hdev->cmd_task); -#else - queue_work(hdev->workqueue, &hdev->cmd_work); -#endif - - return; -} - -static void rtk_notify_profileinfo_to_fw(void) -{ - struct list_head *head = NULL; - struct list_head *iter = NULL; - struct list_head *temp = NULL; - rtk_conn_prof *hci_conn = NULL; - uint8_t handle_number = 0; - uint32_t buffer_size = 0; - uint8_t *p_buf = NULL; - uint8_t *p = NULL; - - head = &btrtl_coex.conn_hash; - list_for_each_safe(iter, temp, head) { - hci_conn = list_entry(iter, rtk_conn_prof, list); - if (hci_conn && hci_conn->profile_bitmap) - handle_number++; - } - - buffer_size = 1 + handle_number * 3 + 1; - - p_buf = kmalloc(buffer_size, GFP_ATOMIC); - - if (NULL == p_buf) { - RTKBT_ERR("%s: alloc error", __func__); - return; - } - p = p_buf; - - RTKBT_DBG("%s: BufferSize %u", __func__, buffer_size); - *p++ = handle_number; - RTKBT_DBG("%s: NumberOfHandles %u", __func__, handle_number); - head = &btrtl_coex.conn_hash; - list_for_each(iter, head) { - hci_conn = list_entry(iter, rtk_conn_prof, list); - if (hci_conn && hci_conn->profile_bitmap) { - UINT16_TO_STREAM(p, hci_conn->handle); - RTKBT_DBG("%s: handle 0x%04x", __func__, - hci_conn->handle); - *p++ = hci_conn->profile_bitmap; - RTKBT_DBG("%s: profile_bitmap 0x%02x", __func__, - hci_conn->profile_bitmap); - handle_number--; - } - if (0 == handle_number) - break; - } - - *p++ = btrtl_coex.profile_status; - RTKBT_DBG("%s: profile_status 0x%02x", __func__, - btrtl_coex.profile_status); - - rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_PROFILE_REPORT_COMMAND, buffer_size, - p_buf); - - kfree(p_buf); - return; -} - -static void update_profile_state(uint8_t profile_index, uint8_t is_busy) -{ - uint8_t need_update = FALSE; - - if ((btrtl_coex.profile_bitmap & BIT(profile_index)) == 0) { - RTKBT_ERR("%s: : ERROR!!! profile(Index: %x) does not exist", - __func__, profile_index); - return; - } - - if (is_busy) { - if ((btrtl_coex.profile_status & BIT(profile_index)) == 0) { - need_update = TRUE; - btrtl_coex.profile_status |= BIT(profile_index); - } - } else { - if ((btrtl_coex.profile_status & BIT(profile_index)) > 0) { - need_update = TRUE; - btrtl_coex.profile_status &= ~(BIT(profile_index)); - } - } - - if (need_update) { - RTKBT_DBG("%s: btrtl_coex.profie_bitmap = %x", - __func__, btrtl_coex.profile_bitmap); - RTKBT_DBG("%s: btrtl_coex.profile_status = %x", - __func__, btrtl_coex.profile_status); - rtk_notify_profileinfo_to_fw(); - } -} - -static void update_profile_connection(rtk_conn_prof * phci_conn, - int8_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); - if (profile_index < 0) - return; - - 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); - - rtk_check_setup_timer(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); - } - phci_conn->profile_refcount[profile_index]++; - } else { - if (!btrtl_coex.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)); - rtk_check_del_timer(profile_index); - } - - phci_conn->profile_refcount[profile_index]--; - if (0 == phci_conn->profile_refcount[profile_index]) { - need_update = TRUE; - phci_conn->profile_bitmap &= ~(BIT(profile_index)); - - /* clear profile_hid_interval if need */ - if ((profile_hid == profile_index) - && (phci_conn-> - profile_bitmap & (BIT(profile_hid_interval)))) { - phci_conn->profile_bitmap &= - ~(BIT(profile_hid_interval)); - btrtl_coex. - profile_refcount[profile_hid_interval]--; - } - } - } - - RTKBT_DBG("%s: btrtl_coex.profile_bitmap 0x%02x", __func__, - btrtl_coex.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(); -} - -static void update_hid_active_state(uint16_t handle, uint16_t interval) -{ - uint8_t need_update = 0; - rtk_conn_prof *phci_conn = - find_connection_by_handle(&btrtl_coex, handle); - - if (phci_conn == NULL) - return; - - RTKBT_DBG("%s: handle 0x%04x, interval %u", __func__, handle, interval); - if (((phci_conn->profile_bitmap) & (BIT(profile_hid))) == 0) { - RTKBT_DBG("HID not connected, nothing to be down"); - return; - } - - if (interval < 60) { - if ((phci_conn->profile_bitmap & (BIT(profile_hid_interval))) == - 0) { - need_update = 1; - phci_conn->profile_bitmap |= BIT(profile_hid_interval); - - btrtl_coex.profile_refcount[profile_hid_interval]++; - if (btrtl_coex. - profile_refcount[profile_hid_interval] == 1) - btrtl_coex.profile_status |= - BIT(profile_hid); - } - } else { - if ((phci_conn->profile_bitmap & (BIT(profile_hid_interval)))) { - need_update = 1; - phci_conn->profile_bitmap &= - ~(BIT(profile_hid_interval)); - - btrtl_coex.profile_refcount[profile_hid_interval]--; - if (btrtl_coex. - profile_refcount[profile_hid_interval] == 0) - btrtl_coex.profile_status &= - ~(BIT(profile_hid)); - } - } - - if (need_update) - rtk_notify_profileinfo_to_fw(); -} - -static uint8_t handle_l2cap_con_req(uint16_t handle, uint16_t psm, - uint16_t scid, uint8_t direction) -{ - uint8_t status = FALSE; - rtk_prof_info *prof_info = NULL; - int8_t profile_index = psm_to_profile_index(psm); - - if (profile_index < 0) { - RTKBT_DBG("PSM(0x%04x) do not need parse", psm); - return status; - } - - spin_lock(&btrtl_coex.spin_lock_profile); - if (direction) //1: out - prof_info = - find_profile_by_handle_scid(&btrtl_coex, handle, scid); - else // 0:in - prof_info = - find_profile_by_handle_dcid(&btrtl_coex, handle, scid); - - if (prof_info) { - RTKBT_DBG("%s: this profile is already exist!", __func__); - spin_unlock(&btrtl_coex.spin_lock_profile); - return status; - } - - if (direction) //1: out - status = list_allocate_add(handle, psm, profile_index, 0, scid); - else // 0:in - status = list_allocate_add(handle, psm, profile_index, scid, 0); - - spin_unlock(&btrtl_coex.spin_lock_profile); - - if (!status) - RTKBT_ERR("%s: list_allocate_add failed!", __func__); - - return status; -} - -static uint8_t handle_l2cap_con_rsp(uint16_t handle, uint16_t dcid, - uint16_t scid, uint8_t direction, - uint8_t result) -{ - rtk_prof_info *prof_info = NULL; - rtk_conn_prof *phci_conn = NULL; - - spin_lock(&btrtl_coex.spin_lock_profile); - 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("handle_l2cap_con_rsp: prof_info Not Find!!"); - spin_unlock(&btrtl_coex.spin_lock_profile); - return FALSE; - } - - if (!result) { //success - RTKBT_DBG("l2cap connection success, update connection"); - if (!direction) //0, in - prof_info->dcid = dcid; - else //1, out - prof_info->scid = dcid; - - phci_conn = find_connection_by_handle(&btrtl_coex, handle); - if (phci_conn) - update_profile_connection(phci_conn, - prof_info->profile_index, - TRUE); - } - - spin_unlock(&btrtl_coex.spin_lock_profile); - return TRUE; -} - -static uint8_t handle_l2cap_discon_req(uint16_t handle, uint16_t dcid, - uint16_t scid, uint8_t direction) -{ - rtk_prof_info *prof_info = NULL; - rtk_conn_prof *phci_conn = NULL; - 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); - if (!direction) //0: in - prof_info = - find_profile_by_handle_dcid_scid(&btrtl_coex, handle, - scid, dcid); - else //1: out - prof_info = - find_profile_by_handle_dcid_scid(&btrtl_coex, handle, - dcid, scid); - - if (!prof_info) { - //LogMsg("handle_l2cap_discon_req: prof_info Not Find!"); - spin_unlock(&btrtl_coex.spin_lock_profile); - return 0; - } - - phci_conn = find_connection_by_handle(&btrtl_coex, handle); - if (!phci_conn) { - spin_unlock(&btrtl_coex.spin_lock_profile); - return 0; - } - - update_profile_connection(phci_conn, prof_info->profile_index, FALSE); - if (prof_info->profile_index == profile_a2dp && - (phci_conn->profile_bitmap & BIT(profile_sink))) - update_profile_connection(phci_conn, profile_sink, FALSE); - - delete_profile_from_hash(prof_info); - spin_unlock(&btrtl_coex.spin_lock_profile); - - return 1; -} - -static const char sample_freqs[4][8] = { - "16", "32", "44.1", "48" -}; - -static const uint8_t sbc_blocks[4] = { 4, 8, 12, 16 }; - -static const char chan_modes[4][16] = { - "MONO", "DUAL_CHANNEL", "STEREO", "JOINT_STEREO" -}; - -static const char alloc_methods[2][12] = { - "LOUDNESS", "SNR" -}; - -static const uint8_t subbands[2] = { 4, 8 }; - -void print_sbc_header(struct sbc_frame_hdr *hdr) -{ - RTKBT_DBG("syncword: %02x", hdr->syncword); - RTKBT_DBG("freq %skHz", sample_freqs[hdr->sampling_frequency]); - RTKBT_DBG("blocks %u", sbc_blocks[hdr->blocks]); - RTKBT_DBG("channel mode %s", chan_modes[hdr->channel_mode]); - RTKBT_DBG("allocation method %s", - alloc_methods[hdr->allocation_method]); - 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) -{ - rtk_prof_info *prof_info = NULL; - - rtk_conn_prof *hci_conn = - find_connection_by_handle(&btrtl_coex, handle); - if (NULL == hci_conn) - 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 */ - if (prof_info->profile_index == profile_a2dp && - prof_info->flags == A2DP_MEDIA) { - if (!is_profile_busy(profile_a2dp)) { - struct sbc_frame_hdr *sbc_header; - struct rtp_header *rtph; - u8 bitpool; - - update_profile_state(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(profile_sink, TRUE); - } - - /* 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_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 *)user_data; - bitpool = sbc_header->bitpool; - - print_sbc_header(sbc_header); - - RTKBT_DBG("bitpool %u", bitpool); - - rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_BITPOOL, - 1, &bitpool); - } - } - btrtl_coex.a2dp_packet_count++; - } - - if (prof_info->profile_index == profile_pan) - btrtl_coex.pan_packet_count++; - } -} - -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) -static void count_a2dp_packet_timeout(struct timer_list *unused) -#else -static void count_a2dp_packet_timeout(unsigned long data) -#endif -{ - if (btrtl_coex.a2dp_packet_count) - RTKBT_DBG("%s: a2dp_packet_count %d", __func__, - btrtl_coex.a2dp_packet_count); - if (btrtl_coex.a2dp_packet_count == 0) { - if (is_profile_busy(profile_a2dp)) { - RTKBT_DBG("%s: a2dp busy->idle!", __func__); - update_profile_state(profile_a2dp, FALSE); - if (btrtl_coex.profile_bitmap & BIT(profile_sink)) - update_profile_state(profile_sink, FALSE); - } - } - btrtl_coex.a2dp_packet_count = 0; - mod_timer(&btrtl_coex.a2dp_count_timer, - jiffies + msecs_to_jiffies(1000)); -} - -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) -static void count_pan_packet_timeout(struct timer_list *unused) -#else -static void count_pan_packet_timeout(unsigned long data) -#endif -{ - if (btrtl_coex.pan_packet_count) - RTKBT_DBG("%s: pan_packet_count %d", __func__, - btrtl_coex.pan_packet_count); - if (btrtl_coex.pan_packet_count < PAN_PACKET_COUNT) { - if (is_profile_busy(profile_pan)) { - RTKBT_DBG("%s: pan busy->idle!", __func__); - update_profile_state(profile_pan, FALSE); - } - } else { - if (!is_profile_busy(profile_pan)) { - RTKBT_DBG("timeout_handler: pan idle->busy!"); - update_profile_state(profile_pan, TRUE); - } - } - btrtl_coex.pan_packet_count = 0; - mod_timer(&btrtl_coex.pan_count_timer, - jiffies + msecs_to_jiffies(1000)); -} - -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) -static void count_hogp_packet_timeout(struct timer_list *unused) -#else -static void count_hogp_packet_timeout(unsigned long data) -#endif -{ - if (btrtl_coex.hogp_packet_count) - RTKBT_DBG("%s: hogp_packet_count %d", __func__, - btrtl_coex.hogp_packet_count); - if (btrtl_coex.hogp_packet_count == 0) { - if (is_profile_busy(profile_hogp)) { - RTKBT_DBG("%s: hogp busy->idle!", __func__); - update_profile_state(profile_hogp, FALSE); - } - } - btrtl_coex.hogp_packet_count = 0; - - if (btrtl_coex.voice_packet_count) - RTKBT_DBG("%s: voice_packet_count %d", __func__, - btrtl_coex.voice_packet_count); - if (btrtl_coex.voice_packet_count == 0) { - if (is_profile_busy(profile_voice)) { - RTKBT_DBG("%s: voice busy->idle!", __func__); - update_profile_state(profile_voice, FALSE); - } - } - btrtl_coex.voice_packet_count = 0; - mod_timer(&btrtl_coex.hogp_count_timer, - jiffies + msecs_to_jiffies(1000)); -} - -#ifdef RTB_SOFTWARE_MAILBOX - -#ifndef RTK_COEX_OVER_SYMBOL -static int udpsocket_send(char *tx_msg, int msg_size) -{ - u8 error = 0; - struct msghdr udpmsg; - mm_segment_t oldfs; - struct iovec iov; - - RTKBT_DBG("send msg %s with len:%d", tx_msg, msg_size); - - if (btrtl_coex.sock_open) { - iov.iov_base = (void *)tx_msg; - iov.iov_len = msg_size; - udpmsg.msg_name = &btrtl_coex.wifi_addr; - udpmsg.msg_namelen = sizeof(struct sockaddr_in); -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) - udpmsg.msg_iov = &iov; - udpmsg.msg_iovlen = 1; -#else - iov_iter_init(&udpmsg.msg_iter, WRITE, &iov, 1, msg_size); -#endif - udpmsg.msg_control = NULL; - udpmsg.msg_controllen = 0; - udpmsg.msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL; - oldfs = get_fs(); - set_fs(KERNEL_DS); -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) - error = sock_sendmsg(btrtl_coex.udpsock, &udpmsg, msg_size); -#else - error = sock_sendmsg(btrtl_coex.udpsock, &udpmsg); -#endif - set_fs(oldfs); - - if (error < 0) - RTKBT_DBG("Error when sendimg msg, error:%d", error); - } - - return error; -} -#endif - -#ifdef RTK_COEX_OVER_SYMBOL -/* Receive message from WiFi */ -u8 rtw_btcoex_wifi_to_bt(u8 *msg, u8 msg_size) -{ - struct sk_buff *nskb; - - if (!rtw_coex_on) { - RTKBT_WARN("Bluetooth is closed"); - return 0; - } - - nskb = alloc_skb(msg_size, GFP_ATOMIC); - if (!nskb) { - RTKBT_ERR("Couldnt alloc skb for WiFi coex message"); - return 0; - } - - memcpy(skb_put(nskb, msg_size), msg, msg_size); - skb_queue_tail(&rtw_q, nskb); - - queue_work(rtw_wq, &rtw_work); - - return 1; -} -EXPORT_SYMBOL(rtw_btcoex_wifi_to_bt); - -static int rtk_send_coexmsg2wifi(u8 *msg, u8 size) -{ - u8 result; - u8 (*btmsg_to_wifi)(u8 *, u8); - - btmsg_to_wifi = __symbol_get(VMLINUX_SYMBOL_STR(rtw_btcoex_bt_to_wifi)); - - if (!btmsg_to_wifi) { - /* RTKBT_ERR("Couldnt get symbol"); */ - return -1; - } - - result = btmsg_to_wifi(msg, size); - __symbol_put(VMLINUX_SYMBOL_STR(rtw_btcoex_bt_to_wifi)); - if (!result) { - RTKBT_ERR("Couldnt send coex msg to WiFi"); - return -1; - } else if (result == 1){ - /* successful to send message */ - return 0; - } else { - RTKBT_ERR("Unknown result %d", result); - return -1; - } -} - -static int rtkbt_process_coexskb(struct sk_buff *skb) -{ - rtk_handle_event_from_wifi(skb->data); - return 0; -} - -static void rtw_work_func(struct work_struct *work) -{ - struct sk_buff *skb; - - while ((skb = skb_dequeue(&rtw_q))) { - rtkbt_process_coexskb(skb); - kfree_skb(skb); - } -} - -#endif - -static int rtkbt_coexmsg_send(char *tx_msg, int msg_size) -{ -#ifdef RTK_COEX_OVER_SYMBOL - return rtk_send_coexmsg2wifi((uint8_t *)tx_msg, (u8)msg_size); -#else - return udpsocket_send(tx_msg, msg_size); -#endif -} - -#ifndef RTK_COEX_OVER_SYMBOL -static void udpsocket_recv_data(void) -{ - u8 recv_data[512]; - u32 len = 0; - u16 recv_length; - struct sk_buff *skb; - - RTKBT_DBG("-"); - - spin_lock(&btrtl_coex.spin_lock_sock); - len = skb_queue_len(&btrtl_coex.sk->sk_receive_queue); - - while (len > 0) { - skb = skb_dequeue(&btrtl_coex.sk->sk_receive_queue); - - /*important: cut the udp header from skb->data! header length is 8 byte */ - recv_length = skb->len - 8; - memset(recv_data, 0, sizeof(recv_data)); - memcpy(recv_data, skb->data + 8, recv_length); - //RTKBT_DBG("received data: %s :with len %u", recv_data, recv_length); - - rtk_handle_event_from_wifi(recv_data); - - len--; - kfree_skb(skb); - } - - spin_unlock(&btrtl_coex.spin_lock_sock); -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) -static void udpsocket_recv(struct sock *sk, int bytes) -#else -static void udpsocket_recv(struct sock *sk) -#endif -{ - spin_lock(&btrtl_coex.spin_lock_sock); - btrtl_coex.sk = sk; - spin_unlock(&btrtl_coex.spin_lock_sock); - queue_delayed_work(btrtl_coex.sock_wq, &btrtl_coex.sock_work, 0); -} - -static void create_udpsocket(void) -{ - int err; - RTKBT_DBG("%s: connect_port: %d", __func__, CONNECT_PORT); - btrtl_coex.sock_open = 0; - - err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, - &btrtl_coex.udpsock); - if (err < 0) { - RTKBT_ERR("%s: sock create error, err = %d", __func__, err); - return; - } - - memset(&btrtl_coex.addr, 0, sizeof(struct sockaddr_in)); - btrtl_coex.addr.sin_family = AF_INET; - btrtl_coex.addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - btrtl_coex.addr.sin_port = htons(CONNECT_PORT); - - memset(&btrtl_coex.wifi_addr, 0, sizeof(struct sockaddr_in)); - btrtl_coex.wifi_addr.sin_family = AF_INET; - btrtl_coex.wifi_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - btrtl_coex.wifi_addr.sin_port = htons(CONNECT_PORT_WIFI); - - err = - btrtl_coex.udpsock->ops->bind(btrtl_coex.udpsock, - (struct sockaddr *)&btrtl_coex. - addr, sizeof(struct sockaddr)); - if (err < 0) { - sock_release(btrtl_coex.udpsock); - RTKBT_ERR("%s: sock bind error, err = %d",__func__, err); - return; - } - - btrtl_coex.sock_open = 1; - btrtl_coex.udpsock->sk->sk_data_ready = udpsocket_recv; -} -#endif /* !RTK_COEX_OVER_SYMBOL */ - -static void rtk_notify_extension_version_to_wifi(void) -{ - uint8_t para_length = 2; - char p_buf[2 + HCI_CMD_PREAMBLE_SIZE]; - char *p = p_buf; - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_HCI_EXTENSION_VERSION_NOTIFY); - *p++ = para_length; - UINT16_TO_STREAM(p, HCI_EXTENSION_VERSION); - RTKBT_DBG("extension version is 0x%x", HCI_EXTENSION_VERSION); - if (rtkbt_coexmsg_send(p_buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -static void rtk_notify_btpatch_version_to_wifi(void) -{ - uint8_t para_length = 4; - char p_buf[para_length + HCI_CMD_PREAMBLE_SIZE]; - char *p = p_buf; - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_HCI_BT_PATCH_VER_NOTIFY); - *p++ = para_length; - UINT16_TO_STREAM(p, btrtl_coex.hci_reversion); - UINT16_TO_STREAM(p, btrtl_coex.lmp_subversion); - RTKBT_DBG("btpatch ver: len %u, hci_rev 0x%04x, lmp_subver 0x%04x", - para_length, btrtl_coex.hci_reversion, - btrtl_coex.lmp_subversion); - - if (rtkbt_coexmsg_send(p_buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -static void rtk_notify_afhmap_to_wifi(void) -{ - uint8_t para_length = 13; - char p_buf[para_length + HCI_CMD_PREAMBLE_SIZE]; - char *p = p_buf; - uint8_t kk = 0; - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_HCI_BT_AFH_MAP_NOTIFY); - *p++ = para_length; - *p++ = btrtl_coex.piconet_id; - *p++ = btrtl_coex.mode; - *p++ = 10; - memcpy(p, btrtl_coex.afh_map, 10); - - RTKBT_DBG("afhmap, piconet_id is 0x%x, map type is 0x%x", - btrtl_coex.piconet_id, btrtl_coex.mode); - for (kk = 0; kk < 10; kk++) - RTKBT_DBG("afhmap data[%d] is 0x%x", kk, - btrtl_coex.afh_map[kk]); - - if (rtkbt_coexmsg_send(p_buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -static void rtk_notify_btcoex_to_wifi(uint8_t opcode, uint8_t status) -{ - uint8_t para_length = 2; - char p_buf[para_length + HCI_CMD_PREAMBLE_SIZE]; - char *p = p_buf; - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_HCI_BT_COEX_NOTIFY); - *p++ = para_length; - *p++ = opcode; - if (!status) - *p++ = 0; - else - *p++ = 1; - - RTKBT_DBG("btcoex, opcode is 0x%x, status is 0x%x", opcode, status); - - if (rtkbt_coexmsg_send(p_buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -static void rtk_notify_btoperation_to_wifi(uint8_t operation, - uint8_t append_data_length, - uint8_t * append_data) -{ - uint8_t para_length = 3 + append_data_length; - char p_buf[para_length + HCI_CMD_PREAMBLE_SIZE]; - char *p = p_buf; - uint8_t kk = 0; - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_BT_OPERATION_NOTIFY); - *p++ = para_length; - *p++ = operation; - *p++ = append_data_length; - if (append_data_length) - memcpy(p, append_data, append_data_length); - - RTKBT_DBG("btoperation: op 0x%02x, append_data_length %u", - operation, append_data_length); - if (append_data_length) { - for (kk = 0; kk < append_data_length; kk++) - RTKBT_DBG("append data is 0x%x", *(append_data + kk)); - } - - if (rtkbt_coexmsg_send(p_buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -static void rtk_notify_info_to_wifi(uint8_t reason, uint8_t length, - uint8_t *report_info) -{ - uint8_t para_length = 4 + length; - char buf[para_length + HCI_CMD_PREAMBLE_SIZE]; - char *p = buf; - struct rtl_btinfo *report = (struct rtl_btinfo *)report_info; - - if (length) { - RTKBT_DBG("bt info: cmd %2.2X", report->cmd); - RTKBT_DBG("bt info: len %2.2X", report->len); - RTKBT_DBG("bt info: data %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X", - report->data[0], report->data[1], report->data[2], - report->data[3], report->data[4], report->data[5]); - } - RTKBT_DBG("bt info: reason 0x%2x, length 0x%2x", reason, length); - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_HCI_BT_INFO_NOTIFY); - *p++ = para_length; - *p++ = btrtl_coex.polling_enable; - *p++ = btrtl_coex.polling_interval; - *p++ = reason; - *p++ = length; - - if (length) - memcpy(p, report_info, length); - - RTKBT_DBG("para length %2x, polling_enable %u, poiiling_interval %u", - para_length, btrtl_coex.polling_enable, - btrtl_coex.polling_interval); - /* send BT INFO to Wi-Fi driver */ - if (rtkbt_coexmsg_send(buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -static void rtk_notify_regester_to_wifi(uint8_t * reg_value) -{ - uint8_t para_length = 9; - char p_buf[para_length + HCI_CMD_PREAMBLE_SIZE]; - char *p = p_buf; - hci_mailbox_register *reg = (hci_mailbox_register *) reg_value; - - if (!btrtl_coex.wifi_on) - return; - - UINT16_TO_STREAM(p, HCI_OP_HCI_BT_REGISTER_VALUE_NOTIFY); - *p++ = para_length; - memcpy(p, reg_value, para_length); - - RTKBT_DBG("bt register, register type is %x", reg->type); - RTKBT_DBG("bt register, register offset is %x", reg->offset); - RTKBT_DBG("bt register, register value is %x", reg->value); - - if (rtkbt_coexmsg_send(p_buf, para_length + HCI_CMD_PREAMBLE_SIZE) < 0) - RTKBT_ERR("%s: sock send error", __func__); -} - -#endif - -void rtk_btcoex_parse_cmd(uint8_t *buffer, int count) -{ - u16 opcode = (buffer[0]) + (buffer[1] << 8); - - if (!test_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { - RTKBT_INFO("%s: Coex is closed, ignore", __func__); - return; - } - - switch (opcode) { - case HCI_OP_INQUIRY: - case HCI_OP_PERIODIC_INQ: - if (!btrtl_coex.isinquirying) { - btrtl_coex.isinquirying = 1; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci (periodic)inq, notify wifi " - "inquiry start"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_INQUIRY_START, - 0, NULL); -#else - RTKBT_INFO("hci (periodic)inq start"); -#endif - } - break; - case HCI_OP_INQUIRY_CANCEL: - case HCI_OP_EXIT_PERIODIC_INQ: - if (btrtl_coex.isinquirying) { - btrtl_coex.isinquirying = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci (periodic)inq cancel/exit, notify wifi " - "inquiry stop"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_INQUIRY_END, 0, - NULL); -#else - RTKBT_INFO("hci (periodic)inq cancel/exit"); -#endif - } - break; - case HCI_OP_ACCEPT_CONN_REQ: - if (!btrtl_coex.ispaging) { - btrtl_coex.ispaging = 1; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci accept connreq, notify wifi page start"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAGE_START, 0, - NULL); -#else - RTKBT_INFO("hci accept conn req"); -#endif - } - break; - case HCI_OP_DISCONNECT: - RTKBT_INFO("HCI Disconnect, handle %04x, reason 0x%02x", - ((u16)buffer[4] << 8 | buffer[3]), buffer[5]); - break; - default: - break; - } -} - -static void rtk_handle_inquiry_complete(void) -{ - if (btrtl_coex.isinquirying) { - btrtl_coex.isinquirying = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("inq complete, notify wifi inquiry end"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_INQUIRY_END, 0, NULL); -#else - RTKBT_INFO("inquiry complete"); -#endif - } -} - -static void rtk_handle_pin_code_req(void) -{ - if (!btrtl_coex.ispairing) { - btrtl_coex.ispairing = 1; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("pin code req, notify wifi pair start"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAIR_START, 0, NULL); -#else - RTKBT_INFO("pin code request"); -#endif - } -} - -static void rtk_handle_io_capa_req(void) -{ - if (!btrtl_coex.ispairing) { - btrtl_coex.ispairing = 1; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("io cap req, notify wifi pair start"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAIR_START, 0, NULL); -#else - RTKBT_INFO("io capability request"); -#endif - } -} - -static void rtk_handle_auth_request(void) -{ - if (btrtl_coex.ispairing) { - btrtl_coex.ispairing = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("auth req, notify wifi pair end"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAIR_END, 0, NULL); -#else - RTKBT_INFO("authentication request"); -#endif - } -} - -static void rtk_handle_link_key_notify(void) -{ - if (btrtl_coex.ispairing) { - btrtl_coex.ispairing = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("link key notify, notify wifi pair end"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAIR_END, 0, NULL); -#else - RTKBT_INFO("link key notify"); -#endif - } -} - -static void rtk_handle_mode_change_evt(u8 * p) -{ - u16 mode_change_handle, mode_interval; - - p++; - STREAM_TO_UINT16(mode_change_handle, p); - p++; - STREAM_TO_UINT16(mode_interval, p); - update_hid_active_state(mode_change_handle, mode_interval); -} - -#ifdef RTB_SOFTWARE_MAILBOX -static void rtk_parse_vendor_mailbox_cmd_evt(u8 * p, u8 total_len) -{ - u8 status, subcmd; - u8 temp_cmd[10]; - - status = *p++; - if (total_len <= 4) { - RTKBT_DBG("receive mailbox cmd from fw, total length <= 4"); - return; - } - subcmd = *p++; - RTKBT_DBG("receive mailbox cmd from fw, subcmd is 0x%x, status is 0x%x", - subcmd, status); - - switch (subcmd) { - case HCI_VENDOR_SUB_CMD_BT_REPORT_CONN_SCO_INQ_INFO: - if (status == 0) //success - rtk_notify_info_to_wifi(POLLING_RESPONSE, - RTL_BTINFO_LEN, (uint8_t *)p); - break; - - case HCI_VENDOR_SUB_CMD_WIFI_CHANNEL_AND_BANDWIDTH_CMD: - rtk_notify_btcoex_to_wifi(WIFI_BW_CHNL_NOTIFY, status); - break; - - case HCI_VENDOR_SUB_CMD_WIFI_FORCE_TX_POWER_CMD: - rtk_notify_btcoex_to_wifi(BT_POWER_DECREASE_CONTROL, status); - break; - - case HCI_VENDOR_SUB_CMD_BT_ENABLE_IGNORE_WLAN_ACT_CMD: - rtk_notify_btcoex_to_wifi(IGNORE_WLAN_ACTIVE_CONTROL, status); - break; - - case HCI_VENDOR_SUB_CMD_SET_BT_PSD_MODE: - rtk_notify_btcoex_to_wifi(BT_PSD_MODE_CONTROL, status); - break; - - case HCI_VENDOR_SUB_CMD_SET_BT_LNA_CONSTRAINT: - rtk_notify_btcoex_to_wifi(LNA_CONSTRAIN_CONTROL, status); - break; - - case HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_ENABLE: - break; - - case HCI_VENDOR_SUB_CMD_BT_SET_TXRETRY_REPORT_PARAM: - break; - - case HCI_VENDOR_SUB_CMD_BT_SET_PTATABLE: - break; - - case HCI_VENDOR_SUB_CMD_GET_AFH_MAP_L: - if (status == 0) { - memcpy(btrtl_coex.afh_map, p + 4, 4); /* cmd_idx, length, piconet_id, mode */ - temp_cmd[0] = HCI_VENDOR_SUB_CMD_GET_AFH_MAP_M; - temp_cmd[1] = 2; - temp_cmd[2] = btrtl_coex.piconet_id; - temp_cmd[3] = btrtl_coex.mode; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 4, - temp_cmd); - } else { - memset(btrtl_coex.afh_map, 0, 10); - rtk_notify_afhmap_to_wifi(); - } - break; - - case HCI_VENDOR_SUB_CMD_GET_AFH_MAP_M: - if (status == 0) { - memcpy(btrtl_coex.afh_map + 4, p + 4, 4); - temp_cmd[0] = HCI_VENDOR_SUB_CMD_GET_AFH_MAP_H; - temp_cmd[1] = 2; - temp_cmd[2] = btrtl_coex.piconet_id; - temp_cmd[3] = btrtl_coex.mode; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 4, - temp_cmd); - } else { - memset(btrtl_coex.afh_map, 0, 10); - rtk_notify_afhmap_to_wifi(); - } - break; - - case HCI_VENDOR_SUB_CMD_GET_AFH_MAP_H: - if (status == 0) - memcpy(btrtl_coex.afh_map + 8, p + 4, 2); - else - memset(btrtl_coex.afh_map, 0, 10); - - rtk_notify_afhmap_to_wifi(); - break; - - case HCI_VENDOR_SUB_CMD_RD_REG_REQ: - if (status == 0) - rtk_notify_regester_to_wifi(p + 3); /* cmd_idx,length,regist type */ - break; - - case HCI_VENDOR_SUB_CMD_WR_REG_REQ: - rtk_notify_btcoex_to_wifi(BT_REGISTER_ACCESS, status); - break; - - default: - break; - } -} -#endif /* RTB_SOFTWARE_MAILBOX */ - -static void rtk_handle_cmd_complete_evt(u8 total_len, u8 * p) -{ - u16 opcode; - - p++; - STREAM_TO_UINT16(opcode, p); - //RTKBT_DBG("cmd_complete, opcode is 0x%x", opcode); - - if (opcode == HCI_OP_PERIODIC_INQ) { - if (*p++ && btrtl_coex.isinquirying) { - btrtl_coex.isinquirying = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci period inq, start error, notify wifi " - "inquiry stop"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_INQUIRY_END, 0, - NULL); -#else - RTKBT_INFO("hci period inquiry start error"); -#endif - } - } - - if (opcode == HCI_OP_READ_LOCAL_VERSION) { - if (!(*p++)) { - p++; - STREAM_TO_UINT16(btrtl_coex.hci_reversion, p); - p += 3; - STREAM_TO_UINT16(btrtl_coex.lmp_subversion, p); - RTKBT_DBG("BTCOEX hci_rev 0x%04x", - btrtl_coex.hci_reversion); - RTKBT_DBG("BTCOEX lmp_subver 0x%04x", - btrtl_coex.lmp_subversion); - } - } - -#ifdef RTB_SOFTWARE_MAILBOX - if (opcode == HCI_VENDOR_MAILBOX_CMD) { - rtk_parse_vendor_mailbox_cmd_evt(p, total_len); - } -#endif -} - -static void rtk_handle_cmd_status_evt(u8 * p) -{ - u16 opcode; - u8 status; - - status = *p++; - p++; - STREAM_TO_UINT16(opcode, p); - //RTKBT_DBG("cmd_status, opcode is 0x%x", opcode); - if ((opcode == HCI_OP_INQUIRY) && (status)) { - if (btrtl_coex.isinquirying) { - btrtl_coex.isinquirying = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci inq, start error, notify wifi inq stop"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_INQUIRY_END, 0, - NULL); -#else - RTKBT_INFO("hci inquiry start error"); -#endif - } - } - - if (opcode == HCI_OP_CREATE_CONN) { - if (!status && !btrtl_coex.ispaging) { - btrtl_coex.ispaging = 1; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci create conn, notify wifi start page"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAGE_START, 0, - NULL); -#else - RTKBT_INFO("hci create connection, start paging"); -#endif - } - } -} - -static void rtk_handle_connection_complete_evt(u8 * p) -{ - u16 handle; - u8 status, link_type; - rtk_conn_prof *hci_conn = NULL; - - status = *p++; - STREAM_TO_UINT16(handle, p); - p += 6; - link_type = *p++; - - RTKBT_INFO("connected, handle %04x, status 0x%02x", handle, status); - - if (status == 0) { - if (btrtl_coex.ispaging) { - btrtl_coex.ispaging = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("notify wifi page success end"); - rtk_notify_btoperation_to_wifi - (BT_OPCODE_PAGE_SUCCESS_END, 0, NULL); -#else - RTKBT_INFO("Page success"); -#endif - } - - hci_conn = find_connection_by_handle(&btrtl_coex, handle); - 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; - memset(hci_conn->profile_refcount, 0, 8); - if ((0 == link_type) || (2 == link_type)) { //sco or esco - hci_conn->type = 1; - update_profile_connection(hci_conn, - profile_sco, - TRUE); - } else - hci_conn->type = 0; - } else { - RTKBT_ERR("hci connection allocate fail"); - } - } else { - RTKBT_DBG("hci conn handle 0x%04x already existed!", - 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; - update_profile_connection(hci_conn, profile_sco, - TRUE); - } else - hci_conn->type = 0; - } - } else if (btrtl_coex.ispaging) { - btrtl_coex.ispaging = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("notify wifi page unsuccess end"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAGE_UNSUCCESS_END, 0, - NULL); -#else - RTKBT_INFO("Page failed"); -#endif - } -} - -static void rtk_handle_le_connection_complete_evt(u8 enhanced, u8 * p) -{ - u16 handle, interval; - u8 status; - rtk_conn_prof *hci_conn = NULL; - - status = *p++; - STREAM_TO_UINT16(handle, p); - if (!enhanced) - p += 8; /* role, address type, address */ - else - p += (8 + 12); /* plus two bluetooth addresses */ - STREAM_TO_UINT16(interval, p); - - RTKBT_INFO("LE connected, handle %04x, status 0x%02x, interval %u", - handle, status, interval); - - if (status == 0) { - if (btrtl_coex.ispaging) { - btrtl_coex.ispaging = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("notify wifi page success end"); - rtk_notify_btoperation_to_wifi - (BT_OPCODE_PAGE_SUCCESS_END, 0, NULL); -#else - RTKBT_INFO("Page success end"); -#endif - } - - hci_conn = find_connection_by_handle(&btrtl_coex, handle); - 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; - memset(hci_conn->profile_refcount, 0, 8); - hci_conn->type = 2; - update_profile_connection(hci_conn, profile_hid, TRUE); //for coex, le is the same as hid - update_hid_active_state(handle, interval); - } else { - RTKBT_ERR("hci connection allocate fail"); - } - } else { - RTKBT_DBG("hci conn handle 0x%04x already existed!", - handle); - hci_conn->profile_bitmap = 0; - memset(hci_conn->profile_refcount, 0, 8); - hci_conn->type = 2; - update_profile_connection(hci_conn, profile_hid, TRUE); - update_hid_active_state(handle, interval); - } - } else if (btrtl_coex.ispaging) { - btrtl_coex.ispaging = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("notify wifi page unsuccess end"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAGE_UNSUCCESS_END, 0, - NULL); -#else - RTKBT_INFO("Page failed"); -#endif - } -} - -static void rtk_handle_le_connection_update_complete_evt(u8 * p) -{ - u16 handle, interval; - /* u8 status; */ - - /* status = *p++; */ - p++; - - STREAM_TO_UINT16(handle, p); - STREAM_TO_UINT16(interval, p); - update_hid_active_state(handle, interval); -} - -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); - break; - 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; - - default: - break; - } -} - -static u8 disconn_profile(struct rtl_hci_conn *conn, u8 pfe_index) -{ - u8 need_update = 0; - - if (!btrtl_coex.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); - } - - if (conn->profile_refcount[pfe_index]) - conn->profile_refcount[pfe_index]--; - else - RTKBT_INFO("%s: conn pfe ref[%u] is 0", __func__, - conn->profile_refcount[pfe_index]); - if (!conn->profile_refcount[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]--; - } - } - - return need_update; -} - -static void disconn_acl(u16 handle, struct rtl_hci_conn *conn) -{ - struct rtl_coex_struct *coex = &btrtl_coex; - rtk_prof_info *prof_info = NULL; - struct list_head *iter = NULL, *temp = NULL; - u8 need_update = 0; - - spin_lock(&coex->spin_lock_profile); - - list_for_each_safe(iter, temp, &coex->profile_list) { - prof_info = list_entry(iter, rtk_prof_info, list); - if (handle == prof_info->handle) { - RTKBT_DBG("hci disconn, hndl %x, psm %x, dcid %x, " - "scid %x, profile %u", prof_info->handle, - prof_info->psm, prof_info->dcid, - prof_info->scid, prof_info->profile_index); - //If both scid and dcid > 0, L2cap connection is exist. - need_update |= disconn_profile(conn, - prof_info->profile_index); - if ((prof_info->flags & A2DP_MEDIA) && - (conn->profile_bitmap & BIT(profile_sink))) - need_update |= disconn_profile(conn, - profile_sink); - delete_profile_from_hash(prof_info); - } - } - if (need_update) - rtk_notify_profileinfo_to_fw(); - spin_unlock(&coex->spin_lock_profile); -} - -static void rtk_handle_disconnect_complete_evt(u8 * p) -{ - u16 handle; - u8 status; - u8 reason; - rtk_conn_prof *hci_conn = NULL; - - if (btrtl_coex.ispairing) { //for slave: connection will be disconnected if authentication fail - btrtl_coex.ispairing = 0; -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("hci disc complete, notify wifi pair end"); - rtk_notify_btoperation_to_wifi(BT_OPCODE_PAIR_END, 0, NULL); -#else - RTKBT_INFO("hci disconnection complete"); -#endif - } - - status = *p++; - STREAM_TO_UINT16(handle, p); - reason = *p; - - RTKBT_INFO("disconn cmpl evt: status 0x%02x, handle %04x, reason 0x%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 - RTKBT_ERR("hci conn handle 0x%04x not found", handle); - } -} - -static void rtk_handle_specific_evt(u8 * p) -{ - u16 subcode; - - STREAM_TO_UINT16(subcode, p); - if (subcode == HCI_VENDOR_PTA_AUTO_REPORT_EVENT) { -#ifdef RTB_SOFTWARE_MAILBOX - RTKBT_DBG("notify wifi driver with autoreport data"); - rtk_notify_info_to_wifi(AUTO_REPORT, RTL_BTINFO_LEN, - (uint8_t *)p); -#else - RTKBT_INFO("auto report data"); -#endif - } -} - -static void rtk_parse_event_data(struct rtl_coex_struct *coex, - u8 *data, u16 len) -{ - u8 *p = data; - u8 event_code = *p++; - u8 total_len = *p++; - - (void)coex; - (void)&len; - - switch (event_code) { - case HCI_EV_INQUIRY_COMPLETE: - rtk_handle_inquiry_complete(); - break; - - case HCI_EV_PIN_CODE_REQ: - rtk_handle_pin_code_req(); - break; - - case HCI_EV_IO_CAPA_REQUEST: - rtk_handle_io_capa_req(); - break; - - case HCI_EV_AUTH_COMPLETE: - rtk_handle_auth_request(); - break; - - case HCI_EV_LINK_KEY_NOTIFY: - rtk_handle_link_key_notify(); - break; - - case HCI_EV_MODE_CHANGE: - rtk_handle_mode_change_evt(p); - break; - - case HCI_EV_CMD_COMPLETE: - rtk_handle_cmd_complete_evt(total_len, p); - break; - - case HCI_EV_CMD_STATUS: - rtk_handle_cmd_status_evt(p); - break; - - case HCI_EV_CONN_COMPLETE: - case HCI_EV_SYNC_CONN_COMPLETE: - rtk_handle_connection_complete_evt(p); - break; - - case HCI_EV_DISCONN_COMPLETE: - rtk_handle_disconnect_complete_evt(p); - break; - - case HCI_EV_LE_META: - rtk_handle_le_meta_evt(p); - break; - - case HCI_EV_VENDOR_SPECIFIC: - rtk_handle_specific_evt(p); - break; - - default: - break; - } -} - -const char l2_dir_str[][4] = { - "RX", "TX", -}; - -void rtl_process_l2_sig(struct rtl_l2_buff *l2) -{ - /* u8 flag; */ - u8 code; - /* u8 identifier; */ - u16 handle; - /* u16 total_len; */ - /* u16 pdu_len, channel_id; */ - /* u16 command_len; */ - u16 psm, scid, dcid, result; - /* u16 status; */ - u8 *pp = l2->data; - - STREAM_TO_UINT16(handle, pp); - /* flag = handle >> 12; */ - handle = handle & 0x0FFF; - /* STREAM_TO_UINT16(total_len, pp); */ - pp += 2; /* data total length */ - - /* STREAM_TO_UINT16(pdu_len, pp); - * STREAM_TO_UINT16(channel_id, pp); */ - pp += 4; /* l2 len and channel id */ - - code = *pp++; - switch (code) { - case L2CAP_CONN_REQ: - /* identifier = *pp++; */ - pp++; - /* STREAM_TO_UINT16(command_len, pp); */ - pp += 2; - STREAM_TO_UINT16(psm, pp); - STREAM_TO_UINT16(scid, pp); - RTKBT_DBG("%s l2cap conn req, hndl 0x%04x, PSM 0x%04x, " - "scid 0x%04x", l2_dir_str[l2->out], handle, psm, - scid); - handle_l2cap_con_req(handle, psm, scid, l2->out); - break; - - case L2CAP_CONN_RSP: - /* identifier = *pp++; */ - pp++; - /* STREAM_TO_UINT16(command_len, pp); */ - pp += 2; - STREAM_TO_UINT16(dcid, pp); - STREAM_TO_UINT16(scid, pp); - STREAM_TO_UINT16(result, pp); - /* STREAM_TO_UINT16(status, pp); */ - pp += 2; - RTKBT_DBG("%s l2cap conn rsp, hndl 0x%04x, dcid 0x%04x, " - "scid 0x%04x, result 0x%04x", l2_dir_str[l2->out], - handle, dcid, scid, result); - handle_l2cap_con_rsp(handle, dcid, scid, l2->out, result); - break; - - case L2CAP_DISCONN_REQ: - /* identifier = *pp++; */ - pp++; - /* STREAM_TO_UINT16(command_len, pp); */ - pp += 2; - STREAM_TO_UINT16(dcid, pp); - STREAM_TO_UINT16(scid, pp); - RTKBT_DBG("%s l2cap disconn req, hndl 0x%04x, dcid 0x%04x, " - "scid 0x%04x", l2_dir_str[l2->out], handle, dcid, scid); - handle_l2cap_discon_req(handle, dcid, scid, l2->out); - break; - default: - RTKBT_DBG("undesired l2 command %u", code); - break; - } -} - -static void rtl_l2_data_process(u8 *pp, u16 len, int dir) -{ - u8 code; - u8 flag; - u16 handle, pdu_len, channel_id; - /* u16 total_len; */ - struct rtl_l2_buff *l2 = NULL; - u8 *hd = pp; - - /* RTKBT_DBG("l2 sig data %p, len %u, dir %d", pp, len, dir); */ - - STREAM_TO_UINT16(handle, pp); - flag = handle >> 12; - handle = handle & 0x0FFF; - /* STREAM_TO_UINT16(total_len, pp); */ - pp += 2; /* data total length */ - - STREAM_TO_UINT16(pdu_len, pp); - STREAM_TO_UINT16(channel_id, pp); - - if (channel_id == 0x0001) { - code = *pp++; - switch (code) { - case L2CAP_CONN_REQ: - case L2CAP_CONN_RSP: - case L2CAP_DISCONN_REQ: - RTKBT_DBG("l2cap op %u, len %u, out %d", code, len, - dir); - l2 = rtl_l2_node_get(&btrtl_coex); - if (l2) { - u16 n; - n = min_t(uint, len, L2_MAX_SUBSEC_LEN); - memcpy(l2->data, hd, n); - l2->out = dir; - rtl_l2_node_to_used(&btrtl_coex, l2); - queue_delayed_work(btrtl_coex.fw_wq, - &btrtl_coex.l2_work, 0); - } else - RTKBT_ERR("%s: failed to get l2 node", - __func__); - break; - case L2CAP_DISCONN_RSP: - break; - default: - 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); - } - return; -} - - -static void rtl_l2_work(struct work_struct *work) -{ - struct rtl_coex_struct *coex; - struct rtl_l2_buff *l2; - unsigned long flags; - - 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); - } - spin_unlock_irqrestore(&coex->buff_lock, flags); - - return; -} - -static void rtl_ev_work(struct work_struct *work) -{ - struct rtl_coex_struct *coex; - struct rtl_hci_ev *ev; - unsigned long flags; - - 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); - spin_unlock_irqrestore(&coex->buff_lock, flags); - - rtk_parse_event_data(coex, ev->data, ev->len); - - spin_lock_irqsave(&coex->buff_lock, flags); - list_add_tail(&ev->list, &coex->ev_free_list); - } - spin_unlock_irqrestore(&coex->buff_lock, flags); -} - -static inline int cmd_cmplt_filter_out(u8 *buf) -{ - u16 opcode; - - opcode = buf[3] | (buf[4] << 8); - switch (opcode) { - case HCI_OP_PERIODIC_INQ: - case HCI_OP_READ_LOCAL_VERSION: -#ifdef RTB_SOFTWARE_MAILBOX - case HCI_VENDOR_MAILBOX_CMD: -#endif - return 0; - default: - return 1; - } -} - -static inline int cmd_status_filter_out(u8 *buf) -{ - u16 opcode; - - opcode = buf[4] | (buf[5] << 8); - switch (opcode) { - case HCI_OP_INQUIRY: - case HCI_OP_CREATE_CONN: - return 0; - default: - return 1; - } -} - -int ev_filter_out(u8 *buf) -{ - switch (buf[0]) { - case HCI_EV_INQUIRY_COMPLETE: - case HCI_EV_PIN_CODE_REQ: - case HCI_EV_IO_CAPA_REQUEST: - case HCI_EV_AUTH_COMPLETE: - case HCI_EV_LINK_KEY_NOTIFY: - case HCI_EV_MODE_CHANGE: - case HCI_EV_CONN_COMPLETE: - case HCI_EV_SYNC_CONN_COMPLETE: - case HCI_EV_DISCONN_COMPLETE: - case HCI_EV_VENDOR_SPECIFIC: - return 0; - case HCI_EV_LE_META: - /* Ignore frequent but not useful events that result in - * costing too much space. - */ - switch (buf[2]) { - case HCI_EV_LE_CONN_COMPLETE: - case HCI_EV_LE_ENHANCED_CONN_COMPLETE: - case HCI_EV_LE_CONN_UPDATE_COMPLETE: - return 0; - } - return 1; - case HCI_EV_CMD_COMPLETE: - return cmd_cmplt_filter_out(buf); - case HCI_EV_CMD_STATUS: - return cmd_status_filter_out(buf); - default: - return 1; - } -} - -static void rtk_btcoex_evt_enqueue(__u8 *s, __u16 count) -{ - struct rtl_hci_ev *ev; - - if (ev_filter_out(s)) - return; - - ev = rtl_ev_node_get(&btrtl_coex); - if (!ev) { - RTKBT_ERR("%s: no free ev node.", __func__); - return; - } - - if (count > MAX_LEN_OF_HCI_EV) { - memcpy(ev->data, s, MAX_LEN_OF_HCI_EV); - ev->len = MAX_LEN_OF_HCI_EV; - } else { - memcpy(ev->data, s, count); - ev->len = count; - } - - rtl_ev_node_to_used(&btrtl_coex, ev); - - queue_delayed_work(btrtl_coex.fw_wq, &btrtl_coex.fw_work, 0); -} - -/* Context: in_interrupt() */ -void rtk_btcoex_parse_event(uint8_t *buffer, int count) -{ - struct rtl_coex_struct *coex = &btrtl_coex; - __u8 *tbuff; - __u16 elen = 0; - - /* RTKBT_DBG("%s: parse ev.", __func__); */ - if (!test_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { - /* RTKBT_INFO("%s: Coex is closed, ignore", __func__); */ - RTKBT_INFO("%s: Coex is closed, ignore %x, %x", - __func__, buffer[0], buffer[1]); - return; - } - - spin_lock(&coex->rxlock); - - /* coex->tbuff will be set to NULL when initializing or - * there is a complete frame or there is start of a frame */ - tbuff = coex->tbuff; - - while (count) { - int len; - - /* Start of a frame */ - if (!tbuff) { - tbuff = coex->back_buff; - coex->tbuff = NULL; - coex->elen = 0; - - coex->pkt_type = HCI_EVENT_PKT; - coex->expect = HCI_EVENT_HDR_SIZE; - } - - len = min_t(uint, coex->expect, count); - memcpy(tbuff, buffer, len); - tbuff += len; - coex->elen += len; - - count -= len; - buffer += len; - coex->expect -= len; - - if (coex->elen == HCI_EVENT_HDR_SIZE) { - /* Complete event header */ - coex->expect = - ((struct hci_event_hdr *)coex->back_buff)->plen; - if (coex->expect > HCI_MAX_EVENT_SIZE - coex->elen) { - tbuff = NULL; - coex->elen = 0; - RTKBT_ERR("tbuff room is not enough"); - break; - } - } - - if (coex->expect == 0) { - /* Complete frame */ - elen = coex->elen; - spin_unlock(&coex->rxlock); - rtk_btcoex_evt_enqueue(coex->back_buff, elen); - spin_lock(&coex->rxlock); - - tbuff = NULL; - coex->elen = 0; - } - } - - /* coex->tbuff would be non-NULL if there isn't a complete frame - * And it will be updated next time */ - coex->tbuff = tbuff; - spin_unlock(&coex->rxlock); -} - - -void rtk_btcoex_parse_l2cap_data_tx(uint8_t *buffer, int count) -{ - if (!test_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { - RTKBT_INFO("%s: Coex is closed, ignore", __func__); - return; - } - - rtl_l2_data_process(buffer, count, 1); - //u16 handle, total_len, pdu_len, channel_ID, command_len, psm, scid, - // dcid, result, status; - //u8 flag, code, identifier; - //u8 *pp = (u8 *) (skb->data); - //STREAM_TO_UINT16(handle, pp); - //flag = handle >> 12; - //handle = handle & 0x0FFF; - //STREAM_TO_UINT16(total_len, pp); - //STREAM_TO_UINT16(pdu_len, pp); - //STREAM_TO_UINT16(channel_ID, pp); - - //if (channel_ID == 0x0001) { - // code = *pp++; - // switch (code) { - // case L2CAP_CONN_REQ: - // identifier = *pp++; - // STREAM_TO_UINT16(command_len, pp); - // STREAM_TO_UINT16(psm, pp); - // STREAM_TO_UINT16(scid, pp); - // RTKBT_DBG("TX l2cap conn req, hndl %x, PSM %x, scid=%x", - // handle, psm, scid); - // handle_l2cap_con_req(handle, psm, scid, 1); - // break; - - // case L2CAP_CONN_RSP: - // identifier = *pp++; - // STREAM_TO_UINT16(command_len, pp); - // STREAM_TO_UINT16(dcid, pp); - // STREAM_TO_UINT16(scid, pp); - // STREAM_TO_UINT16(result, pp); - // STREAM_TO_UINT16(status, pp); - // RTKBT_DBG("TX l2cap conn rsp, hndl %x, dcid %x, " - // "scid %x, result %x", - // handle, dcid, scid, result); - // handle_l2cap_con_rsp(handle, dcid, scid, 1, result); - // break; - - // case L2CAP_DISCONN_REQ: - // identifier = *pp++; - // STREAM_TO_UINT16(command_len, pp); - // STREAM_TO_UINT16(dcid, pp); - // STREAM_TO_UINT16(scid, pp); - // RTKBT_DBG("TX l2cap disconn req, hndl %x, dcid %x, " - // "scid %x", handle, dcid, scid); - // handle_l2cap_discon_req(handle, dcid, scid, 1); - // break; - - // case L2CAP_DISCONN_RSP: - // break; - - // default: - // 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, 1, pp); - //} -} - -void rtk_btcoex_parse_l2cap_data_rx(uint8_t *buffer, int count) -{ - if (!test_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { - RTKBT_INFO("%s: Coex is closed, ignore", __func__); - return; - } - - rtl_l2_data_process(buffer, count, 0); - //u16 handle, total_len, pdu_len, channel_ID, command_len, psm, scid, - // dcid, result, status; - //u8 flag, code, identifier; - //u8 *pp = urb->transfer_buffer; - //STREAM_TO_UINT16(handle, pp); - //flag = handle >> 12; - //handle = handle & 0x0FFF; - //STREAM_TO_UINT16(total_len, pp); - //STREAM_TO_UINT16(pdu_len, pp); - //STREAM_TO_UINT16(channel_ID, pp); - - //if (channel_ID == 0x0001) { - // code = *pp++; - // switch (code) { - // case L2CAP_CONN_REQ: - // identifier = *pp++; - // STREAM_TO_UINT16(command_len, pp); - // STREAM_TO_UINT16(psm, pp); - // STREAM_TO_UINT16(scid, pp); - // RTKBT_DBG("RX l2cap conn req, hndl %x, PSM %x, scid %x", - // handle, psm, scid); - // handle_l2cap_con_req(handle, psm, scid, 0); - // break; - - // case L2CAP_CONN_RSP: - // identifier = *pp++; - // STREAM_TO_UINT16(command_len, pp); - // STREAM_TO_UINT16(dcid, pp); - // STREAM_TO_UINT16(scid, pp); - // STREAM_TO_UINT16(result, pp); - // STREAM_TO_UINT16(status, pp); - // RTKBT_DBG("RX l2cap conn rsp, hndl %x, dcid %x, " - // "scid %x, result %x", - // handle, dcid, scid, result); - // handle_l2cap_con_rsp(handle, dcid, scid, 0, result); - // break; - - // case L2CAP_DISCONN_REQ: - // identifier = *pp++; - // STREAM_TO_UINT16(command_len, pp); - // STREAM_TO_UINT16(dcid, pp); - // STREAM_TO_UINT16(scid, pp); - // RTKBT_DBG("RX l2cap disconn req, hndl %x, dcid %x, " - // "scid %x", handle, dcid, scid); - // handle_l2cap_discon_req(handle, dcid, scid, 0); - // break; - - // case L2CAP_DISCONN_RSP: - // break; - - // default: - // 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, 0, pp); - //} -} - -#ifdef RTB_SOFTWARE_MAILBOX - -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) -static void polling_bt_info(struct timer_list *unused) -#else -static void polling_bt_info(unsigned long data) -#endif -{ - uint8_t temp_cmd[1]; - RTKBT_DBG("polling timer"); - if (btrtl_coex.polling_enable) { - //temp_cmd[0] = HCI_VENDOR_SUB_CMD_BT_REPORT_CONN_SCO_INQ_INFO; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_STATUS_INFO; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 1, temp_cmd); - } - mod_timer(&btrtl_coex.polling_timer, - jiffies + msecs_to_jiffies(1000 * btrtl_coex.polling_interval)); -} - -static void rtk_handle_bt_info_control(uint8_t *p) -{ - uint8_t temp_cmd[20]; - struct rtl_btinfo_ctl *ctl = (struct rtl_btinfo_ctl*)p; - RTKBT_DBG("Received polling_enable %u, polling_time %u, " - "autoreport_enable %u", ctl->polling_enable, - ctl->polling_time, ctl->autoreport_enable); - RTKBT_DBG("coex: original polling_enable %u", - btrtl_coex.polling_enable); - - if (ctl->polling_enable && !btrtl_coex.polling_enable) { - /* setup polling timer for getting bt info from firmware */ - btrtl_coex.polling_timer.expires = - jiffies + msecs_to_jiffies(ctl->polling_time * 1000); - mod_timer(&btrtl_coex.polling_timer, - btrtl_coex.polling_timer.expires); - } - - /* Close bt info polling timer */ - if (!ctl->polling_enable && btrtl_coex.polling_enable) - del_timer(&btrtl_coex.polling_timer); - - if (btrtl_coex.autoreport != ctl->autoreport_enable) { - temp_cmd[0] = HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_ENABLE; - temp_cmd[1] = 1; - temp_cmd[2] = ctl->autoreport_enable; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 3, temp_cmd); - } - - btrtl_coex.polling_enable = ctl->polling_enable; - btrtl_coex.polling_interval = ctl->polling_time; - btrtl_coex.autoreport = ctl->autoreport_enable; - - rtk_notify_info_to_wifi(HOST_RESPONSE, 0, NULL); -} - -static void rtk_handle_bt_coex_control(uint8_t * p) -{ - uint8_t temp_cmd[20]; - uint8_t opcode, opcode_len, value, power_decrease, psd_mode, - access_type; - - opcode = *p++; - RTKBT_DBG("receive bt coex control event from wifi, op 0x%02x", opcode); - - switch (opcode) { - case BT_PATCH_VERSION_QUERY: - rtk_notify_btpatch_version_to_wifi(); - break; - - case IGNORE_WLAN_ACTIVE_CONTROL: - opcode_len = *p++; - value = *p++; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_BT_ENABLE_IGNORE_WLAN_ACT_CMD; - temp_cmd[1] = 1; - temp_cmd[2] = value; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 3, temp_cmd); - break; - - case LNA_CONSTRAIN_CONTROL: - opcode_len = *p++; - value = *p++; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_SET_BT_LNA_CONSTRAINT; - temp_cmd[1] = 1; - temp_cmd[2] = value; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 3, temp_cmd); - break; - - case BT_POWER_DECREASE_CONTROL: - opcode_len = *p++; - power_decrease = *p++; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_WIFI_FORCE_TX_POWER_CMD; - temp_cmd[1] = 1; - temp_cmd[2] = power_decrease; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 3, temp_cmd); - break; - - case BT_PSD_MODE_CONTROL: - opcode_len = *p++; - psd_mode = *p++; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_SET_BT_PSD_MODE; - temp_cmd[1] = 1; - temp_cmd[2] = psd_mode; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 3, temp_cmd); - break; - - case WIFI_BW_CHNL_NOTIFY: - opcode_len = *p++; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_WIFI_CHANNEL_AND_BANDWIDTH_CMD; - temp_cmd[1] = 3; - memcpy(temp_cmd + 2, p, 3); //wifi_state, wifi_centralchannel, chnnels_btnotuse - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 5, temp_cmd); - break; - - case QUERY_BT_AFH_MAP: - opcode_len = *p++; - btrtl_coex.piconet_id = *p++; - btrtl_coex.mode = *p++; - temp_cmd[0] = HCI_VENDOR_SUB_CMD_GET_AFH_MAP_L; - temp_cmd[1] = 2; - temp_cmd[2] = btrtl_coex.piconet_id; - temp_cmd[3] = btrtl_coex.mode; - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 4, temp_cmd); - break; - - case BT_REGISTER_ACCESS: - opcode_len = *p++; - access_type = *p++; - if (access_type == 0) { //read - temp_cmd[0] = HCI_VENDOR_SUB_CMD_RD_REG_REQ; - temp_cmd[1] = 5; - temp_cmd[2] = *p++; - memcpy(temp_cmd + 3, p, 4); - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 7, - temp_cmd); - } else { //write - temp_cmd[0] = HCI_VENDOR_SUB_CMD_RD_REG_REQ; - temp_cmd[1] = 5; - temp_cmd[2] = *p++; - memcpy(temp_cmd + 3, p, 8); - rtk_vendor_cmd_to_fw(HCI_VENDOR_MAILBOX_CMD, 11, - temp_cmd); - } - break; - - default: - break; - } -} - -static void rtk_handle_event_from_wifi(uint8_t * msg) -{ - uint8_t *p = msg; - uint8_t event_code = *p++; - uint8_t total_length; - uint8_t extension_event; - uint8_t operation; - uint16_t wifi_opcode; - uint8_t op_status; - - if (memcmp(msg, invite_rsp, sizeof(invite_rsp)) == 0) { - RTKBT_DBG("receive invite rsp from wifi, wifi is already on"); - btrtl_coex.wifi_on = 1; - rtk_notify_extension_version_to_wifi(); - } - - if (memcmp(msg, attend_req, sizeof(attend_req)) == 0) { - RTKBT_DBG("receive attend req from wifi, wifi turn on"); - btrtl_coex.wifi_on = 1; - rtkbt_coexmsg_send(attend_ack, sizeof(attend_ack)); - rtk_notify_extension_version_to_wifi(); - } - - if (memcmp(msg, wifi_leave, sizeof(wifi_leave)) == 0) { - RTKBT_DBG("receive wifi leave from wifi, wifi turn off"); - btrtl_coex.wifi_on = 0; - rtkbt_coexmsg_send(leave_ack, sizeof(leave_ack)); - if (btrtl_coex.polling_enable) { - btrtl_coex.polling_enable = 0; - del_timer(&btrtl_coex.polling_timer); - } - } - - if (memcmp(msg, leave_ack, sizeof(leave_ack)) == 0) { - RTKBT_DBG("receive leave ack from wifi"); - } - - if (event_code == 0xFE) { - total_length = *p++; - extension_event = *p++; - switch (extension_event) { - case RTK_HS_EXTENSION_EVENT_WIFI_SCAN: - operation = *p; - RTKBT_DBG("Recv WiFi scan notify event from WiFi, " - "op 0x%02x", operation); - break; - - case RTK_HS_EXTENSION_EVENT_HCI_BT_INFO_CONTROL: - rtk_handle_bt_info_control(p); - break; - - case RTK_HS_EXTENSION_EVENT_HCI_BT_COEX_CONTROL: - rtk_handle_bt_coex_control(p); - break; - - default: - break; - } - } - - if (event_code == 0x0E) { - p += 2; //length, number of complete packets - STREAM_TO_UINT16(wifi_opcode, p); - op_status = *p; - RTKBT_DBG("Recv cmd complete event from WiFi, op 0x%02x, " - "status 0x%02x", wifi_opcode, op_status); - } -} -#endif /* RTB_SOFTWARE_MAILBOX */ - -static inline void rtl_free_frags(struct rtl_coex_struct *coex) -{ - unsigned long flags; - - spin_lock_irqsave(&coex->rxlock, flags); - - coex->elen = 0; - coex->tbuff = NULL; - - spin_unlock_irqrestore(&coex->rxlock, flags); -} - -void rtk_btcoex_open(struct hci_dev *hdev) -{ - if (test_and_set_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) { - RTKBT_WARN("RTL COEX is already running."); - return; - } - - RTKBT_INFO("Open BTCOEX"); - - /* Just for test */ - //struct rtl_btinfo_ctl ctl; - - INIT_DELAYED_WORK(&btrtl_coex.fw_work, (void *)rtl_ev_work); -#ifdef RTB_SOFTWARE_MAILBOX -#ifdef RTK_COEX_OVER_SYMBOL - INIT_WORK(&rtw_work, rtw_work_func); - skb_queue_head_init(&rtw_q); - rtw_coex_on = 1; -#else - INIT_DELAYED_WORK(&btrtl_coex.sock_work, - (void *)udpsocket_recv_data); -#endif -#endif /* RTB_SOFTWARE_MAILBOX */ - INIT_DELAYED_WORK(&btrtl_coex.l2_work, (void *)rtl_l2_work); - -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) -#ifdef RTB_SOFTWARE_MAILBOX - timer_setup(&btrtl_coex.polling_timer, polling_bt_info, 0); -#endif - timer_setup(&btrtl_coex.a2dp_count_timer, count_a2dp_packet_timeout, 0); - timer_setup(&btrtl_coex.pan_count_timer, count_pan_packet_timeout, 0); - timer_setup(&btrtl_coex.hogp_count_timer, count_hogp_packet_timeout, 0); -#else -#ifdef RTB_SOFTWARE_MAILBOX - setup_timer(&btrtl_coex.polling_timer, polling_bt_info, 0); -#endif - setup_timer(&btrtl_coex.a2dp_count_timer, count_a2dp_packet_timeout, 0); - setup_timer(&btrtl_coex.pan_count_timer, count_pan_packet_timeout, 0); - setup_timer(&btrtl_coex.hogp_count_timer, count_hogp_packet_timeout, 0); -#endif - - btrtl_coex.hdev = hdev; -#ifdef RTB_SOFTWARE_MAILBOX - btrtl_coex.wifi_on = 0; -#endif - - init_profile_hash(&btrtl_coex); - init_connection_hash(&btrtl_coex); - - btrtl_coex.pkt_type = 0; - btrtl_coex.expect = 0; - btrtl_coex.elen = 0; - btrtl_coex.tbuff = NULL; - -#ifdef RTB_SOFTWARE_MAILBOX -#ifndef RTK_COEX_OVER_SYMBOL - create_udpsocket(); -#endif - rtkbt_coexmsg_send(invite_req, sizeof(invite_req)); -#endif - - /* Just for test */ - //ctl.polling_enable = 1; - //ctl.polling_time = 1; - //ctl.autoreport_enable = 1; - //rtk_handle_bt_info_control((u8 *)&ctl); -} - -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."); - return; - } - - RTKBT_INFO("Close BTCOEX"); - -#ifdef RTB_SOFTWARE_MAILBOX - /* Close coex socket */ - if (btrtl_coex.wifi_on) - rtkbt_coexmsg_send(bt_leave, sizeof(bt_leave)); -#ifdef RTK_COEX_OVER_SYMBOL - rtw_coex_on = 0; - skb_queue_purge(&rtw_q); - cancel_work_sync(&rtw_work); -#else - cancel_delayed_work_sync(&btrtl_coex.sock_work); - if (btrtl_coex.sock_open) { - btrtl_coex.sock_open = 0; - RTKBT_DBG("release udp socket"); - sock_release(btrtl_coex.udpsock); - } -#endif - - /* Delete all timers */ - if (btrtl_coex.polling_enable) { - btrtl_coex.polling_enable = 0; - del_timer_sync(&(btrtl_coex.polling_timer)); - } -#endif /* RTB_SOFTWARE_MAILBOX */ - - del_timer_sync(&btrtl_coex.a2dp_count_timer); - del_timer_sync(&btrtl_coex.pan_count_timer); - del_timer_sync(&btrtl_coex.hogp_count_timer); - - cancel_delayed_work_sync(&btrtl_coex.fw_work); - cancel_delayed_work_sync(&btrtl_coex.l2_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); - RTKBT_DBG("-x"); -} - -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) -{ - RTKBT_DBG("%s: version: %s", __func__, RTK_VERSION); - RTKBT_DBG("create workqueue"); -#ifdef RTB_SOFTWARE_MAILBOX -#ifdef RTK_COEX_OVER_SYMBOL - RTKBT_INFO("Coex over Symbol"); - rtw_wq = create_workqueue("btcoexwork"); - skb_queue_head_init(&rtw_q); -#else - RTKBT_INFO("Coex over UDP"); - btrtl_coex.sock_wq = create_workqueue("btudpwork"); -#endif -#endif /* RTB_SOFTWARE_MAILBOX */ - btrtl_coex.fw_wq = create_workqueue("btfwwork"); - rtl_alloc_buff(&btrtl_coex); - spin_lock_init(&btrtl_coex.rxlock); -} - -void rtk_btcoex_exit(void) -{ - RTKBT_DBG("%s: destroy workqueue", __func__); -#ifdef RTB_SOFTWARE_MAILBOX -#ifdef RTK_COEX_OVER_SYMBOL - flush_workqueue(rtw_wq); - destroy_workqueue(rtw_wq); -#else - flush_workqueue(btrtl_coex.sock_wq); - destroy_workqueue(btrtl_coex.sock_wq); -#endif -#endif - flush_workqueue(btrtl_coex.fw_wq); - destroy_workqueue(btrtl_coex.fw_wq); - rtl_free_buff(&btrtl_coex); -} diff --git a/drivers/bluetooth/rtk_coex.h b/drivers/bluetooth/rtk_coex.h deleted file mode 100644 index 31c97aea6..000000000 --- a/drivers/bluetooth/rtk_coex.h +++ /dev/null @@ -1,373 +0,0 @@ -/* -* -* 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 - -/*********************************** -** Realtek - For coexistence ** -***********************************/ -#define BTRTL_HCIUSB 0 -#define BTRTL_HCIUART 1 - -#define BTRTL_HCI_IF BTRTL_HCIUART - -#define TRUE 1 -#define FALSE 0 - -#define CONNECT_PORT 30001 -#define CONNECT_PORT_WIFI 30000 - -#define invite_req "INVITE_REQ" -#define invite_rsp "INVITE_RSP" -#define attend_req "ATTEND_REQ" -#define attend_ack "ATTEND_ACK" -#define wifi_leave "WIFI_LEAVE" -#define leave_ack "LEAVE_ACK" -#define bt_leave "BT_LEAVE" - -#define HCI_OP_PERIODIC_INQ 0x0403 -#define HCI_EV_LE_META 0x3e -#define HCI_EV_LE_CONN_COMPLETE 0x01 -#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 -#define HCI_EV_LE_ENHANCED_CONN_COMPLETE 0x0a - -//vendor cmd to fw -#define HCI_VENDOR_ENABLE_PROFILE_REPORT_COMMAND 0xfc18 -#define HCI_VENDOR_SET_PROFILE_REPORT_COMMAND 0xfc19 -#define HCI_VENDOR_MAILBOX_CMD 0xfc8f -#define HCI_VENDOR_SET_BITPOOL 0xfc51 - -//subcmd to fw -#define HCI_VENDOR_SUB_CMD_WIFI_CHANNEL_AND_BANDWIDTH_CMD 0x11 -#define HCI_VENDOR_SUB_CMD_WIFI_FORCE_TX_POWER_CMD 0x17 -#define HCI_VENDOR_SUB_CMD_BT_ENABLE_IGNORE_WLAN_ACT_CMD 0x1B -#define HCI_VENDOR_SUB_CMD_BT_REPORT_CONN_SCO_INQ_INFO 0x23 -#define HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_STATUS_INFO 0x27 -#define HCI_VENDOR_SUB_CMD_BT_AUTO_REPORT_ENABLE 0x28 -#define HCI_VENDOR_SUB_CMD_BT_SET_TXRETRY_REPORT_PARAM 0x29 -#define HCI_VENDOR_SUB_CMD_BT_SET_PTATABLE 0x2A -#define HCI_VENDOR_SUB_CMD_SET_BT_PSD_MODE 0x31 -#define HCI_VENDOR_SUB_CMD_SET_BT_LNA_CONSTRAINT 0x32 -#define HCI_VENDOR_SUB_CMD_GET_AFH_MAP_L 0x40 -#define HCI_VENDOR_SUB_CMD_GET_AFH_MAP_M 0x41 -#define HCI_VENDOR_SUB_CMD_GET_AFH_MAP_H 0x42 -#define HCI_VENDOR_SUB_CMD_RD_REG_REQ 0x43 -#define HCI_VENDOR_SUB_CMD_WR_REG_REQ 0x44 - -#define HCI_EV_VENDOR_SPECIFIC 0xff - -//sub event from fw start -#define HCI_VENDOR_PTA_REPORT_EVENT 0x24 -#define HCI_VENDOR_PTA_AUTO_REPORT_EVENT 0x25 - -//vendor cmd to wifi driver -#define HCI_GRP_VENDOR_SPECIFIC (0x3f << 10) -#define HCI_OP_HCI_EXTENSION_VERSION_NOTIFY (0x0100 | HCI_GRP_VENDOR_SPECIFIC) -#define HCI_OP_BT_OPERATION_NOTIFY (0x0102 | HCI_GRP_VENDOR_SPECIFIC) -#define HCI_OP_HCI_BT_INFO_NOTIFY (0x0106 | HCI_GRP_VENDOR_SPECIFIC) -#define HCI_OP_HCI_BT_COEX_NOTIFY (0x0107 | HCI_GRP_VENDOR_SPECIFIC) -#define HCI_OP_HCI_BT_PATCH_VER_NOTIFY (0x0108 | HCI_GRP_VENDOR_SPECIFIC) -#define HCI_OP_HCI_BT_AFH_MAP_NOTIFY (0x0109 | HCI_GRP_VENDOR_SPECIFIC) -#define HCI_OP_HCI_BT_REGISTER_VALUE_NOTIFY (0x010a | HCI_GRP_VENDOR_SPECIFIC) - -//bt info reason to wifi -#define HOST_RESPONSE 0 //Host response when receive the BT Info Control Event -#define POLLING_RESPONSE 1 //The BT Info response for polling by BT firmware. -#define AUTO_REPORT 2 //BT auto report by BT firmware. -#define STACK_REPORT_WHILE_DEVICE_D2 3 //Stack report when BT firmware is under power save state(ex:D2) - -// vendor event from wifi -#define RTK_HS_EXTENSION_EVENT_WIFI_SCAN 0x01 -#define RTK_HS_EXTENSION_EVENT_RADIO_STATUS_NOTIFY 0x02 -#define RTK_HS_EXTENSION_EVENT_HCI_BT_INFO_CONTROL 0x03 -#define RTK_HS_EXTENSION_EVENT_HCI_BT_COEX_CONTROL 0x04 - -//op code from wifi -#define BT_PATCH_VERSION_QUERY 0x00 -#define IGNORE_WLAN_ACTIVE_CONTROL 0x01 -#define LNA_CONSTRAIN_CONTROL 0x02 -#define BT_POWER_DECREASE_CONTROL 0x03 -#define BT_PSD_MODE_CONTROL 0x04 -#define WIFI_BW_CHNL_NOTIFY 0x05 -#define QUERY_BT_AFH_MAP 0x06 -#define BT_REGISTER_ACCESS 0x07 - -//bt operation to notify -#define BT_OPCODE_NONE 0 -#define BT_OPCODE_INQUIRY_START 1 -#define BT_OPCODE_INQUIRY_END 2 -#define BT_OPCODE_PAGE_START 3 -#define BT_OPCODE_PAGE_SUCCESS_END 4 -#define BT_OPCODE_PAGE_UNSUCCESS_END 5 -#define BT_OPCODE_PAIR_START 6 -#define BT_OPCODE_PAIR_END 7 -#define BT_OPCODE_ENABLE_BT 8 -#define BT_OPCODE_DISABLE_BT 9 - -#define HCI_EXTENSION_VERSION 0x0004 -#define HCI_CMD_PREAMBLE_SIZE 3 -#define PAN_PACKET_COUNT 5 - -#define STREAM_TO_UINT16(u16, p) {u16 = ((uint16_t)(*(p)) + (((uint16_t)(*((p) + 1))) << 8)); (p) += 2;} -#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);} - -#define PSM_SDP 0x0001 -#define PSM_RFCOMM 0x0003 -#define PSM_PAN 0x000F -#define PSM_HID 0x0011 -#define PSM_HID_INT 0x0013 -#define PSM_AVCTP 0x0017 -#define PSM_AVDTP 0x0019 -#define PSM_FTP 0x1001 -#define PSM_BIP 0x1003 -#define PSM_OPP 0x1015 -//--add more if needed--// - -enum { - profile_sco = 0, - profile_hid = 1, - profile_a2dp = 2, - profile_pan = 3, - profile_hid_interval = 4, - profile_hogp = 5, - profile_voice = 6, - profile_sink = 7, - profile_max = 8 -}; - -#define A2DP_SIGNAL 0x01 -#define A2DP_MEDIA 0x02 -//profile info data -typedef struct { - struct list_head list; - uint16_t handle; - uint16_t psm; - uint16_t dcid; - uint16_t scid; - uint8_t profile_index; - uint8_t flags; -} rtk_prof_info, *prtk_prof_info; - -//profile info for each connection -typedef struct rtl_hci_conn { - struct list_head list; - uint16_t handle; - uint8_t type; // 0:l2cap, 1:sco/esco, 2:le - uint8_t profile_bitmap; - int8_t profile_refcount[8]; -} rtk_conn_prof, *prtk_conn_prof; - -#ifdef RTB_SOFTWARE_MAILBOX - -struct rtl_btinfo { - u8 cmd; - u8 len; - u8 data[6]; -}; -#define RTL_BTINFO_LEN (sizeof(struct rtl_btinfo)) -/* typedef struct { - * uint8_t cmd_index; - * uint8_t cmd_length; - * uint8_t link_status; - * uint8_t retry_cnt; - * uint8_t rssi; - * uint8_t mailbox_info; - * uint16_t acl_throughput; - * } hci_linkstatus_report; */ - -typedef struct { - uint8_t type; - uint32_t offset; - uint32_t value; -} hci_mailbox_register; - -struct rtl_btinfo_ctl { - uint8_t polling_enable; - uint8_t polling_time; - uint8_t autoreport_enable; -}; -#endif /* RTB_SOFTWARE_MAILBOX */ - -#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; -}; - -#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; -}; - -struct rtl_coex_struct { - struct list_head conn_hash; //hash for connections - struct list_head profile_list; //hash for profile info - struct hci_dev *hdev; -#ifdef RTB_SOFTWARE_MAILBOX - struct socket *udpsock; - struct sockaddr_in addr; - struct sockaddr_in wifi_addr; - struct timer_list polling_timer; -#endif - struct timer_list a2dp_count_timer; - struct timer_list pan_count_timer; - struct timer_list hogp_count_timer; -#ifdef RTB_SOFTWARE_MAILBOX - struct workqueue_struct *sock_wq; - struct delayed_work sock_work; -#endif - struct workqueue_struct *fw_wq; - struct delayed_work fw_work; - struct delayed_work l2_work; -#ifdef RTB_SOFTWARE_MAILBOX - struct sock *sk; -#endif - struct urb *urb; - spinlock_t spin_lock_sock; - spinlock_t spin_lock_profile; - uint32_t a2dp_packet_count; - uint32_t pan_packet_count; - uint32_t hogp_packet_count; - uint32_t voice_packet_count; - uint8_t profile_bitmap; - uint8_t profile_status; - int8_t profile_refcount[8]; - uint8_t ispairing; - uint8_t isinquirying; - uint8_t ispaging; -#ifdef RTB_SOFTWARE_MAILBOX - uint8_t wifi_state; - uint8_t autoreport; - uint8_t polling_enable; - uint8_t polling_interval; - uint8_t piconet_id; - uint8_t mode; - uint8_t afh_map[10]; -#endif - uint16_t hci_reversion; - uint16_t lmp_subversion; -#ifdef RTB_SOFTWARE_MAILBOX - 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; - - spinlock_t rxlock; - __u8 pkt_type; - __u16 expect; - __u8 *tbuff; - __u16 elen; - __u8 back_buff[HCI_MAX_EVENT_SIZE]; - - /* l2cap rx buff */ - struct list_head l2_used_list; - struct list_head l2_free_list; - - /* buff addr and size */ - spinlock_t buff_lock; - unsigned long pages_addr; - unsigned long buff_size; - -#define RTL_COEX_RUNNING (1 << 0) - unsigned long flags; - -}; - -#ifdef __LITTLE_ENDIAN -struct sbc_frame_hdr { - uint8_t syncword:8; /* Sync word */ - uint8_t subbands:1; /* Subbands */ - uint8_t allocation_method:1; /* Allocation method */ - uint8_t channel_mode:2; /* Channel mode */ - uint8_t blocks:2; /* Blocks */ - uint8_t sampling_frequency:2; /* Sampling frequency */ - uint8_t bitpool:8; /* Bitpool */ - uint8_t crc_check:8; /* CRC check */ -} __attribute__ ((packed)); - -/* NOTE: The code is copied from pa. - * only the bit field in 8-bit is affected by endian, not the 16-bit or 32-bit. - * why? - */ -struct rtp_header { - unsigned cc:4; - unsigned x:1; - unsigned p:1; - unsigned v:2; - - unsigned pt:7; - unsigned m:1; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -#else -/* big endian */ -struct sbc_frame_hdr { - uint8_t syncword:8; /* Sync word */ - uint8_t sampling_frequency:2; /* Sampling frequency */ - uint8_t blocks:2; /* Blocks */ - uint8_t channel_mode:2; /* Channel mode */ - uint8_t allocation_method:1; /* Allocation method */ - uint8_t subbands:1; /* Subbands */ - uint8_t bitpool:8; /* Bitpool */ - uint8_t crc_check:8; /* CRC check */ -} __attribute__ ((packed)); - -struct rtp_header { - unsigned v:2; - unsigned p:1; - unsigned x:1; - unsigned cc:4; - - unsigned m:1; - unsigned pt:7; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); -#endif /* __LITTLE_ENDIAN */ - -void rtk_btcoex_parse_event(uint8_t *buffer, int count); -void rtk_btcoex_parse_cmd(uint8_t *buffer, int count); -void rtk_btcoex_parse_l2cap_data_tx(uint8_t *buffer, int count); -void rtk_btcoex_parse_l2cap_data_rx(uint8_t *buffer, int count); - -void rtk_btcoex_open(struct hci_dev *hdev); -void rtk_btcoex_close(void); -void rtk_btcoex_probe(struct hci_dev *hdev); -void rtk_btcoex_init(void); -void rtk_btcoex_exit(void);