diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-05 19:11:24 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-05 19:11:24 +0200 |
commit | 687ee0ad4e897e29f4b41f7a20c866d74c5e0660 (patch) | |
tree | b31a2af35c24a54823674cdd126993b80daeac67 /drivers/bluetooth | |
parent | mm: filemap: fix mapping->nrpages double accounting in fuse (diff) | |
parent | Merge branch 'mlxsw-fixes' (diff) | |
download | linux-687ee0ad4e897e29f4b41f7a20c866d74c5e0660.tar.xz linux-687ee0ad4e897e29f4b41f7a20c866d74c5e0660.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
1) BBR TCP congestion control, from Neal Cardwell, Yuchung Cheng and
co. at Google. https://lwn.net/Articles/701165/
2) Do TCP Small Queues for retransmits, from Eric Dumazet.
3) Support collect_md mode for all IPV4 and IPV6 tunnels, from Alexei
Starovoitov.
4) Allow cls_flower to classify packets in ip tunnels, from Amir Vadai.
5) Support DSA tagging in older mv88e6xxx switches, from Andrew Lunn.
6) Support GMAC protocol in iwlwifi mwm, from Ayala Beker.
7) Support ndo_poll_controller in mlx5, from Calvin Owens.
8) Move VRF processing to an output hook and allow l3mdev to be
loopback, from David Ahern.
9) Support SOCK_DESTROY for UDP sockets. Also from David Ahern.
10) Congestion control in RXRPC, from David Howells.
11) Support geneve RX offload in ixgbe, from Emil Tantilov.
12) When hitting pressure for new incoming TCP data SKBs, perform a
partial rathern than a full purge of the OFO queue (which could be
huge). From Eric Dumazet.
13) Convert XFRM state and policy lookups to RCU, from Florian Westphal.
14) Support RX network flow classification to igb, from Gangfeng Huang.
15) Hardware offloading of eBPF in nfp driver, from Jakub Kicinski.
16) New skbmod packet action, from Jamal Hadi Salim.
17) Remove some inefficiencies in snmp proc output, from Jia He.
18) Add FIB notifications to properly propagate route changes to
hardware which is doing forwarding offloading. From Jiri Pirko.
19) New dsa driver for qca8xxx chips, from John Crispin.
20) Implement RFC7559 ipv6 router solicitation backoff, from Maciej
Żenczykowski.
21) Add L3 mode to ipvlan, from Mahesh Bandewar.
22) Support 802.1ad in mlx4, from Moshe Shemesh.
23) Support hardware LRO in mediatek driver, from Nelson Chang.
24) Add TC offloading to mlx5, from Or Gerlitz.
25) Convert various drivers to ethtool ksettings interfaces, from
Philippe Reynes.
26) TX max rate limiting for cxgb4, from Rahul Lakkireddy.
27) NAPI support for ath10k, from Rajkumar Manoharan.
28) Support XDP in mlx5, from Rana Shahout and Saeed Mahameed.
29) UDP replicast support in TIPC, from Richard Alpe.
30) Per-queue statistics for qed driver, from Sudarsana Reddy Kalluru.
31) Support BQL in thunderx driver, from Sunil Goutham.
32) TSO support in alx driver, from Tobias Regnery.
33) Add stream parser engine and use it in kcm.
34) Support async DHCP replies in ipconfig module, from Uwe
Kleine-König.
35) DSA port fast aging for mv88e6xxx driver, from Vivien Didelot.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1715 commits)
mlxsw: switchx2: Fix misuse of hard_header_len
mlxsw: spectrum: Fix misuse of hard_header_len
net/faraday: Stop NCSI device on shutdown
net/ncsi: Introduce ncsi_stop_dev()
net/ncsi: Rework the channel monitoring
net/ncsi: Allow to extend NCSI request properties
net/ncsi: Rework request index allocation
net/ncsi: Don't probe on the reserved channel ID (0x1f)
net/ncsi: Introduce NCSI_RESERVED_CHANNEL
net/ncsi: Avoid unused-value build warning from ia64-linux-gcc
net: Add netdev all_adj_list refcnt propagation to fix panic
net: phy: Add Edge-rate driver for Microsemi PHYs.
vmxnet3: Wake queue from reset work
i40e: avoid NULL pointer dereference and recursive errors on early PCI error
qed: Add RoCE ll2 & GSI support
qed: Add support for memory registeration verbs
qed: Add support for QP verbs
qed: PD,PKEY and CQ verb support
qed: Add support for RoCE hw init
qede: Add qedr framework
...
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/Kconfig | 23 | ||||
-rw-r--r-- | drivers/bluetooth/Makefile | 2 | ||||
-rw-r--r-- | drivers/bluetooth/btqca.c | 8 | ||||
-rw-r--r-- | drivers/bluetooth/btqcomsmd.c | 182 | ||||
-rw-r--r-- | drivers/bluetooth/btrtl.c | 107 | ||||
-rw-r--r-- | drivers/bluetooth/btusb.c | 14 | ||||
-rw-r--r-- | drivers/bluetooth/btwilink.c | 4 | ||||
-rw-r--r-- | drivers/bluetooth/hci_bcm.c | 2 | ||||
-rw-r--r-- | drivers/bluetooth/hci_bcsp.c | 128 | ||||
-rw-r--r-- | drivers/bluetooth/hci_intel.c | 6 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ldisc.c | 34 | ||||
-rw-r--r-- | drivers/bluetooth/hci_mrvl.c | 387 | ||||
-rw-r--r-- | drivers/bluetooth/hci_qca.c | 2 | ||||
-rw-r--r-- | drivers/bluetooth/hci_uart.h | 9 |
14 files changed, 814 insertions, 94 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index cf50fd2e96df..3cc9bff9d99d 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX Say Y here to compile support for Intel AG6XX protocol. +config BT_HCIUART_MRVL + bool "Marvell protocol support" + depends on BT_HCIUART + select BT_HCIUART_H4 + help + Marvell is serial protocol for communication between Bluetooth + device and host. This protocol is required for most Marvell Bluetooth + devices with UART interface. + + Say Y here to compile support for HCI MRVL protocol. + config BT_HCIBCM203X tristate "HCI BCM203x USB driver" depends on USB @@ -331,4 +342,16 @@ 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_QCOMSMD + tristate "Qualcomm SMD based HCI support" + depends on QCOM_SMD && QCOM_WCNSS_CTRL + select BT_QCA + help + Qualcomm SMD based HCI driver. + This driver is used to bridge HCI data onto the shared memory + channels to the WCNSS core. + + Say Y here to compile support for HCI over Qualcomm SMD into the + kernel or say M to compile as a module. + endmenu diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 9c18939fc5c9..b1fc29a697b7 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_QCOMSMD) += btqcomsmd.o obj-$(CONFIG_BT_BCM) += btbcm.o obj-$(CONFIG_BT_RTL) += btrtl.o obj-$(CONFIG_BT_QCA) += btqca.o @@ -37,6 +38,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL) += hci_intel.o hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o +hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o hci_uart-objs := $(hci_uart-y) ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 4a6208168850..28afd5d585f9 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -55,8 +55,8 @@ static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version) } edl = (struct edl_event_hdr *)(skb->data); - if (!edl || !edl->data) { - BT_ERR("%s: TLV with no header or no data", hdev->name); + if (!edl) { + BT_ERR("%s: TLV with no header", hdev->name); err = -EILSEQ; goto out; } @@ -224,8 +224,8 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size, } edl = (struct edl_event_hdr *)(skb->data); - if (!edl || !edl->data) { - BT_ERR("%s: TLV with no header or no data", hdev->name); + if (!edl) { + BT_ERR("%s: TLV with no header", hdev->name); err = -EILSEQ; goto out; } diff --git a/drivers/bluetooth/btqcomsmd.c b/drivers/bluetooth/btqcomsmd.c new file mode 100644 index 000000000000..08c2c93887c1 --- /dev/null +++ b/drivers/bluetooth/btqcomsmd.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2016, Linaro Ltd. + * Copyright (c) 2015, Sony Mobile Communications Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/soc/qcom/smd.h> +#include <linux/soc/qcom/wcnss_ctrl.h> +#include <linux/platform_device.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "btqca.h" + +struct btqcomsmd { + struct hci_dev *hdev; + + struct qcom_smd_channel *acl_channel; + struct qcom_smd_channel *cmd_channel; +}; + +static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type, + const void *data, size_t count) +{ + struct sk_buff *skb; + + /* Use GFP_ATOMIC as we're in IRQ context */ + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) { + hdev->stat.err_rx++; + return -ENOMEM; + } + + hci_skb_pkt_type(skb) = type; + memcpy(skb_put(skb, count), data, count); + + return hci_recv_frame(hdev, skb); +} + +static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel, + const void *data, size_t count) +{ + struct btqcomsmd *btq = qcom_smd_get_drvdata(channel); + + btq->hdev->stat.byte_rx += count; + return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count); +} + +static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel, + const void *data, size_t count) +{ + struct btqcomsmd *btq = qcom_smd_get_drvdata(channel); + + return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count); +} + +static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btqcomsmd *btq = hci_get_drvdata(hdev); + int ret; + + switch (hci_skb_pkt_type(skb)) { + case HCI_ACLDATA_PKT: + ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len); + hdev->stat.acl_tx++; + hdev->stat.byte_tx += skb->len; + break; + case HCI_COMMAND_PKT: + ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len); + hdev->stat.cmd_tx++; + break; + default: + ret = -EILSEQ; + break; + } + + kfree_skb(skb); + + return ret; +} + +static int btqcomsmd_open(struct hci_dev *hdev) +{ + return 0; +} + +static int btqcomsmd_close(struct hci_dev *hdev) +{ + return 0; +} + +static int btqcomsmd_probe(struct platform_device *pdev) +{ + struct btqcomsmd *btq; + struct hci_dev *hdev; + void *wcnss; + int ret; + + btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL); + if (!btq) + return -ENOMEM; + + wcnss = dev_get_drvdata(pdev->dev.parent); + + btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL", + btqcomsmd_acl_callback); + if (IS_ERR(btq->acl_channel)) + return PTR_ERR(btq->acl_channel); + + btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD", + btqcomsmd_cmd_callback); + if (IS_ERR(btq->cmd_channel)) + return PTR_ERR(btq->cmd_channel); + + qcom_smd_set_drvdata(btq->acl_channel, btq); + qcom_smd_set_drvdata(btq->cmd_channel, btq); + + hdev = hci_alloc_dev(); + if (!hdev) + return -ENOMEM; + + hci_set_drvdata(hdev, btq); + btq->hdev = hdev; + SET_HCIDEV_DEV(hdev, &pdev->dev); + + hdev->bus = HCI_SMD; + hdev->open = btqcomsmd_open; + hdev->close = btqcomsmd_close; + hdev->send = btqcomsmd_send; + hdev->set_bdaddr = qca_set_bdaddr_rome; + + ret = hci_register_dev(hdev); + if (ret < 0) { + hci_free_dev(hdev); + return ret; + } + + platform_set_drvdata(pdev, btq); + + return 0; +} + +static int btqcomsmd_remove(struct platform_device *pdev) +{ + struct btqcomsmd *btq = platform_get_drvdata(pdev); + + hci_unregister_dev(btq->hdev); + hci_free_dev(btq->hdev); + + return 0; +} + +static const struct of_device_id btqcomsmd_of_match[] = { + { .compatible = "qcom,wcnss-bt", }, + { }, +}; + +static struct platform_driver btqcomsmd_driver = { + .probe = btqcomsmd_probe, + .remove = btqcomsmd_remove, + .driver = { + .name = "btqcomsmd", + .of_match_table = btqcomsmd_of_match, + }, +}; + +module_platform_driver(btqcomsmd_driver); + +MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>"); +MODULE_DESCRIPTION("Qualcomm SMD HCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 84288938f7f2..fc9b25703c67 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -33,6 +33,7 @@ #define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8821A 0x8821 #define RTL_ROM_LMP_8761A 0x8761 +#define RTL_ROM_LMP_8822B 0x8822 static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) { @@ -78,11 +79,15 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver, const unsigned char *patch_length_base, *patch_offset_base; u32 patch_offset = 0; u16 patch_length, num_patches; - const u16 project_id_to_lmp_subver[] = { - RTL_ROM_LMP_8723A, - RTL_ROM_LMP_8723B, - RTL_ROM_LMP_8821A, - RTL_ROM_LMP_8761A + static const struct { + __u16 lmp_subver; + __u8 id; + } project_id_to_lmp_subver[] = { + { RTL_ROM_LMP_8723A, 0 }, + { RTL_ROM_LMP_8723B, 1 }, + { RTL_ROM_LMP_8821A, 2 }, + { RTL_ROM_LMP_8761A, 3 }, + { RTL_ROM_LMP_8822B, 8 }, }; ret = rtl_read_rom_version(hdev, &rom_version); @@ -134,14 +139,20 @@ static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver, return -EINVAL; } - if (project_id >= ARRAY_SIZE(project_id_to_lmp_subver)) { + /* Find project_id in table */ + for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) { + if (project_id == project_id_to_lmp_subver[i].id) + break; + } + + if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) { BT_ERR("%s: unknown project id %d", hdev->name, project_id); return -EINVAL; } - if (lmp_subver != project_id_to_lmp_subver[project_id]) { + if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) { BT_ERR("%s: firmware is for %x but this is a %x", hdev->name, - project_id_to_lmp_subver[project_id], lmp_subver); + project_id_to_lmp_subver[i].lmp_subver, lmp_subver); return -EINVAL; } @@ -257,6 +268,26 @@ out: return ret; } +static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff) +{ + const struct firmware *fw; + int ret; + + BT_INFO("%s: rtl: loading %s", hdev->name, name); + ret = request_firmware(&fw, name, &hdev->dev); + if (ret < 0) { + BT_ERR("%s: Failed to load %s", hdev->name, name); + return ret; + } + + ret = fw->size; + *buff = kmemdup(fw->data, ret, GFP_KERNEL); + + release_firmware(fw); + + return ret; +} + static int btrtl_setup_rtl8723a(struct hci_dev *hdev) { const struct firmware *fw; @@ -296,25 +327,74 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, unsigned char *fw_data = NULL; const struct firmware *fw; int ret; + int cfg_sz; + u8 *cfg_buff = NULL; + u8 *tbuff; + char *cfg_name = NULL; + + switch (lmp_subver) { + case RTL_ROM_LMP_8723B: + cfg_name = "rtl_bt/rtl8723b_config.bin"; + break; + case RTL_ROM_LMP_8821A: + cfg_name = "rtl_bt/rtl8821a_config.bin"; + break; + case RTL_ROM_LMP_8761A: + cfg_name = "rtl_bt/rtl8761a_config.bin"; + break; + case RTL_ROM_LMP_8822B: + cfg_name = "rtl_bt/rtl8822b_config.bin"; + break; + default: + BT_ERR("%s: rtl: no config according to lmp_subver %04x", + hdev->name, lmp_subver); + break; + } + + if (cfg_name) { + cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff); + if (cfg_sz < 0) + cfg_sz = 0; + } else + cfg_sz = 0; BT_INFO("%s: rtl: loading %s", hdev->name, fw_name); ret = request_firmware(&fw, fw_name, &hdev->dev); if (ret < 0) { BT_ERR("%s: Failed to load %s", hdev->name, fw_name); - return ret; + goto err_req_fw; } ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data); if (ret < 0) goto out; + if (cfg_sz) { + tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL); + if (!tbuff) { + ret = -ENOMEM; + goto out; + } + + memcpy(tbuff, fw_data, ret); + kfree(fw_data); + + memcpy(tbuff + ret, cfg_buff, cfg_sz); + ret += cfg_sz; + + fw_data = tbuff; + } + + BT_INFO("cfg_sz %d, total size %d", cfg_sz, ret); + ret = rtl_download_firmware(hdev, fw_data, ret); - kfree(fw_data); - if (ret < 0) - goto out; out: release_firmware(fw); + kfree(fw_data); +err_req_fw: + if (cfg_sz) + kfree(cfg_buff); return ret; } @@ -377,6 +457,9 @@ int btrtl_setup_realtek(struct hci_dev *hdev) case RTL_ROM_LMP_8761A: return btrtl_setup_rtl8723b(hdev, lmp_subver, "rtl_bt/rtl8761a_fw.bin"); + case RTL_ROM_LMP_8822B: + return btrtl_setup_rtl8723b(hdev, lmp_subver, + "rtl_bt/rtl8822b_fw.bin"); default: BT_INFO("rtl: assuming no firmware upload needed."); return 0; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 811f9b97e360..6bd63b84abd0 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -62,6 +62,7 @@ static struct usb_driver btusb_driver; #define BTUSB_REALTEK 0x20000 #define BTUSB_BCM2045 0x40000 #define BTUSB_IFNUM_2 0x80000 +#define BTUSB_CW6622 0x100000 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -248,9 +249,11 @@ static const struct usb_device_id blacklist_table[] = { /* QCA ROME chipset */ { USB_DEVICE(0x0cf3, 0xe007), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x0cf3, 0xe009), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe300), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0cf3, 0xe360), .driver_info = BTUSB_QCA_ROME }, { USB_DEVICE(0x0489, 0xe092), .driver_info = BTUSB_QCA_ROME }, + { USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME }, /* Broadcom BCM2035 */ { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 }, @@ -290,7 +293,8 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC }, /* CONWISE Technology based adapters with buggy SCO support */ - { USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC }, + { USB_DEVICE(0x0e5e, 0x6622), + .driver_info = BTUSB_BROKEN_ISOC | BTUSB_CW6622}, /* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */ { USB_DEVICE(0x1310, 0x0001), .driver_info = BTUSB_SWAVE }, @@ -2221,9 +2225,8 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) err = wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING, TASK_INTERRUPTIBLE, msecs_to_jiffies(5000)); - if (err == 1) { + if (err == -EINTR) { BT_ERR("%s: Firmware loading interrupted", hdev->name); - err = -EINTR; goto done; } @@ -2275,7 +2278,7 @@ done: TASK_INTERRUPTIBLE, msecs_to_jiffies(1000)); - if (err == 1) { + if (err == -EINTR) { BT_ERR("%s: Device boot interrupted", hdev->name); return -EINTR; } @@ -2845,6 +2848,9 @@ static int btusb_probe(struct usb_interface *intf, hdev->send = btusb_send_frame; hdev->notify = btusb_notify; + if (id->driver_info & BTUSB_CW6622) + set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); + if (id->driver_info & BTUSB_BCM2045) set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks); diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 485281b3f167..ef51c9c864c5 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -245,6 +245,7 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct ti_st *hst; long len; + int pkt_type; hst = hci_get_drvdata(hdev); @@ -258,6 +259,7 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) * Freeing skb memory is taken care in shared transport layer, * so don't free skb memory here. */ + pkt_type = hci_skb_pkt_type(skb); len = hst->st_write(skb); if (len < 0) { kfree_skb(skb); @@ -268,7 +270,7 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* ST accepted our skb. So, Go ahead and do rest */ hdev->stat.byte_tx += len; - ti_st_tx_complete(hst, hci_skb_pkt_type(skb)); + ti_st_tx_complete(hst, pkt_type); return 0; } diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 1c97eda8bae3..5ccb90ef0146 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -798,7 +798,7 @@ static int bcm_remove(struct platform_device *pdev) static const struct hci_uart_proto bcm_proto = { .id = HCI_UART_BCM, - .name = "BCM", + .name = "Broadcom", .manufacturer = 15, .init_speed = 115200, .oper_speed = 4000000, diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index d7d23ceba4d1..a2c921faaa12 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -90,7 +90,8 @@ struct bcsp_struct { /* ---- BCSP CRC calculation ---- */ /* Table for calculating CRC for polynomial 0x1021, LSB processed first, -initial value 0xffff, bits shifted in reverse order. */ + * initial value 0xffff, bits shifted in reverse order. + */ static const u16 crc_table[] = { 0x0000, 0x1081, 0x2102, 0x3183, @@ -174,7 +175,7 @@ static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb) } static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, - int len, int pkt_type) + int len, int pkt_type) { struct sk_buff *nskb; u8 hdr[4], chan; @@ -213,6 +214,7 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, /* Vendor specific commands */ if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) { u8 desc = *(data + HCI_COMMAND_HDR_SIZE); + if ((desc & 0xf0) == 0xc0) { data += HCI_COMMAND_HDR_SIZE + 1; len -= HCI_COMMAND_HDR_SIZE + 1; @@ -271,8 +273,8 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, /* Put CRC */ if (bcsp->use_crc) { bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc); - bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff)); - bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff)); + bcsp_slip_one_byte(nskb, (u8)((bcsp_txmsg_crc >> 8) & 0x00ff)); + bcsp_slip_one_byte(nskb, (u8)(bcsp_txmsg_crc & 0x00ff)); } bcsp_slip_msgdelim(nskb); @@ -287,7 +289,8 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu) struct sk_buff *skb; /* First of all, check for unreliable messages in the queue, - since they have priority */ + * since they have priority + */ skb = skb_dequeue(&bcsp->unrel); if (skb != NULL) { @@ -414,7 +417,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu) /* spot "conf" pkts and reply with a "conf rsp" pkt */ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && - !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) { + !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) { struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC); BT_DBG("Found a LE conf pkt"); @@ -428,7 +431,7 @@ static void bcsp_handle_le_pkt(struct hci_uart *hu) } /* Spot "sync" pkts. If we find one...disaster! */ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 && - !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) { + !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) { BT_ERR("Found a LE sync pkt, card has reset"); } } @@ -446,7 +449,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char default: memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1); if ((bcsp->rx_skb->data[0] & 0x40) != 0 && - bcsp->rx_state != BCSP_W4_CRC) + bcsp->rx_state != BCSP_W4_CRC) bcsp_crc_update(&bcsp->message_crc, byte); bcsp->rx_count--; } @@ -457,7 +460,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char case 0xdc: memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1); if ((bcsp->rx_skb->data[0] & 0x40) != 0 && - bcsp->rx_state != BCSP_W4_CRC) + bcsp->rx_state != BCSP_W4_CRC) bcsp_crc_update(&bcsp->message_crc, 0xc0); bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; bcsp->rx_count--; @@ -466,7 +469,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char case 0xdd: memcpy(skb_put(bcsp->rx_skb, 1), &db, 1); if ((bcsp->rx_skb->data[0] & 0x40) != 0 && - bcsp->rx_state != BCSP_W4_CRC) + bcsp->rx_state != BCSP_W4_CRC) bcsp_crc_update(&bcsp->message_crc, 0xdb); bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC; bcsp->rx_count--; @@ -485,13 +488,28 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char static void bcsp_complete_rx_pkt(struct hci_uart *hu) { struct bcsp_struct *bcsp = hu->priv; - int pass_up; + int pass_up = 0; if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack); - bcsp->rxseq_txack++; - bcsp->rxseq_txack %= 0x8; - bcsp->txack_req = 1; + + /* check the rx sequence number is as expected */ + if ((bcsp->rx_skb->data[0] & 0x07) == bcsp->rxseq_txack) { + bcsp->rxseq_txack++; + bcsp->rxseq_txack %= 0x8; + } else { + /* handle re-transmitted packet or + * when packet was missed + */ + BT_ERR("Out-of-order packet arrived, got %u expected %u", + bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack); + + /* do not process out-of-order packet payload */ + pass_up = 2; + } + + /* send current txack value to all received reliable packets */ + bcsp->txack_req = 1; /* If needed, transmit an ack pkt */ hci_uart_tx_wakeup(hu); @@ -500,26 +518,33 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu) bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07; BT_DBG("Request for pkt %u from card", bcsp->rxack); + /* handle received ACK indications, + * including those from out-of-order packets + */ bcsp_pkt_cull(bcsp); - if ((bcsp->rx_skb->data[1] & 0x0f) == 6 && - bcsp->rx_skb->data[0] & 0x80) { - hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT; - pass_up = 1; - } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 && - bcsp->rx_skb->data[0] & 0x80) { - hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT; - pass_up = 1; - } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) { - hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT; - pass_up = 1; - } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 && - !(bcsp->rx_skb->data[0] & 0x80)) { - bcsp_handle_le_pkt(hu); - pass_up = 0; - } else - pass_up = 0; - - if (!pass_up) { + + if (pass_up != 2) { + if ((bcsp->rx_skb->data[1] & 0x0f) == 6 && + (bcsp->rx_skb->data[0] & 0x80)) { + hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT; + pass_up = 1; + } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 && + (bcsp->rx_skb->data[0] & 0x80)) { + hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT; + pass_up = 1; + } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) { + hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT; + pass_up = 1; + } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 && + !(bcsp->rx_skb->data[0] & 0x80)) { + bcsp_handle_le_pkt(hu); + pass_up = 0; + } else { + pass_up = 0; + } + } + + if (pass_up == 0) { struct hci_event_hdr hdr; u8 desc = (bcsp->rx_skb->data[1] & 0x0f); @@ -537,18 +562,23 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu) hci_recv_frame(hu->hdev, bcsp->rx_skb); } else { BT_ERR("Packet for unknown channel (%u %s)", - bcsp->rx_skb->data[1] & 0x0f, - bcsp->rx_skb->data[0] & 0x80 ? - "reliable" : "unreliable"); + bcsp->rx_skb->data[1] & 0x0f, + bcsp->rx_skb->data[0] & 0x80 ? + "reliable" : "unreliable"); kfree_skb(bcsp->rx_skb); } } else kfree_skb(bcsp->rx_skb); - } else { + } else if (pass_up == 1) { /* Pull out BCSP hdr */ skb_pull(bcsp->rx_skb, 4); hci_recv_frame(hu->hdev, bcsp->rx_skb); + } else { + /* ignore packet payload of already ACKed re-transmitted + * packets or when a packet was missed in the BCSP window + */ + kfree_skb(bcsp->rx_skb); } bcsp->rx_state = BCSP_W4_PKT_DELIMITER; @@ -567,7 +597,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count) const unsigned char *ptr; BT_DBG("hu %p count %d rx_state %d rx_count %ld", - hu, count, bcsp->rx_state, bcsp->rx_count); + hu, count, bcsp->rx_state, bcsp->rx_count); ptr = data; while (count) { @@ -586,24 +616,14 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count) switch (bcsp->rx_state) { case BCSP_W4_BCSP_HDR: - if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] + - bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) { + if ((0xff & (u8)~(bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] + + bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) { BT_ERR("Error in BCSP hdr checksum"); kfree_skb(bcsp->rx_skb); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; bcsp->rx_count = 0; continue; } - if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */ - && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) { - BT_ERR("Out-of-order packet arrived, got %u expected %u", - bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack); - - kfree_skb(bcsp->rx_skb); - bcsp->rx_state = BCSP_W4_PKT_DELIMITER; - bcsp->rx_count = 0; - continue; - } bcsp->rx_state = BCSP_W4_DATA; bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + (bcsp->rx_skb->data[2] << 4); /* May be 0 */ @@ -620,8 +640,8 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count) case BCSP_W4_CRC: if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) { BT_ERR("Checksum failed: computed %04x received %04x", - bitrev16(bcsp->message_crc), - bscp_get_crc(bcsp)); + bitrev16(bcsp->message_crc), + bscp_get_crc(bcsp)); kfree_skb(bcsp->rx_skb); bcsp->rx_state = BCSP_W4_PKT_DELIMITER; @@ -679,7 +699,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count) /* Arrange to retransmit all messages in the relq. */ static void bcsp_timed_event(unsigned long arg) { - struct hci_uart *hu = (struct hci_uart *) arg; + struct hci_uart *hu = (struct hci_uart *)arg; struct bcsp_struct *bcsp = hu->priv; struct sk_buff *skb; unsigned long flags; @@ -715,7 +735,7 @@ static int bcsp_open(struct hci_uart *hu) init_timer(&bcsp->tbcsp); bcsp->tbcsp.function = bcsp_timed_event; - bcsp->tbcsp.data = (u_long) hu; + bcsp->tbcsp.data = (u_long)hu; bcsp->rx_state = BCSP_W4_PKT_DELIMITER; diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index ed0a4201b551..9e271286c5e5 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -128,7 +128,7 @@ static int intel_wait_booting(struct hci_uart *hu) TASK_INTERRUPTIBLE, msecs_to_jiffies(1000)); - if (err == 1) { + if (err == -EINTR) { bt_dev_err(hu->hdev, "Device boot interrupted"); return -EINTR; } @@ -151,7 +151,7 @@ static int intel_wait_lpm_transaction(struct hci_uart *hu) TASK_INTERRUPTIBLE, msecs_to_jiffies(1000)); - if (err == 1) { + if (err == -EINTR) { bt_dev_err(hu->hdev, "LPM transaction interrupted"); return -EINTR; } @@ -813,7 +813,7 @@ static int intel_setup(struct hci_uart *hu) err = wait_on_bit_timeout(&intel->flags, STATE_DOWNLOADING, TASK_INTERRUPTIBLE, msecs_to_jiffies(5000)); - if (err == 1) { + if (err == -EINTR) { bt_dev_err(hdev, "Firmware loading interrupted"); err = -EINTR; goto done; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index dda97398c59a..9497c469efd2 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -697,34 +697,36 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file, case HCIUARTSETPROTO: if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) { err = hci_uart_set_proto(hu, arg); - if (err) { + if (err) clear_bit(HCI_UART_PROTO_SET, &hu->flags); - return err; - } } else - return -EBUSY; + err = -EBUSY; break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return hu->proto->id; - return -EUNATCH; + err = hu->proto->id; + else + err = -EUNATCH; + break; case HCIUARTGETDEVICE: if (test_bit(HCI_UART_REGISTERED, &hu->flags)) - return hu->hdev->id; - return -EUNATCH; + err = hu->hdev->id; + else + err = -EUNATCH; + break; case HCIUARTSETFLAGS: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) - return -EBUSY; - err = hci_uart_set_flags(hu, arg); - if (err) - return err; + err = -EBUSY; + else + err = hci_uart_set_flags(hu, arg); break; case HCIUARTGETFLAGS: - return hu->hdev_flags; + err = hu->hdev_flags; + break; default: err = n_tty_ioctl_helper(tty, file, cmd, arg); @@ -810,6 +812,9 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_AG6XX ag6xx_init(); #endif +#ifdef CONFIG_BT_HCIUART_MRVL + mrvl_init(); +#endif return 0; } @@ -845,6 +850,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_AG6XX ag6xx_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_MRVL + mrvl_deinit(); +#endif /* Release tty registration of line discipline */ err = tty_unregister_ldisc(N_HCI); diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c new file mode 100644 index 000000000000..bbc4b39b1dbf --- /dev/null +++ b/drivers/bluetooth/hci_mrvl.c @@ -0,0 +1,387 @@ +/* + * + * Bluetooth HCI UART driver for marvell devices + * + * Copyright (C) 2016 Marvell International Ltd. + * Copyright (C) 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/skbuff.h> +#include <linux/firmware.h> +#include <linux/module.h> +#include <linux/tty.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" + +#define HCI_FW_REQ_PKT 0xA5 +#define HCI_CHIP_VER_PKT 0xAA + +#define MRVL_ACK 0x5A +#define MRVL_NAK 0xBF +#define MRVL_RAW_DATA 0x1F + +enum { + STATE_CHIP_VER_PENDING, + STATE_FW_REQ_PENDING, +}; + +struct mrvl_data { + struct sk_buff *rx_skb; + struct sk_buff_head txq; + struct sk_buff_head rawq; + unsigned long flags; + unsigned int tx_len; + u8 id, rev; +}; + +struct hci_mrvl_pkt { + __le16 lhs; + __le16 rhs; +} __packed; +#define HCI_MRVL_PKT_SIZE 4 + +static int mrvl_open(struct hci_uart *hu) +{ + struct mrvl_data *mrvl; + + BT_DBG("hu %p", hu); + + mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL); + if (!mrvl) + return -ENOMEM; + + skb_queue_head_init(&mrvl->txq); + skb_queue_head_init(&mrvl->rawq); + + set_bit(STATE_CHIP_VER_PENDING, &mrvl->flags); + + hu->priv = mrvl; + return 0; +} + +static int mrvl_close(struct hci_uart *hu) +{ + struct mrvl_data *mrvl = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&mrvl->txq); + skb_queue_purge(&mrvl->rawq); + kfree_skb(mrvl->rx_skb); + kfree(mrvl); + + hu->priv = NULL; + return 0; +} + +static int mrvl_flush(struct hci_uart *hu) +{ + struct mrvl_data *mrvl = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&mrvl->txq); + skb_queue_purge(&mrvl->rawq); + + return 0; +} + +static struct sk_buff *mrvl_dequeue(struct hci_uart *hu) +{ + struct mrvl_data *mrvl = hu->priv; + struct sk_buff *skb; + + skb = skb_dequeue(&mrvl->txq); + if (!skb) { + /* Any raw data ? */ + skb = skb_dequeue(&mrvl->rawq); + } else { + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + } + + return skb; +} + +static int mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + struct mrvl_data *mrvl = hu->priv; + + skb_queue_tail(&mrvl->txq, skb); + return 0; +} + +static void mrvl_send_ack(struct hci_uart *hu, unsigned char type) +{ + struct mrvl_data *mrvl = hu->priv; + struct sk_buff *skb; + + /* No H4 payload, only 1 byte header */ + skb = bt_skb_alloc(0, GFP_ATOMIC); + if (!skb) { + bt_dev_err(hu->hdev, "Unable to alloc ack/nak packet"); + return; + } + hci_skb_pkt_type(skb) = type; + + skb_queue_tail(&mrvl->txq, skb); + hci_uart_tx_wakeup(hu); +} + +static int mrvl_recv_fw_req(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_mrvl_pkt *pkt = (void *)skb->data; + struct hci_uart *hu = hci_get_drvdata(hdev); + struct mrvl_data *mrvl = hu->priv; + int ret = 0; + + if ((pkt->lhs ^ pkt->rhs) != 0xffff) { + bt_dev_err(hdev, "Corrupted mrvl header"); + mrvl_send_ack(hu, MRVL_NAK); + ret = -EINVAL; + goto done; + } + mrvl_send_ack(hu, MRVL_ACK); + + if (!test_bit(STATE_FW_REQ_PENDING, &mrvl->flags)) { + bt_dev_err(hdev, "Received unexpected firmware request"); + ret = -EINVAL; + goto done; + } + + mrvl->tx_len = le16_to_cpu(pkt->lhs); + + clear_bit(STATE_FW_REQ_PENDING, &mrvl->flags); + smp_mb__after_atomic(); + wake_up_bit(&mrvl->flags, STATE_FW_REQ_PENDING); + +done: + kfree_skb(skb); + return ret; +} + +static int mrvl_recv_chip_ver(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_mrvl_pkt *pkt = (void *)skb->data; + struct hci_uart *hu = hci_get_drvdata(hdev); + struct mrvl_data *mrvl = hu->priv; + u16 version = le16_to_cpu(pkt->lhs); + int ret = 0; + + if ((pkt->lhs ^ pkt->rhs) != 0xffff) { + bt_dev_err(hdev, "Corrupted mrvl header"); + mrvl_send_ack(hu, MRVL_NAK); + ret = -EINVAL; + goto done; + } + mrvl_send_ack(hu, MRVL_ACK); + + if (!test_bit(STATE_CHIP_VER_PENDING, &mrvl->flags)) { + bt_dev_err(hdev, "Received unexpected chip version"); + goto done; + } + + mrvl->id = version; + mrvl->rev = version >> 8; + + bt_dev_info(hdev, "Controller id = %x, rev = %x", mrvl->id, mrvl->rev); + + clear_bit(STATE_CHIP_VER_PENDING, &mrvl->flags); + smp_mb__after_atomic(); + wake_up_bit(&mrvl->flags, STATE_CHIP_VER_PENDING); + +done: + kfree_skb(skb); + return ret; +} + +#define HCI_RECV_CHIP_VER \ + .type = HCI_CHIP_VER_PKT, \ + .hlen = HCI_MRVL_PKT_SIZE, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = HCI_MRVL_PKT_SIZE + +#define HCI_RECV_FW_REQ \ + .type = HCI_FW_REQ_PKT, \ + .hlen = HCI_MRVL_PKT_SIZE, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = HCI_MRVL_PKT_SIZE + +static const struct h4_recv_pkt mrvl_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { HCI_RECV_FW_REQ, .recv = mrvl_recv_fw_req }, + { HCI_RECV_CHIP_VER, .recv = mrvl_recv_chip_ver }, +}; + +static int mrvl_recv(struct hci_uart *hu, const void *data, int count) +{ + struct mrvl_data *mrvl = hu->priv; + + if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) + return -EUNATCH; + + mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count, + mrvl_recv_pkts, + ARRAY_SIZE(mrvl_recv_pkts)); + if (IS_ERR(mrvl->rx_skb)) { + int err = PTR_ERR(mrvl->rx_skb); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); + mrvl->rx_skb = NULL; + return err; + } + + return count; +} + +static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) +{ + struct hci_uart *hu = hci_get_drvdata(hdev); + struct mrvl_data *mrvl = hu->priv; + const struct firmware *fw = NULL; + const u8 *fw_ptr, *fw_max; + int err; + + err = request_firmware(&fw, name, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Failed to load firmware file %s", name); + return err; + } + + fw_ptr = fw->data; + fw_max = fw->data + fw->size; + + bt_dev_info(hdev, "Loading %s", name); + + set_bit(STATE_FW_REQ_PENDING, &mrvl->flags); + + while (fw_ptr <= fw_max) { + struct sk_buff *skb; + + /* Controller drives the firmware load by sending firmware + * request packets containing the expected fragment size. + */ + err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING, + TASK_INTERRUPTIBLE, + msecs_to_jiffies(2000)); + if (err == 1) { + bt_dev_err(hdev, "Firmware load interrupted"); + err = -EINTR; + break; + } else if (err) { + bt_dev_err(hdev, "Firmware request timeout"); + err = -ETIMEDOUT; + break; + } + + bt_dev_dbg(hdev, "Firmware request, expecting %d bytes", + mrvl->tx_len); + + if (fw_ptr == fw_max) { + /* Controller requests a null size once firmware is + * fully loaded. If controller expects more data, there + * is an issue. + */ + if (!mrvl->tx_len) { + bt_dev_info(hdev, "Firmware loading complete"); + } else { + bt_dev_err(hdev, "Firmware loading failure"); + err = -EINVAL; + } + break; + } + + if (fw_ptr + mrvl->tx_len > fw_max) { + mrvl->tx_len = fw_max - fw_ptr; + bt_dev_dbg(hdev, "Adjusting tx_len to %d", + mrvl->tx_len); + } + + skb = bt_skb_alloc(mrvl->tx_len, GFP_KERNEL); + if (!skb) { + bt_dev_err(hdev, "Failed to alloc mem for FW packet"); + err = -ENOMEM; + break; + } + bt_cb(skb)->pkt_type = MRVL_RAW_DATA; + + memcpy(skb_put(skb, mrvl->tx_len), fw_ptr, mrvl->tx_len); + fw_ptr += mrvl->tx_len; + + set_bit(STATE_FW_REQ_PENDING, &mrvl->flags); + + skb_queue_tail(&mrvl->rawq, skb); + hci_uart_tx_wakeup(hu); + } + + release_firmware(fw); + return err; +} + +static int mrvl_setup(struct hci_uart *hu) +{ + int err; + + hci_uart_set_flow_control(hu, true); + + err = mrvl_load_firmware(hu->hdev, "mrvl/helper_uart_3000000.bin"); + if (err) { + bt_dev_err(hu->hdev, "Unable to download firmware helper"); + return -EINVAL; + } + + hci_uart_set_baudrate(hu, 3000000); + hci_uart_set_flow_control(hu, false); + + err = mrvl_load_firmware(hu->hdev, "mrvl/uart8897_bt.bin"); + if (err) + return err; + + return 0; +} + +static const struct hci_uart_proto mrvl_proto = { + .id = HCI_UART_MRVL, + .name = "Marvell", + .init_speed = 115200, + .open = mrvl_open, + .close = mrvl_close, + .flush = mrvl_flush, + .setup = mrvl_setup, + .recv = mrvl_recv, + .enqueue = mrvl_enqueue, + .dequeue = mrvl_dequeue, +}; + +int __init mrvl_init(void) +{ + return hci_uart_register_proto(&mrvl_proto); +} + +int __exit mrvl_deinit(void) +{ + return hci_uart_unregister_proto(&mrvl_proto); +} diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 683c2b642057..6c867fbc56a7 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -397,7 +397,7 @@ static int qca_open(struct hci_uart *hu) skb_queue_head_init(&qca->txq); skb_queue_head_init(&qca->tx_wait_q); spin_lock_init(&qca->hci_ibs_lock); - qca->workqueue = create_singlethread_workqueue("qca_wq"); + qca->workqueue = alloc_ordered_workqueue("qca_wq", 0); if (!qca->workqueue) { BT_ERR("QCA Workqueue not initialized properly"); kfree(qca); diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 839bad1d8152..070139513e65 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -35,7 +35,7 @@ #define HCIUARTGETFLAGS _IOR('U', 204, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 10 +#define HCI_UART_MAX_PROTO 12 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 @@ -47,6 +47,8 @@ #define HCI_UART_BCM 7 #define HCI_UART_QCA 8 #define HCI_UART_AG6XX 9 +#define HCI_UART_NOKIA 10 +#define HCI_UART_MRVL 11 #define HCI_UART_RAW_DEVICE 0 #define HCI_UART_RESET_ON_INIT 1 @@ -189,3 +191,8 @@ int qca_deinit(void); int ag6xx_init(void); int ag6xx_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_MRVL +int mrvl_init(void); +int mrvl_deinit(void); +#endif |