diff options
author | David S. Miller <davem@davemloft.net> | 2018-08-10 14:24:57 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-08-10 14:24:57 -0700 |
commit | 0780b866668ed5f88fd79e96ea8ac94258939ae0 (patch) | |
tree | 313c6b20a1891fe10f7f8d67bbaa62e654af9495 | |
parent | fd685657cd5441e504113db1928196d030c8c414 (diff) | |
parent | aa12af77aae05008b3e637b85944dcd512f75eba (diff) | |
download | linux-0780b866668ed5f88fd79e96ea8ac94258939ae0.tar.bz2 |
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says:
====================
pull request: bluetooth-next 2018-08-10
Here's one more (most likely last) bluetooth-next pull request for the
4.19 kernel.
- Added support for MediaTek serial Bluetooth devices
- Initial skeleton for controller-side address resolution support
- Fix BT_HCIUART_RTL related Kconfig dependencies
- A few other minor fixes/cleanups
Please let me know if there are any issues pulling. Thanks.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Documentation/devicetree/bindings/net/mediatek-bluetooth.txt | 35 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/bluetooth/Kconfig | 24 | ||||
-rw-r--r-- | drivers/bluetooth/Makefile | 1 | ||||
-rw-r--r-- | drivers/bluetooth/btmtkuart.c | 629 | ||||
-rw-r--r-- | drivers/bluetooth/btqca.c | 6 | ||||
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 4 | ||||
-rw-r--r-- | include/net/bluetooth/hci.h | 5 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 31 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 3 |
10 files changed, 737 insertions, 9 deletions
diff --git a/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt b/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt new file mode 100644 index 000000000000..14ceb2a5b4e8 --- /dev/null +++ b/Documentation/devicetree/bindings/net/mediatek-bluetooth.txt @@ -0,0 +1,35 @@ +MediaTek SoC built-in Bluetooth Devices +================================== + +This device is a serial attached device to BTIF device and thus it must be a +child node of the serial node with BTIF. The dt-bindings details for BTIF +device can be known via Documentation/devicetree/bindings/serial/8250.txt. + +Required properties: + +- compatible: Must be + "mediatek,mt7622-bluetooth": for MT7622 SoC +- clocks: Should be the clock specifiers corresponding to the entry in + clock-names property. +- clock-names: Should contain "ref" entries. +- power-domains: Phandle to the power domain that the device is part of + +Example: + + btif: serial@1100c000 { + compatible = "mediatek,mt7622-btif", + "mediatek,mtk-btif"; + reg = <0 0x1100c000 0 0x1000>; + interrupts = <GIC_SPI 90 IRQ_TYPE_LEVEL_LOW>; + clocks = <&pericfg CLK_PERI_BTIF_PD>; + clock-names = "main"; + reg-shift = <2>; + reg-io-width = <4>; + + bluetooth { + compatible = "mediatek,mt7622-bluetooth"; + power-domains = <&scpsys MT7622_POWER_DOMAIN_WB>; + clocks = <&clk25m>; + clock-names = "ref"; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 82f277462349..9777a1dd6ff6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8988,6 +8988,14 @@ F: include/uapi/linux/meye.h F: include/uapi/linux/ivtv* F: include/uapi/linux/uvcvideo.h +MEDIATEK BLUETOOTH DRIVER +M: Sean Wang <sean.wang@mediatek.com> +L: linux-bluetooth@vger.kernel.org +L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/net/mediatek-bluetooth.txt +F: drivers/bluetooth/btmtkuart.c + MEDIATEK CIR DRIVER M: Sean Wang <sean.wang@mediatek.com> S: Maintained diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 5f953ca8ac5b..2df11cc08a46 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -195,6 +195,19 @@ config BT_HCIUART_BCM Say Y here to compile support for Broadcom protocol. +config BT_HCIUART_RTL + bool "Realtek protocol support" + depends on BT_HCIUART + depends on BT_HCIUART_SERDEV + depends on GPIOLIB + select BT_HCIUART_3WIRE + select BT_RTL + help + The Realtek protocol support enables Bluetooth HCI over 3-Wire + serial port internface for Realtek Bluetooth controllers. + + Say Y here to compile support for Realtek protocol. + config BT_HCIUART_QCA bool "Qualcomm Atheros protocol support" depends on BT_HCIUART @@ -365,6 +378,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_MTKUART + tristate "MediaTek HCI UART driver" + depends on SERIAL_DEV_BUS + help + MediaTek Bluetooth HCI UART driver. + This driver is required if you want to use MediaTek Bluetooth + with serial interface. + + Say Y here to compile support for MediaTek Bluetooth UART devices + into the kernel or say M to compile it as module (btmtkuart). + config BT_QCOMSMD tristate "Qualcomm SMD based HCI support" depends on RPMSG || (COMPILE_TEST && RPMSG=n) diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index ec16c55eb6e9..b7e393cfc1e3 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_MTKUART) += btmtkuart.o obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o obj-$(CONFIG_BT_BCM) += btbcm.o obj-$(CONFIG_BT_RTL) += btrtl.o diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c new file mode 100644 index 000000000000..e0571fe04d00 --- /dev/null +++ b/drivers/bluetooth/btmtkuart.c @@ -0,0 +1,629 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 MediaTek Inc. + +/* + * Bluetooth support for MediaTek serial devices + * + * Author: Sean Wang <sean.wang@mediatek.com> + * + */ + +#include <asm/unaligned.h> +#include <linux/atomic.h> +#include <linux/clk.h> +#include <linux/firmware.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm_runtime.h> +#include <linux/serdev.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "h4_recv.h" + +#define VERSION "0.1" + +#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin" + +#define MTK_STP_TLR_SIZE 2 + +#define BTMTKUART_TX_STATE_ACTIVE 1 +#define BTMTKUART_TX_STATE_WAKEUP 2 +#define BTMTKUART_TX_WAIT_VND_EVT 3 + +enum { + MTK_WMT_PATCH_DWNLD = 0x1, + MTK_WMT_FUNC_CTRL = 0x6, + MTK_WMT_RST = 0x7 +}; + +struct mtk_stp_hdr { + u8 prefix; + __be16 dlen; + u8 cs; +} __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 btmtkuart_dev { + struct hci_dev *hdev; + struct serdev_device *serdev; + struct clk *clk; + + struct work_struct tx_work; + unsigned long tx_state; + struct sk_buff_head txq; + + struct sk_buff *rx_skb; + + u8 stp_pad[6]; + u8 stp_cursor; + u16 stp_dlen; +}; + +static int mtk_hci_wmt_sync(struct hci_dev *hdev, u8 op, u8 flag, u16 plen, + const void *param) +{ + struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); + struct mtk_hci_wmt_cmd wc; + struct mtk_wmt_hdr *hdr; + u32 hlen; + int err; + + hlen = sizeof(*hdr) + plen; + if (hlen > 255) + return -EINVAL; + + hdr = (struct mtk_wmt_hdr *)&wc; + hdr->dir = 1; + hdr->op = op; + hdr->dlen = cpu_to_le16(plen + 1); + hdr->flag = flag; + memcpy(wc.data, param, plen); + + set_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state); + + err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc); + if (err < 0) { + clear_bit(BTMTKUART_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 BTMTKUART_TX_WAIT_VND_EVT + * state to be cleared. The driver speicfic event receive routine + * will clear that state and with that indicate completion of the + * WMT command. + */ + err = wait_on_bit_timeout(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT, + TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT); + if (err == -EINTR) { + bt_dev_err(hdev, "Execution of wmt command interrupted"); + return err; + } + + if (err) { + bt_dev_err(hdev, "Execution of wmt command timed out"); + return -ETIMEDOUT; + } + + return 0; +} + +static int mtk_setup_fw(struct hci_dev *hdev) +{ + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + int err, dlen; + u8 flag; + + err = request_firmware(&fw, FIRMWARE_MT7622, &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) + return -EINVAL; + + fw_size -= 30; + fw_ptr += 30; + flag = 1; + + 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; + + err = mtk_hci_wmt_sync(hdev, MTK_WMT_PATCH_DWNLD, flag, dlen, + fw_ptr); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)", + err); + break; + } + + fw_size -= dlen; + fw_ptr += dlen; + } + + release_firmware(fw); + + return err; +} + +static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btmtkuart_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; + + err = hci_recv_frame(hdev, skb); + + if (hdr->evt == HCI_EV_VENDOR) { + if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT, + &bdev->tx_state)) { + /* Barrier to sync with other CPUs */ + smp_mb__after_atomic(); + wake_up_bit(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT); + } + } + + 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 = btmtkuart_recv_event }, +}; + +static void btmtkuart_tx_work(struct work_struct *work) +{ + struct btmtkuart_dev *bdev = container_of(work, struct btmtkuart_dev, + tx_work); + struct serdev_device *serdev = bdev->serdev; + struct hci_dev *hdev = bdev->hdev; + + while (1) { + clear_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state); + + while (1) { + struct sk_buff *skb = skb_dequeue(&bdev->txq); + int len; + + if (!skb) + break; + + len = serdev_device_write_buf(serdev, skb->data, + skb->len); + hdev->stat.byte_tx += len; + + skb_pull(skb, len); + if (skb->len > 0) { + skb_queue_head(&bdev->txq, skb); + break; + } + + 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; + } + + kfree_skb(skb); + } + + if (!test_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state)) + break; + } + + clear_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state); +} + +static void btmtkuart_tx_wakeup(struct btmtkuart_dev *bdev) +{ + if (test_and_set_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state)) + set_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state); + + schedule_work(&bdev->tx_work); +} + +static const unsigned char * +mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count, + int *sz_h4) +{ + struct mtk_stp_hdr *shdr; + + /* The cursor is reset when all the data of STP is consumed out */ + if (!bdev->stp_dlen && bdev->stp_cursor >= 6) + bdev->stp_cursor = 0; + + /* Filling pad until all STP info is obtained */ + while (bdev->stp_cursor < 6 && count > 0) { + bdev->stp_pad[bdev->stp_cursor] = *data; + bdev->stp_cursor++; + data++; + count--; + } + + /* Retrieve STP info and have a sanity check */ + if (!bdev->stp_dlen && bdev->stp_cursor >= 6) { + shdr = (struct mtk_stp_hdr *)&bdev->stp_pad[2]; + bdev->stp_dlen = be16_to_cpu(shdr->dlen) & 0x0fff; + + /* Resync STP when unexpected data is being read */ + if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) { + bt_dev_err(bdev->hdev, "stp format unexpect (%d, %d)", + shdr->prefix, bdev->stp_dlen); + bdev->stp_cursor = 2; + bdev->stp_dlen = 0; + } + } + + /* Directly quit when there's no data found for H4 can process */ + if (count <= 0) + return NULL; + + /* Tranlate to how much the size of data H4 can handle so far */ + *sz_h4 = min_t(int, count, bdev->stp_dlen); + + /* Update the remaining size of STP packet */ + bdev->stp_dlen -= *sz_h4; + + /* Data points to STP payload which can be handled by H4 */ + return data; +} + +static int btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count) +{ + struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); + const unsigned char *p_left = data, *p_h4; + int sz_left = count, sz_h4, adv; + int err; + + while (sz_left > 0) { + /* The serial data received from MT7622 BT controller is + * at all time padded around with the STP header and tailer. + * + * A full STP packet is looking like + * ----------------------------------- + * | STP header | H:4 | STP tailer | + * ----------------------------------- + * but it doesn't guarantee to contain a full H:4 packet which + * means that it's possible for multiple STP packets forms a + * full H:4 packet that means extra STP header + length doesn't + * indicate a full H:4 frame, things can fragment. Whose length + * recorded in STP header just shows up the most length the + * H:4 engine can handle currently. + */ + + p_h4 = mtk_stp_split(bdev, p_left, sz_left, &sz_h4); + if (!p_h4) + break; + + adv = p_h4 - p_left; + sz_left -= adv; + p_left += adv; + + bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, p_h4, + sz_h4, mtk_recv_pkts, + sizeof(mtk_recv_pkts)); + if (IS_ERR(bdev->rx_skb)) { + err = PTR_ERR(bdev->rx_skb); + bt_dev_err(bdev->hdev, + "Frame reassembly failed (%d)", err); + bdev->rx_skb = NULL; + return err; + } + + sz_left -= sz_h4; + p_left += sz_h4; + } + + return 0; +} + +static int btmtkuart_receive_buf(struct serdev_device *serdev, const u8 *data, + size_t count) +{ + struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); + int err; + + err = btmtkuart_recv(bdev->hdev, data, count); + if (err < 0) + return err; + + bdev->hdev->stat.byte_rx += count; + + return count; +} + +static void btmtkuart_write_wakeup(struct serdev_device *serdev) +{ + struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); + + btmtkuart_tx_wakeup(bdev); +} + +static const struct serdev_device_ops btmtkuart_client_ops = { + .receive_buf = btmtkuart_receive_buf, + .write_wakeup = btmtkuart_write_wakeup, +}; + +static int btmtkuart_open(struct hci_dev *hdev) +{ + struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); + struct device *dev; + int err; + + err = serdev_device_open(bdev->serdev); + if (err) { + bt_dev_err(hdev, "Unable to open UART device %s", + dev_name(&bdev->serdev->dev)); + goto err_open; + } + + bdev->stp_cursor = 2; + bdev->stp_dlen = 0; + + dev = &bdev->serdev->dev; + + /* Enable the power domain and clock the device requires */ + pm_runtime_enable(dev); + err = pm_runtime_get_sync(dev); + if (err < 0) { + pm_runtime_put_noidle(dev); + goto err_disable_rpm; + } + + err = clk_prepare_enable(bdev->clk); + if (err < 0) + goto err_put_rpm; + + return 0; + +err_put_rpm: + pm_runtime_put_sync(dev); +err_disable_rpm: + pm_runtime_disable(dev); +err_open: + return err; +} + +static int btmtkuart_close(struct hci_dev *hdev) +{ + struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); + struct device *dev = &bdev->serdev->dev; + + /* Shutdown the clock and power domain the device requires */ + clk_disable_unprepare(bdev->clk); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + serdev_device_close(bdev->serdev); + + return 0; +} + +static int btmtkuart_flush(struct hci_dev *hdev) +{ + struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); + + /* Flush any pending characters */ + serdev_device_write_flush(bdev->serdev); + skb_queue_purge(&bdev->txq); + + cancel_work_sync(&bdev->tx_work); + + kfree_skb(bdev->rx_skb); + bdev->rx_skb = NULL; + + bdev->stp_cursor = 2; + bdev->stp_dlen = 0; + + return 0; +} + +static int btmtkuart_setup(struct hci_dev *hdev) +{ + u8 param = 0x1; + int err = 0; + + /* Setup a firmware which the device definitely requires */ + err = mtk_setup_fw(hdev); + if (err < 0) + return err; + + /* Activate function the firmware providing to */ + err = mtk_hci_wmt_sync(hdev, MTK_WMT_RST, 0x4, 0, 0); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt rst (%d)", err); + return err; + } + + /* Enable Bluetooth protocol */ + err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param), + ¶m); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; + } + + return 0; +} + +static int btmtkuart_shutdown(struct hci_dev *hdev) +{ + u8 param = 0x0; + int err; + + /* Disable the device */ + err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param), + ¶m); + if (err < 0) { + bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err); + return err; + } + + return 0; +} + +static int btmtkuart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btmtkuart_dev *bdev = hci_get_drvdata(hdev); + struct mtk_stp_hdr *shdr; + int err, dlen, type = 0; + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); + + /* Make sure that there is enough rooms for STP header and trailer */ + if (unlikely(skb_headroom(skb) < sizeof(*shdr)) || + (skb_tailroom(skb) < MTK_STP_TLR_SIZE)) { + err = pskb_expand_head(skb, sizeof(*shdr), MTK_STP_TLR_SIZE, + GFP_ATOMIC); + if (err < 0) + return err; + } + + /* Add the STP header */ + dlen = skb->len; + shdr = skb_push(skb, sizeof(*shdr)); + shdr->prefix = 0x80; + shdr->dlen = cpu_to_be16((dlen & 0x0fff) | (type << 12)); + shdr->cs = 0; /* MT7622 doesn't care about checksum value */ + + /* Add the STP trailer */ + skb_put_zero(skb, MTK_STP_TLR_SIZE); + + skb_queue_tail(&bdev->txq, skb); + + btmtkuart_tx_wakeup(bdev); + return 0; +} + +static int btmtkuart_probe(struct serdev_device *serdev) +{ + struct btmtkuart_dev *bdev; + struct hci_dev *hdev; + + bdev = devm_kzalloc(&serdev->dev, sizeof(*bdev), GFP_KERNEL); + if (!bdev) + return -ENOMEM; + + bdev->clk = devm_clk_get(&serdev->dev, "ref"); + if (IS_ERR(bdev->clk)) + return PTR_ERR(bdev->clk); + + bdev->serdev = serdev; + serdev_device_set_drvdata(serdev, bdev); + + serdev_device_set_client_ops(serdev, &btmtkuart_client_ops); + + INIT_WORK(&bdev->tx_work, btmtkuart_tx_work); + skb_queue_head_init(&bdev->txq); + + /* Initialize and register HCI device */ + hdev = hci_alloc_dev(); + if (!hdev) { + dev_err(&serdev->dev, "Can't allocate HCI device\n"); + return -ENOMEM; + } + + bdev->hdev = hdev; + + hdev->bus = HCI_UART; + hci_set_drvdata(hdev, bdev); + + hdev->open = btmtkuart_open; + hdev->close = btmtkuart_close; + hdev->flush = btmtkuart_flush; + hdev->setup = btmtkuart_setup; + hdev->shutdown = btmtkuart_shutdown; + hdev->send = btmtkuart_send_frame; + SET_HCIDEV_DEV(hdev, &serdev->dev); + + hdev->manufacturer = 70; + set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); + + if (hci_register_dev(hdev) < 0) { + dev_err(&serdev->dev, "Can't register HCI device\n"); + hci_free_dev(hdev); + return -ENODEV; + } + + return 0; +} + +static void btmtkuart_remove(struct serdev_device *serdev) +{ + struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev); + struct hci_dev *hdev = bdev->hdev; + + hci_unregister_dev(hdev); + hci_free_dev(hdev); +} + +#ifdef CONFIG_OF +static const struct of_device_id mtk_of_match_table[] = { + { .compatible = "mediatek,mt7622-bluetooth"}, + { } +}; +MODULE_DEVICE_TABLE(of, mtk_of_match_table); +#endif + +static struct serdev_device_driver btmtkuart_driver = { + .probe = btmtkuart_probe, + .remove = btmtkuart_remove, + .driver = { + .name = "btmtkuart", + .of_match_table = of_match_ptr(mtk_of_match_table), + }, +}; + +module_serdev_device_driver(btmtkuart_driver); + +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_DESCRIPTION("MediaTek Bluetooth Serial driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_MT7622); diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 488f5e7521dd..ec9e03a6b778 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -39,7 +39,7 @@ int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version) cmd = EDL_PATCH_VER_REQ_CMD; skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, - &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); + &cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "Reading QCA version information failed (%d)", @@ -229,7 +229,7 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size, cmd); skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd, - HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); + HCI_EV_VENDOR, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err); @@ -318,7 +318,7 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) cmd[2] = sizeof(bdaddr_t); /* size */ memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t)); skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd, - HCI_VENDOR_PKT, HCI_INIT_TIMEOUT); + HCI_EV_VENDOR, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); bt_dev_err(hdev, "QCA Change address command failed (%d)", err); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 63c0dcbc4914..8eede1197cd2 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -841,6 +841,7 @@ static void h5_serdev_remove(struct serdev_device *serdev) hci_uart_unregister_device(&h5->serdev_hu); } +#ifdef CONFIG_BT_HCIUART_RTL static int h5_btrtl_setup(struct h5 *h5) { struct btrtl_device_info *btrtl_dev; @@ -922,10 +923,13 @@ static struct h5_vnd rtl_vnd = { .close = h5_btrtl_close, .acpi_gpio_map = acpi_btrtl_gpios, }; +#endif #ifdef CONFIG_ACPI static const struct acpi_device_id h5_acpi_match[] = { +#ifdef CONFIG_BT_HCIUART_RTL { "OBDA8723", (kernel_ulong_t)&rtl_vnd }, +#endif { }, }; MODULE_DEVICE_TABLE(acpi, h5_acpi_match); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f008097552e..cdd9f1fe7cfa 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -269,6 +269,7 @@ enum { HCI_VENDOR_DIAG, HCI_FORCE_BREDR_SMP, HCI_FORCE_STATIC_ADDR, + HCI_LL_RPA_RESOLUTION, __HCI_NUM_FLAGS, }; @@ -1524,6 +1525,8 @@ struct hci_rp_le_read_resolv_list_size { __u8 size; } __packed; +#define HCI_OP_LE_SET_ADDR_RESOLV_ENABLE 0x202d + #define HCI_OP_LE_READ_MAX_DATA_LEN 0x202f struct hci_rp_le_read_max_data_len { __u8 status; @@ -2176,6 +2179,8 @@ struct hci_evt_le_ext_adv_set_term { __u8 num_evts; } __packed; +#define HCI_EV_VENDOR 0xff + /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 754714c8d752..f12555f23a49 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1132,7 +1132,6 @@ static void hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_cp_le_set_ext_adv_enable *cp; - struct hci_cp_ext_adv_set *adv_set; __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -1144,8 +1143,6 @@ static void hci_cc_le_set_ext_adv_enable(struct hci_dev *hdev, if (!cp) return; - adv_set = (void *) cp->data; - hci_dev_lock(hdev); if (cp->enable) { @@ -1483,6 +1480,30 @@ static void hci_cc_le_read_resolv_list_size(struct hci_dev *hdev, hdev->le_resolv_list_size = rp->size; } +static void hci_cc_le_set_addr_resolution_enable(struct hci_dev *hdev, + struct sk_buff *skb) +{ + __u8 *sent, status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADDR_RESOLV_ENABLE); + if (!sent) + return; + + hci_dev_lock(hdev); + + if (*sent) + hci_dev_set_flag(hdev, HCI_LL_RPA_RESOLUTION); + else + hci_dev_clear_flag(hdev, HCI_LL_RPA_RESOLUTION); + + hci_dev_unlock(hdev); +} + static void hci_cc_le_read_max_data_len(struct hci_dev *hdev, struct sk_buff *skb) { @@ -3266,6 +3287,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, hci_cc_le_read_resolv_list_size(hdev, skb); break; + case HCI_OP_LE_SET_ADDR_RESOLV_ENABLE: + hci_cc_le_set_addr_resolution_enable(hdev, skb); + break; + case HCI_OP_LE_READ_MAX_DATA_LEN: hci_cc_le_read_max_data_len(hdev, skb); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 231602f7cb66..3bdc8f3ca259 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3356,7 +3356,6 @@ int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip) static void set_default_phy_complete(struct hci_dev *hdev, u8 status, u16 opcode, struct sk_buff *skb) { - struct mgmt_cp_set_phy_confguration *cp; struct mgmt_pending_cmd *cmd; BT_DBG("status 0x%02x", status); @@ -3367,8 +3366,6 @@ static void set_default_phy_complete(struct hci_dev *hdev, u8 status, if (!cmd) goto unlock; - cp = cmd->param; - if (status) { mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_PHY_CONFIGURATION, |