From 756e161993824961fad4ba62c40045d9ab65bdb8 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 8 Mar 2019 09:15:43 +0800 Subject: [PATCH 01/28] mmc: add SDIO identifiers for MediaTek Bluetooth devices The SDIO identifier for MediaTek Bluetooth devices were defined in the MediaTek Bluetooth driver. Moving the definitions in MMC header file seems common sense. Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- include/linux/mmc/sdio_ids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 4332199c71c2..d1a5d5df02f5 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -59,6 +59,8 @@ #define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128 #define SDIO_DEVICE_ID_MARVELL_8887WLAN 0x9134 +#define SDIO_VENDOR_ID_MEDIATEK 0x037a + #define SDIO_VENDOR_ID_SIANO 0x039a #define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201 #define SDIO_DEVICE_ID_SIANO_NICE 0x0202 From 9aebfd4a2200ab8075e44379c758bccefdc589bb Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Fri, 8 Mar 2019 09:15:44 +0800 Subject: [PATCH 02/28] Bluetooth: mediatek: add support for MediaTek MT7663S and MT7668S SDIO devices This adds the support of enabling MT7663S and MT7668S SDIO-based Bluetooth function. There are quite many differences between MT766[3,8]S and standard Bluetooth SDIO devices such as Type-A and Type-B devices. For example, MT766[3,8]S have its own SDIO registers layout, definition, SDIO packet format, and the specific flow should be programmed on them to complete the device initialization and low power control and so on. Currently, there are many independent programming sequences from the transport which are exactly the same as the ones in btusb.c about MediaTek support [1] and btmtkuart.c. We can try to split the transport independent Bluetooth setups on the advance, place them into the common files and allow varous transport drivers to reuse them in the future. [1] http://lists.infradead.org/pipermail/linux-mediatek/2019-January/017074.html Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 11 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/btmtksdio.c | 979 ++++++++++++++++++++++++++++++++++ 3 files changed, 991 insertions(+) create mode 100644 drivers/bluetooth/btmtksdio.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 7b2e76e7f22f..b0f9a20401d6 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -379,6 +379,17 @@ config BT_WILINK Say Y here to compile support for Texas Instrument's WiLink7 driver into the kernel or say M to compile it as module (btwilink). +config BT_MTKSDIO + tristate "MediaTek HCI SDIO driver" + depends on MMC + help + MediaTek Bluetooth HCI SDIO driver. + This driver is required if you want to use MediaTek Bluetooth + with SDIO interface. + + Say Y here to compile support for MediaTek Bluetooth SDIO devices + into the kernel or say M to compile it as module (btmtksdio). + config BT_MTKUART tristate "MediaTek HCI UART driver" depends on SERIAL_DEV_BUS diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index b7e393cfc1e3..34887b9b3a85 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_BT_ATH3K) += ath3k.o obj-$(CONFIG_BT_MRVL) += btmrvl.o obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o obj-$(CONFIG_BT_WILINK) += btwilink.o +obj-$(CONFIG_BT_MTKSDIO) += btmtksdio.o obj-$(CONFIG_BT_MTKUART) += btmtkuart.o obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o obj-$(CONFIG_BT_BCM) += btbcm.o diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c new file mode 100644 index 000000000000..b4b8320f279e --- /dev/null +++ b/drivers/bluetooth/btmtksdio.c @@ -0,0 +1,979 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 MediaTek Inc. + +/* + * Bluetooth support for MediaTek SDIO devices + * + * This file is written based on btsdio.c and btmtkuart.c. + * + * Author: Sean Wang + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "h4_recv.h" + +#define VERSION "0.1" + +#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin" +#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin" + +struct btmtksdio_data { + const char *fwname; +}; + +static const struct btmtksdio_data mt7663_data = { + .fwname = FIRMWARE_MT7663, +}; + +static const struct btmtksdio_data mt7668_data = { + .fwname = FIRMWARE_MT7668, +}; + +static const struct sdio_device_id btmtksdio_table[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7663), + .driver_data = (kernel_ulong_t)&mt7663_data }, + {SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668), + .driver_data = (kernel_ulong_t)&mt7668_data }, + { } /* Terminating entry */ +}; + +#define MTK_REG_CHLPCR 0x4 /* W1S */ +#define C_INT_EN_SET BIT(0) +#define C_INT_EN_CLR BIT(1) +#define C_FW_OWN_REQ_SET BIT(8) +#define C_FW_OWN_REQ_CLR BIT(9) + +#define MTK_REG_CSDIOCSR 0x8 +#define SDIO_RE_INIT_EN BIT(0) +#define SDIO_INT_CTL BIT(2) + +#define MTK_REG_CHCR 0xc +#define C_INT_CLR_CTRL BIT(1) + +/* CHISR have the same bits field definition with CHIER */ +#define MTK_REG_CHISR 0x10 +#define MTK_REG_CHIER 0x14 +#define FW_OWN_BACK_INT BIT(0) +#define RX_DONE_INT BIT(1) +#define TX_EMPTY BIT(2) +#define TX_FIFO_OVERFLOW BIT(8) +#define RX_PKT_LEN GENMASK(31, 16) + +#define MTK_REG_CTDR 0x18 + +#define MTK_REG_CRDR 0x1c + +#define MTK_SDIO_BLOCK_SIZE 256 + +#define BTMTKSDIO_TX_WAIT_VND_EVT 1 + +enum { + MTK_WMT_PATCH_DWNLD = 0x1, + MTK_WMT_TEST = 0x2, + MTK_WMT_WAKEUP = 0x3, + MTK_WMT_HIF = 0x4, + MTK_WMT_FUNC_CTRL = 0x6, + MTK_WMT_RST = 0x7, + MTK_WMT_SEMAPHORE = 0x17, +}; + +enum { + BTMTK_WMT_INVALID, + BTMTK_WMT_PATCH_UNDONE, + BTMTK_WMT_PATCH_DONE, + BTMTK_WMT_ON_UNDONE, + BTMTK_WMT_ON_DONE, + BTMTK_WMT_ON_PROGRESS, +}; + +struct mtkbtsdio_hdr { + __le16 len; + __le16 reserved; + u8 bt_type; +} __packed; + +struct mtk_wmt_hdr { + u8 dir; + u8 op; + __le16 dlen; + u8 flag; +} __packed; + +struct mtk_hci_wmt_cmd { + struct mtk_wmt_hdr hdr; + u8 data[256]; +} __packed; + +struct btmtk_hci_wmt_evt { + struct hci_event_hdr hhdr; + struct mtk_wmt_hdr whdr; +} __packed; + +struct btmtk_hci_wmt_evt_funcc { + struct btmtk_hci_wmt_evt hwhdr; + __be16 status; +} __packed; + +struct btmtk_tci_sleep { + u8 mode; + __le16 duration; + __le16 host_duration; + u8 host_wakeup_pin; + u8 time_compensation; +} __packed; + +struct btmtk_hci_wmt_params { + u8 op; + u8 flag; + u16 dlen; + const void *data; + u32 *status; +}; + +struct btmtksdio_dev { + struct hci_dev *hdev; + struct sdio_func *func; + + struct work_struct tx_work; + unsigned long tx_state; + struct sk_buff_head txq; + + struct sk_buff *evt_skb; + + const struct btmtksdio_data *data; +}; + +static int mtk_hci_wmt_sync(struct hci_dev *hdev, + struct btmtk_hci_wmt_params *wmt_params) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc; + u32 hlen, status = BTMTK_WMT_INVALID; + struct btmtk_hci_wmt_evt *wmt_evt; + struct mtk_hci_wmt_cmd wc; + struct mtk_wmt_hdr *hdr; + int err; + + hlen = sizeof(*hdr) + wmt_params->dlen; + if (hlen > 255) + return -EINVAL; + + hdr = (struct mtk_wmt_hdr *)&wc; + hdr->dir = 1; + hdr->op = wmt_params->op; + hdr->dlen = cpu_to_le16(wmt_params->dlen + 1); + hdr->flag = wmt_params->flag; + memcpy(wc.data, wmt_params->data, wmt_params->dlen); + + set_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state); + + err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc); + if (err < 0) { + clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state); + return err; + } + + /* The vendor specific WMT commands are all answered by a vendor + * specific event and will not have the Command Status or Command + * Complete as with usual HCI command flow control. + * + * After sending the command, wait for BTMTKSDIO_TX_WAIT_VND_EVT + * state to be cleared. The driver specific event receive routine + * will clear that state and with that indicate completion of the + * WMT command. + */ + err = wait_on_bit_timeout(&bdev->tx_state, BTMTKSDIO_TX_WAIT_VND_EVT, + TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); + if (err == -EINTR) { + bt_dev_err(hdev, "Execution of wmt command interrupted"); + clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state); + return err; + } + + if (err) { + bt_dev_err(hdev, "Execution of wmt command timed out"); + clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state); + return -ETIMEDOUT; + } + + /* Parse and handle the return WMT event */ + wmt_evt = (struct btmtk_hci_wmt_evt *)bdev->evt_skb->data; + if (wmt_evt->whdr.op != hdr->op) { + bt_dev_err(hdev, "Wrong op received %d expected %d", + wmt_evt->whdr.op, hdr->op); + err = -EIO; + goto err_free_skb; + } + + switch (wmt_evt->whdr.op) { + case MTK_WMT_SEMAPHORE: + if (wmt_evt->whdr.flag == 2) + status = BTMTK_WMT_PATCH_UNDONE; + else + status = BTMTK_WMT_PATCH_DONE; + break; + case MTK_WMT_FUNC_CTRL: + wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt; + if (be16_to_cpu(wmt_evt_funcc->status) == 0x404) + status = BTMTK_WMT_ON_DONE; + else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420) + status = BTMTK_WMT_ON_PROGRESS; + else + status = BTMTK_WMT_ON_UNDONE; + break; + } + + if (wmt_params->status) + *wmt_params->status = status; + +err_free_skb: + kfree_skb(bdev->evt_skb); + bdev->evt_skb = NULL; + + return err; +} + +static int btmtksdio_tx_packet(struct btmtksdio_dev *bdev, + struct sk_buff *skb) +{ + struct mtkbtsdio_hdr *sdio_hdr; + int err; + + /* Make sure that there are enough rooms for SDIO header */ + if (unlikely(skb_headroom(skb) < sizeof(*sdio_hdr))) { + err = pskb_expand_head(skb, sizeof(*sdio_hdr), 0, + GFP_ATOMIC); + if (err < 0) + return err; + } + + /* Prepend MediaTek SDIO Specific Header */ + skb_push(skb, sizeof(*sdio_hdr)); + + sdio_hdr = (void *)skb->data; + sdio_hdr->len = cpu_to_le16(skb->len); + sdio_hdr->reserved = cpu_to_le16(0); + sdio_hdr->bt_type = hci_skb_pkt_type(skb); + + err = sdio_writesb(bdev->func, MTK_REG_CTDR, skb->data, + round_up(skb->len, MTK_SDIO_BLOCK_SIZE)); + if (err < 0) + goto err_skb_pull; + + bdev->hdev->stat.byte_tx += skb->len; + + kfree_skb(skb); + + return 0; + +err_skb_pull: + skb_pull(skb, sizeof(*sdio_hdr)); + + return err; +} + +static u32 btmtksdio_drv_own_query(struct btmtksdio_dev *bdev) +{ + return sdio_readl(bdev->func, MTK_REG_CHLPCR, NULL); +} + +static void btmtksdio_tx_work(struct work_struct *work) +{ + struct btmtksdio_dev *bdev = container_of(work, struct btmtksdio_dev, + tx_work); + struct sk_buff *skb; + int err; + + sdio_claim_host(bdev->func); + + while ((skb = skb_dequeue(&bdev->txq))) { + err = btmtksdio_tx_packet(bdev, skb); + if (err < 0) { + bdev->hdev->stat.err_tx++; + skb_queue_head(&bdev->txq, skb); + break; + } + } + + sdio_release_host(bdev->func); +} + +static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + struct hci_event_hdr *hdr = (void *)skb->data; + int err; + + /* Fix up the vendor event id with 0xff for vendor specific instead + * of 0xe4 so that event send via monitoring socket can be parsed + * properly. + */ + if (hdr->evt == 0xe4) + hdr->evt = HCI_EV_VENDOR; + + /* When someone waits for the WMT event, the skb is being cloned + * and being processed the events from there then. + */ + if (test_bit(BTMTKSDIO_TX_WAIT_VND_EVT, &bdev->tx_state)) { + bdev->evt_skb = skb_clone(skb, GFP_KERNEL); + if (!bdev->evt_skb) { + err = -ENOMEM; + goto err_out; + } + } + + err = hci_recv_frame(hdev, skb); + if (err < 0) + goto err_free_skb; + + if (hdr->evt == HCI_EV_VENDOR) { + if (test_and_clear_bit(BTMTKSDIO_TX_WAIT_VND_EVT, + &bdev->tx_state)) { + /* Barrier to sync with other CPUs */ + smp_mb__after_atomic(); + wake_up_bit(&bdev->tx_state, BTMTKSDIO_TX_WAIT_VND_EVT); + } + } + + return 0; + +err_free_skb: + kfree_skb(bdev->evt_skb); + bdev->evt_skb = NULL; + +err_out: + return err; +} + +static const struct h4_recv_pkt mtk_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = btmtksdio_recv_event }, +}; + +static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size) +{ + const struct h4_recv_pkt *pkts = mtk_recv_pkts; + int pkts_count = ARRAY_SIZE(mtk_recv_pkts); + struct mtkbtsdio_hdr *sdio_hdr; + unsigned char *old_data; + unsigned int old_len; + int err, i, pad_size; + struct sk_buff *skb; + u16 dlen; + + if (rx_size < sizeof(*sdio_hdr)) + return -EILSEQ; + + /* A SDIO packet is exactly containing a Bluetooth packet */ + skb = bt_skb_alloc(rx_size, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_put(skb, rx_size); + + err = sdio_readsb(bdev->func, skb->data, MTK_REG_CRDR, rx_size); + if (err < 0) + goto err_kfree_skb; + + /* Keep old data for dump the content in case of some error is + * caught in the following packet parsing. + */ + old_data = skb->data; + old_len = skb->len; + + bdev->hdev->stat.byte_rx += rx_size; + + sdio_hdr = (void *)skb->data; + + /* We assume the default error as -EILSEQ simply to make the error path + * be cleaner. + */ + err = -EILSEQ; + + if (rx_size != le16_to_cpu(sdio_hdr->len)) { + bt_dev_err(bdev->hdev, "Rx size in sdio header is mismatched "); + goto err_kfree_skb; + } + + hci_skb_pkt_type(skb) = sdio_hdr->bt_type; + + /* Remove MediaTek SDIO header */ + skb_pull(skb, sizeof(*sdio_hdr)); + + /* We have to dig into the packet to get payload size and then know how + * many padding bytes at the tail, these padding bytes should be removed + * before the packet is indicated to the core layer. + */ + for (i = 0; i < pkts_count; i++) { + if (sdio_hdr->bt_type == (&pkts[i])->type) + break; + } + + if (i >= pkts_count) { + bt_dev_err(bdev->hdev, "Invalid bt type 0x%02x", + sdio_hdr->bt_type); + goto err_kfree_skb; + } + + /* Remaining bytes cannot hold a header*/ + if (skb->len < (&pkts[i])->hlen) { + bt_dev_err(bdev->hdev, "The size of bt header is mismatched"); + goto err_kfree_skb; + } + + switch ((&pkts[i])->lsize) { + case 1: + dlen = skb->data[(&pkts[i])->loff]; + break; + case 2: + dlen = get_unaligned_le16(skb->data + + (&pkts[i])->loff); + break; + default: + goto err_kfree_skb; + } + + pad_size = skb->len - (&pkts[i])->hlen - dlen; + + /* Remaining bytes cannot hold a payload */ + if (pad_size < 0) { + bt_dev_err(bdev->hdev, "The size of bt payload is mismatched"); + goto err_kfree_skb; + } + + /* Remove padding bytes */ + skb_trim(skb, skb->len - pad_size); + + /* Complete frame */ + (&pkts[i])->recv(bdev->hdev, skb); + + return 0; + +err_kfree_skb: + print_hex_dump(KERN_ERR, "err sdio rx: ", DUMP_PREFIX_NONE, 4, 1, + old_data, old_len, true); + kfree_skb(skb); + + return err; +} + +static void btmtksdio_interrupt(struct sdio_func *func) +{ + struct btmtksdio_dev *bdev = sdio_get_drvdata(func); + u32 int_status; + u16 rx_size; + + /* Disable interrupt */ + sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0); + + int_status = sdio_readl(func, MTK_REG_CHISR, NULL); + + /* Ack an interrupt as soon as possible before any operation on + * hardware. + * + * Note that we don't ack any status during operations to avoid race + * condition between the host and the device such as it's possible to + * mistakenly ack RX_DONE for the next packet and then cause interrupts + * not be raised again but there is still pending data in the hardware + * FIFO. + */ + sdio_writel(func, int_status, MTK_REG_CHISR, NULL); + + if (unlikely(!int_status)) + bt_dev_err(bdev->hdev, "CHISR is 0\n"); + + if (int_status & FW_OWN_BACK_INT) + bt_dev_dbg(bdev->hdev, "Get fw own back\n"); + + if (int_status & TX_EMPTY) + schedule_work(&bdev->tx_work); + else if (unlikely(int_status & TX_FIFO_OVERFLOW)) + bt_dev_warn(bdev->hdev, "Tx fifo overflow\n"); + + if (int_status & RX_DONE_INT) { + rx_size = (int_status & RX_PKT_LEN) >> 16; + + if (btmtksdio_rx_packet(bdev, rx_size) < 0) + bdev->hdev->stat.err_rx++; + } + + /* Enable interrupt */ + sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, 0); +} + +static int btmtksdio_open(struct hci_dev *hdev) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + int err; + u32 status; + + sdio_claim_host(bdev->func); + + err = sdio_enable_func(bdev->func); + if (err < 0) + goto err_release_host; + + /* Get ownership from the device */ + sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err); + if (err < 0) + goto err_disable_func; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + status & C_FW_OWN_REQ_SET, 2000, 1000000); + if (err < 0) { + bt_dev_err(bdev->hdev, "Cannot get ownership from device"); + goto err_disable_func; + } + + /* Disable interrupt & mask out all interrupt sources */ + sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, &err); + if (err < 0) + goto err_disable_func; + + sdio_writel(bdev->func, 0, MTK_REG_CHIER, &err); + if (err < 0) + goto err_disable_func; + + err = sdio_claim_irq(bdev->func, btmtksdio_interrupt); + if (err < 0) + goto err_disable_func; + + err = sdio_set_block_size(bdev->func, MTK_SDIO_BLOCK_SIZE); + if (err < 0) + goto err_release_irq; + + /* SDIO CMD 5 allows the SDIO device back to idle state an + * synchronous interrupt is supported in SDIO 4-bit mode + */ + sdio_writel(bdev->func, SDIO_INT_CTL | SDIO_RE_INIT_EN, + MTK_REG_CSDIOCSR, &err); + if (err < 0) + goto err_release_irq; + + /* Setup write-1-clear for CHISR register */ + sdio_writel(bdev->func, C_INT_CLR_CTRL, MTK_REG_CHCR, &err); + if (err < 0) + goto err_release_irq; + + /* Setup interrupt sources */ + sdio_writel(bdev->func, RX_DONE_INT | TX_EMPTY | TX_FIFO_OVERFLOW, + MTK_REG_CHIER, &err); + if (err < 0) + goto err_release_irq; + + /* Enable interrupt */ + sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, &err); + if (err < 0) + goto err_release_irq; + + sdio_release_host(bdev->func); + + return 0; + +err_release_irq: + sdio_release_irq(bdev->func); + +err_disable_func: + sdio_disable_func(bdev->func); + +err_release_host: + sdio_release_host(bdev->func); + + return err; +} + +static int btmtksdio_close(struct hci_dev *hdev) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + u32 status; + int err; + + sdio_claim_host(bdev->func); + + /* Disable interrupt */ + sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL); + + sdio_release_irq(bdev->func); + + /* Return ownership to the device */ + sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, NULL); + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + !(status & C_FW_OWN_REQ_SET), 2000, 1000000); + if (err < 0) + bt_dev_err(bdev->hdev, "Cannot return ownership to device"); + + sdio_disable_func(bdev->func); + + sdio_release_host(bdev->func); + + return 0; +} + +static int btmtksdio_flush(struct hci_dev *hdev) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + + skb_queue_purge(&bdev->txq); + + cancel_work_sync(&bdev->tx_work); + + return 0; +} + +static int btmtksdio_func_query(struct hci_dev *hdev) +{ + struct btmtk_hci_wmt_params wmt_params; + int status, err; + u8 param = 0; + + /* Query whether the function is enabled */ + wmt_params.op = MTK_WMT_FUNC_CTRL; + wmt_params.flag = 4; + wmt_params.dlen = sizeof(param); + wmt_params.data = ¶m; + wmt_params.status = &status; + + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to query function status (%d)", err); + return err; + } + + return status; +} + +static int mtk_setup_firmware(struct hci_dev *hdev, const char *fwname) +{ + struct btmtk_hci_wmt_params wmt_params; + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + int err, dlen; + u8 flag; + + err = request_firmware(&fw, fwname, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Failed to load firmware file (%d)", err); + return err; + } + + fw_ptr = fw->data; + fw_size = fw->size; + + /* The size of patch header is 30 bytes, should be skip */ + if (fw_size < 30) { + err = -EINVAL; + goto free_fw; + } + + fw_size -= 30; + fw_ptr += 30; + flag = 1; + + wmt_params.op = MTK_WMT_PATCH_DWNLD; + wmt_params.status = NULL; + + while (fw_size > 0) { + dlen = min_t(int, 250, fw_size); + + /* Tell device the position in sequence */ + if (fw_size - dlen <= 0) + flag = 3; + else if (fw_size < fw->size - 30) + flag = 2; + + wmt_params.flag = flag; + wmt_params.dlen = dlen; + wmt_params.data = fw_ptr; + + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", + err); + goto free_fw; + } + + fw_size -= dlen; + fw_ptr += dlen; + } + + wmt_params.op = MTK_WMT_RST; + wmt_params.flag = 4; + wmt_params.dlen = 0; + wmt_params.data = NULL; + wmt_params.status = NULL; + + /* Activate funciton the firmware providing to */ + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); + goto free_fw; + } + + /* Wait a few moments for firmware activation done */ + usleep_range(10000, 12000); + +free_fw: + release_firmware(fw); + return err; +} + +static int btmtksdio_setup(struct hci_dev *hdev) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + struct btmtk_hci_wmt_params wmt_params; + ktime_t calltime, delta, rettime; + struct btmtk_tci_sleep tci_sleep; + unsigned long long duration; + struct sk_buff *skb; + int err, status; + u8 param = 0x1; + + calltime = ktime_get(); + + /* Query whether the firmware is already download */ + wmt_params.op = MTK_WMT_SEMAPHORE; + wmt_params.flag = 1; + wmt_params.dlen = 0; + wmt_params.data = NULL; + wmt_params.status = &status; + + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to query firmware status (%d)", err); + return err; + } + + if (status == BTMTK_WMT_PATCH_DONE) { + bt_dev_info(hdev, "Firmware already downloaded"); + goto ignore_setup_fw; + } + + /* Setup a firmware which the device definitely requires */ + err = mtk_setup_firmware(hdev, bdev->data->fwname); + if (err < 0) + return err; + +ignore_setup_fw: + /* Query whether the device is already enabled */ + err = readx_poll_timeout(btmtksdio_func_query, hdev, status, + status < 0 || status != BTMTK_WMT_ON_PROGRESS, + 2000, 5000000); + /* -ETIMEDOUT happens */ + if (err < 0) + return err; + + /* The other errors happen in btusb_mtk_func_query */ + if (status < 0) + return status; + + if (status == BTMTK_WMT_ON_DONE) { + bt_dev_info(hdev, "function already on"); + goto ignore_func_on; + } + + /* Enable Bluetooth protocol */ + wmt_params.op = MTK_WMT_FUNC_CTRL; + wmt_params.flag = 0; + wmt_params.dlen = sizeof(param); + wmt_params.data = ¶m; + wmt_params.status = NULL; + + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; + } + +ignore_func_on: + /* Apply the low power environment setup */ + tci_sleep.mode = 0x5; + tci_sleep.duration = cpu_to_le16(0x640); + tci_sleep.host_duration = cpu_to_le16(0x640); + tci_sleep.host_wakeup_pin = 0; + tci_sleep.time_compensation = 0; + + skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "Failed to apply low power setting (%d)", err); + return err; + } + kfree_skb(skb); + + rettime = ktime_get(); + delta = ktime_sub(rettime, calltime); + duration = (unsigned long long)ktime_to_ns(delta) >> 10; + + bt_dev_info(hdev, "Device setup in %llu usecs", duration); + + return 0; +} + +static int btmtksdio_shutdown(struct hci_dev *hdev) +{ + struct btmtk_hci_wmt_params wmt_params; + u8 param = 0x0; + int err; + + /* Disable the device */ + wmt_params.op = MTK_WMT_FUNC_CTRL; + wmt_params.flag = 0; + wmt_params.dlen = sizeof(param); + wmt_params.data = ¶m; + wmt_params.status = NULL; + + err = mtk_hci_wmt_sync(hdev, &wmt_params); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; + } + + return 0; +} + +static int btmtksdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); + + switch (hci_skb_pkt_type(skb)) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + + default: + return -EILSEQ; + } + + skb_queue_tail(&bdev->txq, skb); + + schedule_work(&bdev->tx_work); + + return 0; +} + +static int btmtksdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct btmtksdio_dev *bdev; + struct hci_dev *hdev; + int err; + + bdev = devm_kzalloc(&func->dev, sizeof(*bdev), GFP_KERNEL); + if (!bdev) + return -ENOMEM; + + bdev->data = (void *)id->driver_data; + if (!bdev->data) + return -ENODEV; + + bdev->func = func; + + INIT_WORK(&bdev->tx_work, btmtksdio_tx_work); + skb_queue_head_init(&bdev->txq); + + /* Initialize and register HCI device */ + hdev = hci_alloc_dev(); + if (!hdev) { + dev_err(&func->dev, "Can't allocate HCI device\n"); + return -ENOMEM; + } + + bdev->hdev = hdev; + + hdev->bus = HCI_SDIO; + hci_set_drvdata(hdev, bdev); + + hdev->open = btmtksdio_open; + hdev->close = btmtksdio_close; + hdev->flush = btmtksdio_flush; + hdev->setup = btmtksdio_setup; + hdev->shutdown = btmtksdio_shutdown; + hdev->send = btmtksdio_send_frame; + SET_HCIDEV_DEV(hdev, &func->dev); + + hdev->manufacturer = 70; + set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + + err = hci_register_dev(hdev); + if (err < 0) { + dev_err(&func->dev, "Can't register HCI device\n"); + hci_free_dev(hdev); + return err; + } + + sdio_set_drvdata(func, bdev); + + return 0; +} + +static void btmtksdio_remove(struct sdio_func *func) +{ + struct btmtksdio_dev *bdev = sdio_get_drvdata(func); + struct hci_dev *hdev; + + if (!bdev) + return; + + hdev = bdev->hdev; + + sdio_set_drvdata(func, NULL); + hci_unregister_dev(hdev); + hci_free_dev(hdev); +} + +static struct sdio_driver btmtksdio_driver = { + .name = "btmtksdio", + .probe = btmtksdio_probe, + .remove = btmtksdio_remove, + .id_table = btmtksdio_table, +}; + +static int __init btmtksdio_init(void) +{ + BT_INFO("MediaTek Bluetooth SDIO driver ver %s", VERSION); + + return sdio_register_driver(&btmtksdio_driver); +} + +static void __exit btmtksdio_exit(void) +{ + sdio_unregister_driver(&btmtksdio_driver); +} + +module_init(btmtksdio_init); +module_exit(btmtksdio_exit); + +MODULE_AUTHOR("Sean Wang "); +MODULE_DESCRIPTION("MediaTek Bluetooth SDIO driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_MT7663); +MODULE_FIRMWARE(FIRMWARE_MT7668); From 4fdd5a4f8b4407c21897dbfba9d0ee77eb80a42c Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 11 Mar 2019 11:38:31 -0700 Subject: [PATCH 03/28] Bluetooth: hci_qca: Add helper function to get the chip family Many functions obtain a 'struct qca_serdev' only to read the btsoc_type field. Add a helper function that encapsulates this. This also fixes crashes observed on platforms with ROME controllers that are instantiated through ldisc and not as serdev clients. The crashes are caused by NULL pointer dereferentiations, which stem from the driver's assumption that a QCA HCI device is always associated with a serdev device. Fixes: fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth chip wcn3990") Reported-by: Balakrishna Godavarthi Signed-off-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 45 +++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 237aea34b69f..4ea995d610d2 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -174,6 +174,21 @@ static int qca_power_setup(struct hci_uart *hu, bool on); static void qca_power_shutdown(struct hci_uart *hu); static int qca_power_off(struct hci_dev *hdev); +static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu) +{ + enum qca_btsoc_type soc_type; + + if (hu->serdev) { + struct qca_serdev *qsd = serdev_device_get_drvdata(hu->serdev); + + soc_type = qsd->btsoc_type; + } else { + soc_type = QCA_ROME; + } + + return soc_type; +} + static void __serial_clock_on(struct tty_struct *tty) { /* TODO: Some chipset requires to enable UART clock on client @@ -963,7 +978,6 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) { struct hci_uart *hu = hci_get_drvdata(hdev); struct qca_data *qca = hu->priv; - struct qca_serdev *qcadev; struct sk_buff *skb; u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 }; @@ -985,8 +999,6 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) skb_queue_tail(&qca->txq, skb); hci_uart_tx_wakeup(hu); - qcadev = serdev_device_get_drvdata(hu->serdev); - /* Wait for the baudrate change request to be sent */ while (!skb_queue_empty(&qca->txq)) @@ -996,7 +1008,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS)); /* Give the controller time to process the request */ - if (qcadev->btsoc_type == QCA_WCN3990) + if (qca_soc_type(hu) == QCA_WCN3990) msleep(10); else msleep(300); @@ -1072,10 +1084,7 @@ static unsigned int qca_get_speed(struct hci_uart *hu, static int qca_check_speeds(struct hci_uart *hu) { - struct qca_serdev *qcadev; - - qcadev = serdev_device_get_drvdata(hu->serdev); - if (qcadev->btsoc_type == QCA_WCN3990) { + if (qca_soc_type(hu) == QCA_WCN3990) { if (!qca_get_speed(hu, QCA_INIT_SPEED) && !qca_get_speed(hu, QCA_OPER_SPEED)) return -EINVAL; @@ -1091,7 +1100,6 @@ static int qca_check_speeds(struct hci_uart *hu) static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) { unsigned int speed, qca_baudrate; - struct qca_serdev *qcadev; int ret = 0; if (speed_type == QCA_INIT_SPEED) { @@ -1099,6 +1107,8 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) if (speed) host_set_baudrate(hu, speed); } else { + enum qca_btsoc_type soc_type = qca_soc_type(hu); + speed = qca_get_speed(hu, QCA_OPER_SPEED); if (!speed) return 0; @@ -1106,8 +1116,7 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) /* Disable flow control for wcn3990 to deassert RTS while * changing the baudrate of chip and host. */ - qcadev = serdev_device_get_drvdata(hu->serdev); - if (qcadev->btsoc_type == QCA_WCN3990) + if (soc_type == QCA_WCN3990) hci_uart_set_flow_control(hu, true); qca_baudrate = qca_get_baudrate_value(speed); @@ -1119,7 +1128,7 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) host_set_baudrate(hu, speed); error: - if (qcadev->btsoc_type == QCA_WCN3990) + if (soc_type == QCA_WCN3990) hci_uart_set_flow_control(hu, false); } @@ -1181,12 +1190,10 @@ static int qca_setup(struct hci_uart *hu) struct hci_dev *hdev = hu->hdev; struct qca_data *qca = hu->priv; unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; - struct qca_serdev *qcadev; + enum qca_btsoc_type soc_type = qca_soc_type(hu); int ret; int soc_ver = 0; - qcadev = serdev_device_get_drvdata(hu->serdev); - ret = qca_check_speeds(hu); if (ret) return ret; @@ -1194,7 +1201,7 @@ static int qca_setup(struct hci_uart *hu) /* Patch downloading has to be done without IBS mode */ clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); - if (qcadev->btsoc_type == QCA_WCN3990) { + if (soc_type == QCA_WCN3990) { bt_dev_info(hdev, "setting up wcn3990"); /* Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute @@ -1225,7 +1232,7 @@ static int qca_setup(struct hci_uart *hu) qca_baudrate = qca_get_baudrate_value(speed); } - if (qcadev->btsoc_type != QCA_WCN3990) { + if (soc_type != QCA_WCN3990) { /* Get QCA version information */ ret = qca_read_soc_version(hdev, &soc_ver); if (ret) @@ -1234,7 +1241,7 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver); /* Setup patch / NVM configurations */ - ret = qca_uart_setup(hdev, qca_baudrate, qcadev->btsoc_type, soc_ver); + ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver); if (!ret) { set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); qca_debugfs_init(hdev); @@ -1250,7 +1257,7 @@ static int qca_setup(struct hci_uart *hu) } /* Setup bdaddr */ - if (qcadev->btsoc_type == QCA_WCN3990) + if (soc_type == QCA_WCN3990) hu->hdev->set_bdaddr = qca_set_bdaddr; else hu->hdev->set_bdaddr = qca_set_bdaddr_rome; From 75c98a97958158154a61329992d70f08669b2b7a Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 12 Mar 2019 16:02:57 -0700 Subject: [PATCH 04/28] Bluetooth: btqca: Fix misspelling of 'baudrate' Rename the misspelled struct 'qca_bardrate' to 'qca_baudrate' Signed-off-by: Matthias Kaehlcke Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btqca.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index c72c56ea7480..6fdc25d7bba7 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -41,7 +41,7 @@ #define QCA_WCN3990_POWERON_PULSE 0xFC #define QCA_WCN3990_POWEROFF_PULSE 0xC0 -enum qca_bardrate { +enum qca_baudrate { QCA_BAUDRATE_115200 = 0, QCA_BAUDRATE_57600, QCA_BAUDRATE_38400, From ba8f5289f706aed94cc95b15cc5b89e22062f61f Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Thu, 14 Mar 2019 15:43:37 +0200 Subject: [PATCH 05/28] Bluetooth: Fix not initializing L2CAP tx_credits l2cap_le_flowctl_init was reseting the tx_credits which works only for outgoing connection since that set the tx_credits on the response, for incoming connections that was not the case which leaves the channel without any credits causing it to be suspended. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org # 4.20+ --- net/bluetooth/l2cap_core.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f17e393b43b4..b53acd6c9a3d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -510,12 +510,12 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) } EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults); -static void l2cap_le_flowctl_init(struct l2cap_chan *chan) +static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) { chan->sdu = NULL; chan->sdu_last_frag = NULL; chan->sdu_len = 0; - chan->tx_credits = 0; + chan->tx_credits = tx_credits; /* Derive MPS from connection MTU to stop HCI fragmentation */ chan->mps = min_t(u16, chan->imtu, chan->conn->mtu - L2CAP_HDR_SIZE); /* Give enough credits for a full packet */ @@ -1281,7 +1281,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan) if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags)) return; - l2cap_le_flowctl_init(chan); + l2cap_le_flowctl_init(chan, 0); req.psm = chan->psm; req.scid = cpu_to_le16(chan->scid); @@ -5532,11 +5532,10 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, chan->dcid = scid; chan->omtu = mtu; chan->remote_mps = mps; - chan->tx_credits = __le16_to_cpu(req->credits); __l2cap_chan_add(conn, chan); - l2cap_le_flowctl_init(chan); + l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits)); dcid = chan->scid; credits = chan->rx_credits; From bbb69b37be15e1cff74730b7fa5659e1ee705795 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Fri, 15 Mar 2019 03:17:28 +0000 Subject: [PATCH 06/28] Bluetooth: Add return check for L2CAP security level set Add return check for security level set for socket interface since stack will check the return value. Signed-off-by: Fugang Duan Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap_sock.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index dcb14abebeba..a7be8b59b3c2 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -791,10 +791,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, conn = chan->conn; - /*change security for LE channels */ + /* change security for LE channels */ if (chan->scid == L2CAP_CID_ATT) { - if (smp_conn_security(conn->hcon, sec.level)) + if (smp_conn_security(conn->hcon, sec.level)) { + err = -EINVAL; break; + } + set_bit(FLAG_PENDING_SECURITY, &chan->flags); sk->sk_state = BT_CONFIG; chan->state = BT_CONFIG; From db0a390835209c1c5dce7669de3d23a8cba10f34 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 14 Mar 2019 05:01:58 +0800 Subject: [PATCH 07/28] mmc: sdio: Add helper macro for sdio_driver boilerplate This patch introduces the module_sdio_driver macro which is a convenience macro for SDIO driver modules similar to module_usb_driver. It is intended to be used by drivers which init/exit section does nothing but register/ unregister the SDIO driver. By using this macro it is possible to eliminate a few lines of boilerplate code per SDIO driver. Suggested-by: Marcel Holtmann Signed-off-by: Sean Wang Acked-by: Ulf Hansson Signed-off-by: Marcel Holtmann --- include/linux/mmc/sdio_func.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index 97ca105347a6..5685805533b5 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -111,6 +111,18 @@ struct sdio_driver { extern int sdio_register_driver(struct sdio_driver *); extern void sdio_unregister_driver(struct sdio_driver *); +/** + * module_sdio_driver() - Helper macro for registering a SDIO driver + * @__sdio_driver: sdio_driver struct + * + * Helper macro for SDIO drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_sdio_driver(__sdio_driver) \ + module_driver(__sdio_driver, sdio_register_driver, \ + sdio_unregister_driver) + /* * SDIO I/O operations */ From a6094a468ffca372cb0107cc6bafc7c992477e7b Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 14 Mar 2019 05:01:59 +0800 Subject: [PATCH 08/28] Bluetooth: mediatek: Use module_sdio_driver helper Macro module_sdio_driver is used for drivers whose init and exit paths only register and unregister to SDIO API. So remove boilerplate code to make code simpler by using module_sdio_driver. Suggested-by: Marcel Holtmann Signed-off-by: Sean Wang Acked-by: Ulf Hansson Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtksdio.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index b4b8320f279e..befe43f9a34a 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -956,20 +956,7 @@ static struct sdio_driver btmtksdio_driver = { .id_table = btmtksdio_table, }; -static int __init btmtksdio_init(void) -{ - BT_INFO("MediaTek Bluetooth SDIO driver ver %s", VERSION); - - return sdio_register_driver(&btmtksdio_driver); -} - -static void __exit btmtksdio_exit(void) -{ - sdio_unregister_driver(&btmtksdio_driver); -} - -module_init(btmtksdio_init); -module_exit(btmtksdio_exit); +module_sdio_driver(btmtksdio_driver); MODULE_AUTHOR("Sean Wang "); MODULE_DESCRIPTION("MediaTek Bluetooth SDIO driver ver " VERSION); From afa8d3160add52e79c1d022ce22d20528d462910 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 14 Mar 2019 05:02:00 +0800 Subject: [PATCH 09/28] Bluetooth: btsdio: Use module_sdio_driver helper Macro module_sdio_driver is used for drivers whose init and exit paths only register and unregister to SDIO API. So remove boilerplate code to make code simpler by using module_sdio_driver. Signed-off-by: Sean Wang Acked-by: Ulf Hansson Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btsdio.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 282d1af1d3ba..4cfa9abe03c8 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -376,20 +376,7 @@ static struct sdio_driver btsdio_driver = { .id_table = btsdio_table, }; -static int __init btsdio_init(void) -{ - BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION); - - return sdio_register_driver(&btsdio_driver); -} - -static void __exit btsdio_exit(void) -{ - sdio_unregister_driver(&btsdio_driver); -} - -module_init(btsdio_init); -module_exit(btsdio_exit); +module_sdio_driver(btsdio_driver); MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION); From 637c8e9013912f3161260bcaf74a184440aae363 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 19 Mar 2019 04:58:33 +0800 Subject: [PATCH 10/28] Bluetooth: btmtksdio: fix uninitialized symbol errors in btmtksdio_rx_packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed all the below warnings. They would probably cause the following error handling path would use the uninitialized value and then produce unexpected behavior. drivers/bluetooth/btmtksdio.c:470:2: warning: ‘old_len’ may be used uninitialized in this function [-Wmaybe-uninitialized] print_hex_dump(KERN_ERR, "err sdio rx: ", DUMP_PREFIX_NONE, 4, 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ old_data, old_len, true); ~~~~~~~~~~~~~~~~~~~~~~~~ drivers/bluetooth/btmtksdio.c:376:15: note: ‘old_len’ was declared here unsigned int old_len; ^~~~~~~ drivers/bluetooth/btmtksdio.c:470:2: warning: ‘old_data’ may be used uninitialized in this function [-Wmaybe-uninitialized] print_hex_dump(KERN_ERR, "err sdio rx: ", DUMP_PREFIX_NONE, 4, 1, ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ old_data, old_len, true); ~~~~~~~~~~~~~~~~~~~~~~~~ drivers/bluetooth/btmtksdio.c:375:17: note: ‘old_data’ was declared here unsigned char *old_data; ^~~~~~~~ v2: Remove old_len and old_data because the error path for sdio_readsb also seems wrong. And change the prefix from "mediatek" to "btmtksdio". Fixes: d74eef2834b5 ("Bluetooth: mediatek: add support for MediaTek MT7663S and MT7668S SDIO devices") Reported-by: Dan Carpenter Reported-by: Marcel Holtmann Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtksdio.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index befe43f9a34a..7d0d1cb93b0e 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -372,8 +372,6 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size) const struct h4_recv_pkt *pkts = mtk_recv_pkts; int pkts_count = ARRAY_SIZE(mtk_recv_pkts); struct mtkbtsdio_hdr *sdio_hdr; - unsigned char *old_data; - unsigned int old_len; int err, i, pad_size; struct sk_buff *skb; u16 dlen; @@ -392,12 +390,6 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size) if (err < 0) goto err_kfree_skb; - /* Keep old data for dump the content in case of some error is - * caught in the following packet parsing. - */ - old_data = skb->data; - old_len = skb->len; - bdev->hdev->stat.byte_rx += rx_size; sdio_hdr = (void *)skb->data; @@ -467,8 +459,6 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size) return 0; err_kfree_skb: - print_hex_dump(KERN_ERR, "err sdio rx: ", DUMP_PREFIX_NONE, 4, 1, - old_data, old_len, true); kfree_skb(skb); return err; From cac63f9b163700fb70a609ad220697c61b797d6b Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Tue, 5 Mar 2019 08:14:25 +0800 Subject: [PATCH 11/28] Bluetooth: mediatek: Fixed incorrect type in assignment Fixed warning: incorrect type in assignment reported by kbuild test robot. The detailed warning is shown as below. make ARCH=x86_64 allmodconfig make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' All warnings (new ones prefixed by >>): btmtkuart.c:671:18: sparse: warning: incorrect type in assignment (different base types) btmtkuart.c:671:18: sparse: expected unsigned int [usertype] baudrate btmtkuart.c:671:18: sparse: got restricted __le32 [usertype] sparse warnings: (new ones prefixed by >>) btmtkuart.c:671:18: sparse: warning: incorrect type in assignment (different base types) btmtkuart.c:671:18: sparse: expected unsigned int [usertype] baudrate btmtkuart.c:671:18: sparse: got restricted __le32 [usertype] vim +671 drivers/bluetooth/btmtkuart.c 659 660 static int btmtkuart_change_baudrate(struct hci_dev *hdev) 661 { 662 struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); 663 struct btmtk_hci_wmt_params wmt_params; 664 u32 baudrate; 665 u8 param; 666 int err; 667 668 /* Indicate the device to enter the probe state the host is 669 * ready to change a new baudrate. 670 */ > 671 baudrate = cpu_to_le32(bdev->desired_speed); 672 wmt_params.op = MTK_WMT_HIF; Fixes: 22eaf6c9946a ("Bluetooth: mediatek: add support for MediaTek MT7663U and MT7668U UART devices") Reported-by: kernel test robot Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtkuart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index b0b680dd69f4..f5dbeec8e274 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -661,7 +661,7 @@ static int btmtkuart_change_baudrate(struct hci_dev *hdev) { struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); struct btmtk_hci_wmt_params wmt_params; - u32 baudrate; + __le32 baudrate; u8 param; int err; From 98df7446c2a2fcc62a605da054ccc0accda1a6a9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 23 Apr 2019 15:50:22 +0100 Subject: [PATCH 12/28] Bluetooth: hci_h5: fix spelling mistake "sliped" -> "slipped" There is a spelling mistake in a BT_DBG debug message. Fix it. Signed-off-by: Colin Ian King Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 069d1c8fde73..3f02ae560120 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -536,7 +536,7 @@ static void h5_unslip_one_byte(struct h5 *h5, unsigned char c) skb_put_data(h5->rx_skb, byte, 1); h5->rx_pending--; - BT_DBG("unsliped 0x%02hhx, rx_pending %zu", *byte, h5->rx_pending); + BT_DBG("unslipped 0x%02hhx, rx_pending %zu", *byte, h5->rx_pending); } static void h5_reset_rx(struct h5 *h5) From e1052fb282a4e44d990e857c18edbd943d9ecdaf Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 18 Apr 2019 17:07:59 +0800 Subject: [PATCH 13/28] Bluetooth: btmtksdio: Drop newline with bt_dev logging macros bt_dev logging macros already include a newline at each output so drop these unnecessary additional newlines in the driver. Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtksdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 7d0d1cb93b0e..681e3e34977e 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -487,15 +487,15 @@ static void btmtksdio_interrupt(struct sdio_func *func) sdio_writel(func, int_status, MTK_REG_CHISR, NULL); if (unlikely(!int_status)) - bt_dev_err(bdev->hdev, "CHISR is 0\n"); + bt_dev_err(bdev->hdev, "CHISR is 0"); if (int_status & FW_OWN_BACK_INT) - bt_dev_dbg(bdev->hdev, "Get fw own back\n"); + bt_dev_dbg(bdev->hdev, "Get fw own back"); if (int_status & TX_EMPTY) schedule_work(&bdev->tx_work); else if (unlikely(int_status & TX_FIFO_OVERFLOW)) - bt_dev_warn(bdev->hdev, "Tx fifo overflow\n"); + bt_dev_warn(bdev->hdev, "Tx fifo overflow"); if (int_status & RX_DONE_INT) { rx_size = (int_status & RX_PKT_LEN) >> 16; From 2e47cc2b3a7dcef7deab3301796f21f80e161b13 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 18 Apr 2019 17:08:00 +0800 Subject: [PATCH 14/28] Bluetooth: btmtksdio: Add a bit definition for CHLPCR Add a register bit definition about CHLPCR bit 8 because the bit is quite different in the meaning between reading and writing that bit. The patch adds a definition particularly for the bit read to avoid the confusion about using write definition to read the bit. Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtksdio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 681e3e34977e..9c123a9de673 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -56,7 +56,8 @@ static const struct sdio_device_id btmtksdio_table[] = { #define MTK_REG_CHLPCR 0x4 /* W1S */ #define C_INT_EN_SET BIT(0) #define C_INT_EN_CLR BIT(1) -#define C_FW_OWN_REQ_SET BIT(8) +#define C_FW_OWN_REQ_SET BIT(8) /* For write */ +#define C_COM_DRV_OWN BIT(8) /* For read */ #define C_FW_OWN_REQ_CLR BIT(9) #define MTK_REG_CSDIOCSR 0x8 @@ -526,7 +527,7 @@ static int btmtksdio_open(struct hci_dev *hdev) goto err_disable_func; err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, - status & C_FW_OWN_REQ_SET, 2000, 1000000); + status & C_COM_DRV_OWN, 2000, 1000000); if (err < 0) { bt_dev_err(bdev->hdev, "Cannot get ownership from device"); goto err_disable_func; @@ -606,7 +607,7 @@ static int btmtksdio_close(struct hci_dev *hdev) sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, NULL); err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, - !(status & C_FW_OWN_REQ_SET), 2000, 1000000); + !(status & C_COM_DRV_OWN), 2000, 1000000); if (err < 0) bt_dev_err(bdev->hdev, "Cannot return ownership to device"); From bcaa7d72dffddfa4196a37108d67fc12fb4edfca Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 18 Apr 2019 17:08:01 +0800 Subject: [PATCH 15/28] Bluetooth: btmtksdio: Fix hdev->stat.byte_rx accumulation Accumulate hdev->stat.byte_rx only for valid packets as btmtkuart doing. Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtksdio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 9c123a9de673..877c0a831775 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -391,8 +391,6 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size) if (err < 0) goto err_kfree_skb; - bdev->hdev->stat.byte_rx += rx_size; - sdio_hdr = (void *)skb->data; /* We assume the default error as -EILSEQ simply to make the error path @@ -457,6 +455,8 @@ static int btmtksdio_rx_packet(struct btmtksdio_dev *bdev, u16 rx_size) /* Complete frame */ (&pkts[i])->recv(bdev->hdev, skb); + bdev->hdev->stat.byte_rx += rx_size; + return 0; err_kfree_skb: From 7f3c563c575e73c689fe2762c5ec61159caa1568 Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Thu, 18 Apr 2019 17:08:02 +0800 Subject: [PATCH 16/28] Bluetooth: btmtksdio: Add runtime PM support to SDIO based Bluetooth Add runtime PM support to btmtksdio. With this way, there will be the benefit of the device entering the more power saving state once it is been a while data traffic is idle. Signed-off-by: Sean Wang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmtksdio.c | 144 ++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 877c0a831775..813338288453 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,10 @@ #define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin" #define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin" +#define MTKBTSDIO_AUTOSUSPEND_DELAY 8000 + +static bool enable_autosuspend; + struct btmtksdio_data { const char *fwname; }; @@ -150,6 +155,7 @@ struct btmtk_hci_wmt_params { struct btmtksdio_dev { struct hci_dev *hdev; struct sdio_func *func; + struct device *dev; struct work_struct tx_work; unsigned long tx_state; @@ -301,6 +307,8 @@ static void btmtksdio_tx_work(struct work_struct *work) struct sk_buff *skb; int err; + pm_runtime_get_sync(bdev->dev); + sdio_claim_host(bdev->func); while ((skb = skb_dequeue(&bdev->txq))) { @@ -313,6 +321,9 @@ static void btmtksdio_tx_work(struct work_struct *work) } sdio_release_host(bdev->func); + + pm_runtime_mark_last_busy(bdev->dev); + pm_runtime_put_autosuspend(bdev->dev); } static int btmtksdio_recv_event(struct hci_dev *hdev, struct sk_buff *skb) @@ -471,6 +482,18 @@ static void btmtksdio_interrupt(struct sdio_func *func) u32 int_status; u16 rx_size; + /* It is required that the host gets ownership from the device before + * accessing any register, however, if SDIO host is not being released, + * a potential deadlock probably happens in a circular wait between SDIO + * IRQ work and PM runtime work. So, we have to explicitly release SDIO + * host here and claim again after the PM runtime work is all done. + */ + sdio_release_host(bdev->func); + + pm_runtime_get_sync(bdev->dev); + + sdio_claim_host(bdev->func); + /* Disable interrupt */ sdio_writel(func, C_INT_EN_CLR, MTK_REG_CHLPCR, 0); @@ -507,6 +530,9 @@ static void btmtksdio_interrupt(struct sdio_func *func) /* Enable interrupt */ sdio_writel(func, C_INT_EN_SET, MTK_REG_CHLPCR, 0); + + pm_runtime_mark_last_busy(bdev->dev); + pm_runtime_put_autosuspend(bdev->dev); } static int btmtksdio_open(struct hci_dev *hdev) @@ -815,6 +841,23 @@ static int btmtksdio_setup(struct hci_dev *hdev) delta = ktime_sub(rettime, calltime); duration = (unsigned long long)ktime_to_ns(delta) >> 10; + pm_runtime_set_autosuspend_delay(bdev->dev, + MTKBTSDIO_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(bdev->dev); + + err = pm_runtime_set_active(bdev->dev); + if (err < 0) + return err; + + /* Default forbid runtime auto suspend, that can be allowed by + * enable_autosuspend flag or the PM runtime entry under sysfs. + */ + pm_runtime_forbid(bdev->dev); + pm_runtime_enable(bdev->dev); + + if (enable_autosuspend) + pm_runtime_allow(bdev->dev); + bt_dev_info(hdev, "Device setup in %llu usecs", duration); return 0; @@ -822,10 +865,16 @@ static int btmtksdio_setup(struct hci_dev *hdev) static int btmtksdio_shutdown(struct hci_dev *hdev) { + struct btmtksdio_dev *bdev = hci_get_drvdata(hdev); struct btmtk_hci_wmt_params wmt_params; u8 param = 0x0; int err; + /* Get back the state to be consistent with the state + * in btmtksdio_setup. + */ + pm_runtime_get_sync(bdev->dev); + /* Disable the device */ wmt_params.op = MTK_WMT_FUNC_CTRL; wmt_params.flag = 0; @@ -839,6 +888,9 @@ static int btmtksdio_shutdown(struct hci_dev *hdev) return err; } + pm_runtime_put_noidle(bdev->dev); + pm_runtime_disable(bdev->dev); + return 0; } @@ -885,6 +937,7 @@ static int btmtksdio_probe(struct sdio_func *func, if (!bdev->data) return -ENODEV; + bdev->dev = &func->dev; bdev->func = func; INIT_WORK(&bdev->tx_work, btmtksdio_tx_work); @@ -922,6 +975,25 @@ static int btmtksdio_probe(struct sdio_func *func, sdio_set_drvdata(func, bdev); + /* pm_runtime_enable would be done after the firmware is being + * downloaded because the core layer probably already enables + * runtime PM for this func such as the case host->caps & + * MMC_CAP_POWER_OFF_CARD. + */ + if (pm_runtime_enabled(bdev->dev)) + pm_runtime_disable(bdev->dev); + + /* As explaination in drivers/mmc/core/sdio_bus.c tells us: + * Unbound SDIO functions are always suspended. + * During probe, the function is set active and the usage count + * is incremented. If the driver supports runtime PM, + * it should call pm_runtime_put_noidle() in its probe routine and + * pm_runtime_get_noresume() in its remove routine. + * + * So, put a pm_runtime_put_noidle here ! + */ + pm_runtime_put_noidle(bdev->dev); + return 0; } @@ -933,6 +1005,9 @@ static void btmtksdio_remove(struct sdio_func *func) if (!bdev) return; + /* Be consistent the state in btmtksdio_probe */ + pm_runtime_get_noresume(bdev->dev); + hdev = bdev->hdev; sdio_set_drvdata(func, NULL); @@ -940,15 +1015,84 @@ static void btmtksdio_remove(struct sdio_func *func) hci_free_dev(hdev); } +#ifdef CONFIG_PM +static int btmtksdio_runtime_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct btmtksdio_dev *bdev; + u32 status; + int err; + + bdev = sdio_get_drvdata(func); + if (!bdev) + return 0; + + sdio_claim_host(bdev->func); + + sdio_writel(bdev->func, C_FW_OWN_REQ_SET, MTK_REG_CHLPCR, &err); + if (err < 0) + goto out; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + !(status & C_COM_DRV_OWN), 2000, 1000000); +out: + bt_dev_info(bdev->hdev, "status (%d) return ownership to device", err); + + sdio_release_host(bdev->func); + + return err; +} + +static int btmtksdio_runtime_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct btmtksdio_dev *bdev; + u32 status; + int err; + + bdev = sdio_get_drvdata(func); + if (!bdev) + return 0; + + sdio_claim_host(bdev->func); + + sdio_writel(bdev->func, C_FW_OWN_REQ_CLR, MTK_REG_CHLPCR, &err); + if (err < 0) + goto out; + + err = readx_poll_timeout(btmtksdio_drv_own_query, bdev, status, + status & C_COM_DRV_OWN, 2000, 1000000); +out: + bt_dev_info(bdev->hdev, "status (%d) get ownership from device", err); + + sdio_release_host(bdev->func); + + return err; +} + +static UNIVERSAL_DEV_PM_OPS(btmtksdio_pm_ops, btmtksdio_runtime_suspend, + btmtksdio_runtime_resume, NULL); +#define BTMTKSDIO_PM_OPS (&btmtksdio_pm_ops) +#else /* CONFIG_PM */ +#define BTMTKSDIO_PM_OPS NULL +#endif /* CONFIG_PM */ + static struct sdio_driver btmtksdio_driver = { .name = "btmtksdio", .probe = btmtksdio_probe, .remove = btmtksdio_remove, .id_table = btmtksdio_table, + .drv = { + .owner = THIS_MODULE, + .pm = BTMTKSDIO_PM_OPS, + } }; module_sdio_driver(btmtksdio_driver); +module_param(enable_autosuspend, bool, 0644); +MODULE_PARM_DESC(enable_autosuspend, "Enable autosuspend by default"); + MODULE_AUTHOR("Sean Wang "); MODULE_DESCRIPTION("MediaTek Bluetooth SDIO driver ver " VERSION); MODULE_VERSION(VERSION); From 73623340546cceff421c95b53abd8140d1f2b2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Sz=C5=B1cs?= Date: Sun, 14 Apr 2019 20:38:14 +0200 Subject: [PATCH 17/28] Bluetooth: btmrvl: add support for SD8987 chipset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for Marvell 88W8987 chipset with SDIO interface. Register offsets and supported feature flags are updated. The corresponding firmware image file shall be "mrvl/sd8987_uapsta.bin". Signed-off-by: Tamás Szűcs Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 4 ++-- drivers/bluetooth/btmrvl_sdio.c | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index b0f9a20401d6..b9c34ff9a0d3 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -336,7 +336,7 @@ config BT_MRVL The core driver to support Marvell Bluetooth devices. This driver is required if you want to support - Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8977/8997. + Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8977/8987/8997. Say Y here to compile Marvell Bluetooth driver into the kernel or say M to compile it as module. @@ -350,7 +350,7 @@ config BT_MRVL_SDIO The driver for Marvell Bluetooth chipsets with SDIO interface. This driver is required if you want to use Marvell Bluetooth - devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8977/SD8997 + devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8977/SD8987/SD8997 chipsets are supported. Say Y here to compile support for Marvell BT-over-SDIO driver diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 047b75ce1deb..0f3a020703ab 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -235,6 +235,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8977 = { .fw_dump_end = 0xf8, }; +static const struct btmrvl_sdio_card_reg btmrvl_reg_8987 = { + .cfg = 0x00, + .host_int_mask = 0x08, + .host_intstatus = 0x0c, + .card_status = 0x5c, + .sq_read_base_addr_a0 = 0xf8, + .sq_read_base_addr_a1 = 0xf9, + .card_revision = 0xc8, + .card_fw_status0 = 0xe8, + .card_fw_status1 = 0xe9, + .card_rx_len = 0xea, + .card_rx_unit = 0xeb, + .io_port_0 = 0xe4, + .io_port_1 = 0xe5, + .io_port_2 = 0xe6, + .int_read_to_clear = true, + .host_int_rsr = 0x04, + .card_misc_cfg = 0xd8, + .fw_dump_ctrl = 0xf0, + .fw_dump_start = 0xf1, + .fw_dump_end = 0xf8, +}; + static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = { .cfg = 0x00, .host_int_mask = 0x08, @@ -312,6 +335,15 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8977 = { .supports_fw_dump = true, }; +static const struct btmrvl_sdio_device btmrvl_sdio_sd8987 = { + .helper = NULL, + .firmware = "mrvl/sd8987_uapsta.bin", + .reg = &btmrvl_reg_8987, + .support_pscan_win_report = true, + .sd_blksz_fw_dl = 256, + .supports_fw_dump = true, +}; + static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = { .helper = NULL, .firmware = "mrvl/sd8997_uapsta.bin", @@ -343,6 +375,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8977 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9146), .driver_data = (unsigned long)&btmrvl_sdio_sd8977 }, + /* Marvell SD8987 Bluetooth device */ + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x914A), + .driver_data = (unsigned long)&btmrvl_sdio_sd8987 }, /* Marvell SD8997 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142), .driver_data = (unsigned long)&btmrvl_sdio_sd8997 }, @@ -1797,4 +1832,5 @@ MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8977_uapsta.bin"); +MODULE_FIRMWARE("mrvl/sd8987_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin"); From a1616a5ac99ede5d605047a9012481ce7ff18b16 Mon Sep 17 00:00:00 2001 From: Young Xiao Date: Fri, 12 Apr 2019 15:24:30 +0800 Subject: [PATCH 18/28] Bluetooth: hidp: fix buffer overflow Struct ca is copied from userspace. It is not checked whether the "name" field is NULL terminated, which allows local users to obtain potentially sensitive information from kernel stack memory, via a HIDPCONNADD command. This vulnerability is similar to CVE-2011-1079. Signed-off-by: Young Xiao Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/hidp/sock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index 9f85a1943be9..2151913892ce 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -75,6 +75,7 @@ static int do_hidp_sock_ioctl(struct socket *sock, unsigned int cmd, void __user sockfd_put(csock); return err; } + ca.name[sizeof(ca.name)-1] = 0; err = hidp_connection_add(&ca, csock, isock); if (!err && copy_to_user(argp, &ca, sizeof(ca))) From 5035726128cd2e3813ee44deedb9898509edb232 Mon Sep 17 00:00:00 2001 From: Ferry Toth Date: Tue, 9 Apr 2019 16:15:50 +0200 Subject: [PATCH 19/28] Bluetooth: btbcm: Add default address for BCM43341B The BCM43341B has the default MAC address 43:34:1B:00:1F:AC if none is given. This address was found when enabling Bluetooth on multiple Intel Edison modules. It also contains the sequence 43341B, the name the chip identifies itself as. Using the same BD_ADDR is problematic when having multiple Intel Edison modules in each others range. The default address also has the LAA (locally administered address) bit set which prevents a BNEP device from being created, needed for BT tethering. Add this to the list of black listed default MAC addresses and let the user configure a valid one using f.i. `btmgmt -i hci0 public-addr xx:xx:xx:xx:xx:xx` Suggested-by: Andy Shevchenko Signed-off-by: Ferry Toth Reviewed-by: Andy Shevchenko Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btbcm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index d5d6e6e5da3b..62d3aa2b26f6 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -37,6 +37,7 @@ #define BDADDR_BCM43430A0 (&(bdaddr_t) {{0xac, 0x1f, 0x12, 0xa0, 0x43, 0x43}}) #define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}}) #define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}}) +#define BDADDR_BCM43341B (&(bdaddr_t) {{0xac, 0x1f, 0x00, 0x1b, 0x34, 0x43}}) int btbcm_check_bdaddr(struct hci_dev *hdev) { @@ -82,7 +83,8 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) !bacmp(&bda->bdaddr, BDADDR_BCM20702A1) || !bacmp(&bda->bdaddr, BDADDR_BCM4324B3) || !bacmp(&bda->bdaddr, BDADDR_BCM4330B1) || - !bacmp(&bda->bdaddr, BDADDR_BCM43430A0)) { + !bacmp(&bda->bdaddr, BDADDR_BCM43430A0) || + !bacmp(&bda->bdaddr, BDADDR_BCM43341B)) { bt_dev_info(hdev, "BCM: Using default device address (%pMR)", &bda->bdaddr); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); From f57c4bbf34439531adccd7d3a4ecc14f409c1399 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 3 Apr 2019 08:34:16 +0300 Subject: [PATCH 20/28] 6lowpan: Off by one handling ->nexthdr NEXTHDR_MAX is 255. What happens here is that we take a u8 value "hdr->nexthdr" from the network and then look it up in lowpan_nexthdr_nhcs[]. The problem is that if hdr->nexthdr is 0xff then we read one element beyond the end of the array so the array needs to be one element larger. Fixes: 92aa7c65d295 ("6lowpan: add generic nhc layer interface") Signed-off-by: Dan Carpenter Acked-by: Jukka Rissanen Acked-by: Alexander Aring Signed-off-by: Marcel Holtmann --- net/6lowpan/nhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/6lowpan/nhc.c b/net/6lowpan/nhc.c index 4fa2fdda174d..9e56fb98f33c 100644 --- a/net/6lowpan/nhc.c +++ b/net/6lowpan/nhc.c @@ -18,7 +18,7 @@ #include "nhc.h" static struct rb_root rb_root = RB_ROOT; -static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX]; +static struct lowpan_nhc *lowpan_nexthdr_nhcs[NEXTHDR_MAX + 1]; static DEFINE_SPINLOCK(lowpan_nhc_lock); static int lowpan_nhc_insert(struct lowpan_nhc *nhc) From 039287aa9f7247f27ecae70a6e4aefa43f431d6b Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 5 Mar 2019 14:09:00 +0100 Subject: [PATCH 21/28] Bluetooth: btbcm: Add entry for BCM2076B1 UART Bluetooth Add the device ID for the BT/FM/GPS combo chip BCM2076 (rev B1) used in the AMPAK AP6476 WiFi/BT/FM/GPS module. Signed-off-by: Stephan Gerhold Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btbcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 62d3aa2b26f6..71e74ec08310 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -335,6 +335,7 @@ struct bcm_subver_table { static const struct bcm_subver_table bcm_uart_subver_table[] = { { 0x4103, "BCM4330B1" }, /* 002.001.003 */ { 0x410e, "BCM43341B0" }, /* 002.001.014 */ + { 0x4204, "BCM2076B1" }, /* 002.002.004 */ { 0x4406, "BCM4324B3" }, /* 002.004.006 */ { 0x6109, "BCM4335C0" }, /* 003.001.009 */ { 0x610c, "BCM4354" }, /* 003.001.012 */ From cd9151b618da4723877bd94eae952f2e50acbc0e Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Wed, 3 Apr 2019 12:11:44 +0530 Subject: [PATCH 22/28] Bluetooth: Fix incorrect pointer arithmatic in ext_adv_report_evt In ext_adv_report_event rssi comes before data (not after data as in legacy adv_report_evt) so "+ 1" is not required in the ptr arithmatic to point to next report. Signed-off-by: Jaganath Kanakkassery Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 609fd6871c5a..66b631ab0d35 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -5433,7 +5433,7 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) ev->data, ev->length); } - ptr += sizeof(*ev) + ev->length + 1; + ptr += sizeof(*ev) + ev->length; } hci_dev_unlock(hdev); From 62611abc8f37d00e3b0cff0eb2d72fa92b05fd27 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Mon, 1 Apr 2019 11:43:12 +0800 Subject: [PATCH 23/28] Bluetooth: hci_bcm: Fix empty regulator supplies for Intel Macs The code path for Macs goes through bcm_apple_get_resources(), which skips over the code that sets up the regulator supplies. As a result, the call to regulator_bulk_enable() / regulator_bulk_disable() results in a NULL pointer dereference. This was reported on the kernel.org Bugzilla, bug 202963. Unbreak Broadcom Bluetooth support on Intel Macs by checking if the supplies were set up before enabling or disabling them. The same does not need to be done for the clocks, as the common clock framework API checks for NULL pointers. Fixes: 75d11676dccb ("Bluetooth: hci_bcm: Add support for regulator supplies") Cc: # 5.0.x Signed-off-by: Chen-Yu Tsai Tested-by: Imre Kaloz Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_bcm.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index ddbe518c3e5b..b5d31d583d60 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -228,9 +228,15 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered) int err; if (powered && !dev->res_enabled) { - err = regulator_bulk_enable(BCM_NUM_SUPPLIES, dev->supplies); - if (err) - return err; + /* Intel Macs use bcm_apple_get_resources() and don't + * have regulator supplies configured. + */ + if (dev->supplies[0].supply) { + err = regulator_bulk_enable(BCM_NUM_SUPPLIES, + dev->supplies); + if (err) + return err; + } /* LPO clock needs to be 32.768 kHz */ err = clk_set_rate(dev->lpo_clk, 32768); @@ -259,7 +265,13 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered) if (!powered && dev->res_enabled) { clk_disable_unprepare(dev->txco_clk); clk_disable_unprepare(dev->lpo_clk); - regulator_bulk_disable(BCM_NUM_SUPPLIES, dev->supplies); + + /* Intel Macs use bcm_apple_get_resources() and don't + * have regulator supplies configured. + */ + if (dev->supplies[0].supply) + regulator_bulk_disable(BCM_NUM_SUPPLIES, + dev->supplies); } /* wait for device to power on and come out of reset */ From 7f09d5a6c33be66a5ca19bf9dd1c2d90c5dfcf0d Mon Sep 17 00:00:00 2001 From: Balakrishna Godavarthi Date: Mon, 1 Apr 2019 15:19:08 +0530 Subject: [PATCH 24/28] Bluetooth: hci_qca: Give enough time to ROME controller to bootup. This patch enables enough time to ROME controller to bootup after we bring the enable pin out of reset. Fixes: 05ba533c5c11 ("Bluetooth: hci_qca: Add serdev support"). Signed-off-by: Balakrishna Godavarthi Reviewed-by: Rocky Liao Tested-by: Rocky Liao Tested-by: Claire Chang Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 4ea995d610d2..a80c3bc90904 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -523,6 +523,8 @@ static int qca_open(struct hci_uart *hu) qcadev = serdev_device_get_drvdata(hu->serdev); if (qcadev->btsoc_type != QCA_WCN3990) { gpiod_set_value_cansleep(qcadev->bt_en, 1); + /* Controller needs time to bootup. */ + msleep(150); } else { hu->init_speed = qcadev->init_speed; hu->oper_speed = qcadev->oper_speed; From 5bec1fb866df8f58b04a46bcbe27481214977e4c Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 28 Mar 2019 12:30:29 -0500 Subject: [PATCH 25/28] Bluetooth: Use struct_size() helper One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct foo { int stuff; struct boo entry[]; }; size = sizeof(struct foo) + count * sizeof(struct boo); Instead of leaving these open-coded and prone to type mistakes, we can now use the new struct_size() helper: size = struct_size(instance, entry, count); This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2457f408d17d..150114e33b20 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2301,8 +2301,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS); } - expected_len = sizeof(*cp) + key_count * - sizeof(struct mgmt_link_key_info); + expected_len = struct_size(cp, keys, key_count); if (expected_len != len) { bt_dev_err(hdev, "load_link_keys: expected %u bytes, got %u bytes", expected_len, len); @@ -5030,7 +5029,7 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, MGMT_STATUS_INVALID_PARAMS); } - expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info); + expected_len = struct_size(cp, irks, irk_count); if (expected_len != len) { bt_dev_err(hdev, "load_irks: expected %u bytes, got %u bytes", expected_len, len); @@ -5112,8 +5111,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, MGMT_STATUS_INVALID_PARAMS); } - expected_len = sizeof(*cp) + key_count * - sizeof(struct mgmt_ltk_info); + expected_len = struct_size(cp, keys, key_count); if (expected_len != len) { bt_dev_err(hdev, "load_keys: expected %u bytes, got %u bytes", expected_len, len); @@ -5847,8 +5845,7 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS); } - expected_len = sizeof(*cp) + param_count * - sizeof(struct mgmt_conn_param); + expected_len = struct_size(cp, params, param_count); if (expected_len != len) { bt_dev_err(hdev, "load_conn_param: expected %u bytes, got %u bytes", expected_len, len); From ecf2b768bd11e2ff09ecbe621b387d0d58e970cf Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 23 Apr 2019 11:16:52 -0700 Subject: [PATCH 26/28] Bluetooth: hci_qca: Fix crash with non-serdev devices qca_set_baudrate() calls serdev_device_wait_until_sent() assuming that the HCI is always associated with a serdev device. This isn't true for ROME controllers instantiated through ldisc, where the call causes a crash due to a NULL pointer dereferentiation. Only call the function when we have a serdev device. The timeout for ROME devices at the end of qca_set_baudrate() is long enough to be reasonably sure that the command was sent. Fixes: fa9ad876b8e0 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth chip wcn3990") Reported-by: Balakrishna Godavarthi Reported-by: Rocky Liao Signed-off-by: Matthias Kaehlcke Reviewed-by: Rocky Liao Tested-by: Rocky Liao Reviewed-by: Balakrishna Godavarthi Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_qca.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index a80c3bc90904..7f75652686fe 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1006,7 +1006,8 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) while (!skb_queue_empty(&qca->txq)) usleep_range(100, 200); - serdev_device_wait_until_sent(hu->serdev, + if (hu->serdev) + serdev_device_wait_until_sent(hu->serdev, msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS)); /* Give the controller time to process the request */ From e668eb1e1578f4fec1cf85ea62e43cb0814b6a6e Mon Sep 17 00:00:00 2001 From: Balakrishna Godavarthi Date: Thu, 18 Apr 2019 18:51:23 +0530 Subject: [PATCH 27/28] Bluetooth: hci_core: Don't stop BT if the BD address missing in dts When flag HCI_QUIRK_USE_BDADDR_PROPERTY is set, we will read the bluetooth address from dts. If the bluetooth address node is missing from the dts we will enable it controller UNCONFIGURED state. This patch enables the normal flow even if the BD address is missing from the dts tree. Signed-off-by: Balakrishna Godavarthi Tested-by: Harish Bandi Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d6b2540ba7f8..3d9175f130b3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1460,8 +1460,6 @@ static int hci_dev_do_open(struct hci_dev *hdev) hdev->set_bdaddr) ret = hdev->set_bdaddr(hdev, &hdev->public_addr); - else - ret = -EADDRNOTAVAIL; } setup_failed: From d5bb334a8e171b262e48f378bd2096c0ea458265 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 24 Apr 2019 22:19:17 +0200 Subject: [PATCH 28/28] Bluetooth: Align minimum encryption key size for LE and BR/EDR connections The minimum encryption key size for LE connections is 56 bits and to align LE with BR/EDR, enforce 56 bits of minimum encryption key size for BR/EDR connections as well. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg Cc: stable@vger.kernel.org --- include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/hci_conn.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 094e61e07030..05b1b96f4d9e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -190,6 +190,9 @@ struct adv_info { #define HCI_MAX_SHORT_NAME_LENGTH 10 +/* Min encryption key size to match with SMP */ +#define HCI_MIN_ENC_KEY_SIZE 7 + /* Default LE RPA expiry time, 15 minutes */ #define HCI_DEFAULT_RPA_TIMEOUT (15 * 60) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index bd4978ce8c45..3cf0764d5793 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1276,6 +1276,14 @@ int hci_conn_check_link_mode(struct hci_conn *conn) !test_bit(HCI_CONN_ENCRYPT, &conn->flags)) return 0; + /* The minimum encryption key size needs to be enforced by the + * host stack before establishing any L2CAP connections. The + * specification in theory allows a minimum of 1, but to align + * BR/EDR and LE transports, a minimum of 7 is chosen. + */ + if (conn->enc_key_size < HCI_MIN_ENC_KEY_SIZE) + return 0; + return 1; }