diff options
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/Kconfig | 14 | ||||
-rw-r--r-- | drivers/bluetooth/Makefile | 1 | ||||
-rw-r--r-- | drivers/bluetooth/btbcm.c | 47 | ||||
-rw-r--r-- | drivers/bluetooth/btintel.c | 77 | ||||
-rw-r--r-- | drivers/bluetooth/btintel.h | 12 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.c | 2 | ||||
-rw-r--r-- | drivers/bluetooth/btmtkuart.c | 6 | ||||
-rw-r--r-- | drivers/bluetooth/btnxpuart.c | 1352 | ||||
-rw-r--r-- | drivers/bluetooth/btqca.c | 14 | ||||
-rw-r--r-- | drivers/bluetooth/btqca.h | 10 | ||||
-rw-r--r-- | drivers/bluetooth/btrtl.c | 502 | ||||
-rw-r--r-- | drivers/bluetooth/btrtl.h | 58 | ||||
-rw-r--r-- | drivers/bluetooth/btsdio.c | 1 | ||||
-rw-r--r-- | drivers/bluetooth/btusb.c | 318 | ||||
-rw-r--r-- | drivers/bluetooth/hci_bcm.c | 60 | ||||
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 6 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ldisc.c | 8 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ll.c | 2 | ||||
-rw-r--r-- | drivers/bluetooth/hci_mrvl.c | 90 | ||||
-rw-r--r-- | drivers/bluetooth/hci_qca.c | 67 | ||||
-rw-r--r-- | drivers/bluetooth/hci_vhci.c | 101 |
21 files changed, 2627 insertions, 121 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 5a1a7bec3c42..bc211c324206 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -363,6 +363,7 @@ config BT_HCIBLUECARD config BT_HCIVHCI tristate "HCI VHCI (Virtual HCI device) driver" + select WANT_DEV_COREDUMP help Bluetooth Virtual HCI device driver. This driver is required if you want to use HCI Emulation software. @@ -465,4 +466,17 @@ config BT_VIRTIO Say Y here to compile support for HCI over Virtio into the kernel or say M to compile as a module. +config BT_NXPUART + tristate "NXP protocol support" + depends on SERIAL_DEV_BUS + select CRC32 + select CRC8 + help + NXP is serial driver required for NXP Bluetooth + devices with UART interface. + + Say Y here to compile support for NXP Bluetooth UART device into + the kernel, or say M here to compile as a module (btnxpuart). + + endmenu diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index e0b261f24fc9..7a5967e9ac48 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_BT_QCA) += btqca.o obj-$(CONFIG_BT_MTK) += btmtk.o obj-$(CONFIG_BT_VIRTIO) += virtio_bt.o +obj-$(CONFIG_BT_NXPUART) += btnxpuart.o obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 43e98a598bd9..de2ea589aa49 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -6,6 +6,7 @@ * Copyright (C) 2015 Intel Corporation */ +#include <linux/efi.h> #include <linux/module.h> #include <linux/firmware.h> #include <linux/dmi.h> @@ -34,6 +35,43 @@ /* For kmalloc-ing the fw-name array instead of putting it on the stack */ typedef char bcm_fw_name[BCM_FW_NAME_LEN]; +#ifdef CONFIG_EFI +static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) +{ + efi_guid_t guid = EFI_GUID(0x74b00bd9, 0x805a, 0x4d61, 0xb5, 0x1f, + 0x43, 0x26, 0x81, 0x23, 0xd1, 0x13); + bdaddr_t efi_bdaddr, bdaddr; + efi_status_t status; + unsigned long len; + int ret; + + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) + return -EOPNOTSUPP; + + len = sizeof(efi_bdaddr); + status = efi.get_variable(L"BDADDR", &guid, NULL, &len, &efi_bdaddr); + if (status != EFI_SUCCESS) + return -ENXIO; + + if (len != sizeof(efi_bdaddr)) + return -EIO; + + baswap(&bdaddr, &efi_bdaddr); + + ret = btbcm_set_bdaddr(hdev, &bdaddr); + if (ret) + return ret; + + bt_dev_info(hdev, "BCM: Using EFI device address (%pMR)", &bdaddr); + return 0; +} +#else +static int btbcm_set_bdaddr_from_efi(struct hci_dev *hdev) +{ + return -EOPNOTSUPP; +} +#endif + int btbcm_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; @@ -87,9 +125,12 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) !bacmp(&bda->bdaddr, BDADDR_BCM4345C5) || !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); + /* Try falling back to BDADDR EFI variable */ + if (btbcm_set_bdaddr_from_efi(hdev) != 0) { + bt_dev_info(hdev, "BCM: Using default device address (%pMR)", + &bda->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } } kfree_skb(skb); diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index af774688f1c0..d9349ba48281 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -43,6 +43,12 @@ struct cmd_write_boot_params { u8 fw_build_yy; } __packed; +static struct { + const char *driver_name; + u8 hw_variant; + u32 fw_build_num; +} coredump_info; + int btintel_check_bdaddr(struct hci_dev *hdev) { struct hci_rp_read_bd_addr *bda; @@ -315,6 +321,9 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) return -EINVAL; } + coredump_info.hw_variant = ver->hw_variant; + coredump_info.fw_build_num = ver->fw_build_num; + bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u", variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, ver->fw_build_num, ver->fw_build_ww, @@ -509,6 +518,9 @@ static int btintel_version_info_tlv(struct hci_dev *hdev, return -EINVAL; } + coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt); + coredump_info.fw_build_num = version->build_num; + bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant, 2000 + (version->timestamp >> 8), version->timestamp & 0xff, version->build_type, version->build_num); @@ -1462,6 +1474,59 @@ int btintel_set_quality_report(struct hci_dev *hdev, bool enable) } EXPORT_SYMBOL_GPL(btintel_set_quality_report); +static void btintel_coredump(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc4e, 0, NULL, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb)); + return; + } + + kfree_skb(skb); +} + +static void btintel_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb) +{ + char buf[80]; + + snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n", + coredump_info.hw_variant); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n", + coredump_info.fw_build_num); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info.driver_name); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Vendor: Intel\n"); + skb_put_data(skb, buf, strlen(buf)); +} + +static int btintel_register_devcoredump_support(struct hci_dev *hdev) +{ + struct intel_debug_features features; + int err; + + err = btintel_read_debug_features(hdev, &features); + if (err) { + bt_dev_info(hdev, "Error reading debug features"); + return err; + } + + if (!(features.page1[0] & 0x3f)) { + bt_dev_dbg(hdev, "Telemetry exception format not supported"); + return -EOPNOTSUPP; + } + + hci_devcd_register(hdev, btintel_coredump, btintel_dmp_hdr, NULL); + + return err; +} + static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev, struct intel_version *ver) { @@ -2597,6 +2662,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) btintel_set_msft_opcode(hdev, ver.hw_variant); err = btintel_bootloader_setup(hdev, &ver); + btintel_register_devcoredump_support(hdev); break; default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", @@ -2670,6 +2736,7 @@ static int btintel_setup_combined(struct hci_dev *hdev) btintel_set_msft_opcode(hdev, ver.hw_variant); err = btintel_bootloader_setup(hdev, &ver); + btintel_register_devcoredump_support(hdev); break; case 0x17: case 0x18: @@ -2684,15 +2751,15 @@ static int btintel_setup_combined(struct hci_dev *hdev) */ set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); - /* Valid LE States quirk for GfP */ - if (INTEL_HW_VARIANT(ver_tlv.cnvi_bt) == 0x18) - set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); + /* Apply LE States quirk from solar onwards */ + set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); /* Setup MSFT Extension support */ btintel_set_msft_opcode(hdev, INTEL_HW_VARIANT(ver_tlv.cnvi_bt)); err = btintel_bootloader_setup_tlv(hdev, &ver_tlv); + btintel_register_devcoredump_support(hdev); break; default: bt_dev_err(hdev, "Unsupported Intel hw variant (%u)", @@ -2742,7 +2809,7 @@ static int btintel_shutdown_combined(struct hci_dev *hdev) return 0; } -int btintel_configure_setup(struct hci_dev *hdev) +int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name) { hdev->manufacturer = 2; hdev->setup = btintel_setup_combined; @@ -2751,6 +2818,8 @@ int btintel_configure_setup(struct hci_dev *hdev) hdev->set_diag = btintel_set_diag_combined; hdev->set_bdaddr = btintel_set_bdaddr; + coredump_info.driver_name = driver_name; + return 0; } EXPORT_SYMBOL_GPL(btintel_configure_setup); diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h index 8fdb65b66315..d6a1dc8d8a82 100644 --- a/drivers/bluetooth/btintel.h +++ b/drivers/bluetooth/btintel.h @@ -143,6 +143,13 @@ struct btintel_loc_aware_reg { __le32 delta; } __packed; +#define INTEL_TLV_TYPE_ID 0x01 + +#define INTEL_TLV_SYSTEM_EXCEPTION 0x00 +#define INTEL_TLV_FATAL_EXCEPTION 0x01 +#define INTEL_TLV_DEBUG_EXCEPTION 0x02 +#define INTEL_TLV_TEST_EXCEPTION 0xDE + #define INTEL_HW_PLATFORM(cnvx_bt) ((u8)(((cnvx_bt) & 0x0000ff00) >> 8)) #define INTEL_HW_VARIANT(cnvx_bt) ((u8)(((cnvx_bt) & 0x003f0000) >> 16)) #define INTEL_CNVX_TOP_TYPE(cnvx_top) ((cnvx_top) & 0x00000fff) @@ -212,7 +219,7 @@ int btintel_read_boot_params(struct hci_dev *hdev, struct intel_boot_params *params); int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver, const struct firmware *fw, u32 *boot_param); -int btintel_configure_setup(struct hci_dev *hdev); +int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name); void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len); void btintel_secure_send_result(struct hci_dev *hdev, const void *ptr, unsigned int len); @@ -293,7 +300,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev, return -EOPNOTSUPP; } -static inline int btintel_configure_setup(struct hci_dev *hdev) +static inline int btintel_configure_setup(struct hci_dev *hdev, + const char *driver_name) { return -ENODEV; } diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index ba057ebfda5c..d76c799553aa 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -40,7 +40,7 @@ static struct memory_type_mapping mem_type_mapping_tbl[] = { {"EXTLAST", NULL, 0, 0xFE}, }; -static const struct of_device_id btmrvl_sdio_of_match_table[] = { +static const struct of_device_id btmrvl_sdio_of_match_table[] __maybe_unused = { { .compatible = "marvell,sd8897-bt" }, { .compatible = "marvell,sd8997-bt" }, { } diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c index c98691cdbbd5..7680c67cdb35 100644 --- a/drivers/bluetooth/btmtkuart.c +++ b/drivers/bluetooth/btmtkuart.c @@ -959,16 +959,16 @@ static void btmtkuart_remove(struct serdev_device *serdev) hci_free_dev(hdev); } -static const struct btmtkuart_data mt7622_data = { +static const struct btmtkuart_data mt7622_data __maybe_unused = { .fwname = FIRMWARE_MT7622, }; -static const struct btmtkuart_data mt7663_data = { +static const struct btmtkuart_data mt7663_data __maybe_unused = { .flags = BTMTKUART_FLAG_STANDALONE_HW, .fwname = FIRMWARE_MT7663, }; -static const struct btmtkuart_data mt7668_data = { +static const struct btmtkuart_data mt7668_data __maybe_unused = { .flags = BTMTKUART_FLAG_STANDALONE_HW, .fwname = FIRMWARE_MT7668, }; diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c new file mode 100644 index 000000000000..3a34d7c1475b --- /dev/null +++ b/drivers/bluetooth/btnxpuart.c @@ -0,0 +1,1352 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * NXP Bluetooth driver + * Copyright 2023 NXP + */ + +#include <linux/module.h> +#include <linux/kernel.h> + +#include <linux/serdev.h> +#include <linux/of.h> +#include <linux/skbuff.h> +#include <asm/unaligned.h> +#include <linux/firmware.h> +#include <linux/string.h> +#include <linux/crc8.h> +#include <linux/crc32.h> +#include <linux/string_helpers.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "h4_recv.h" + +#define MANUFACTURER_NXP 37 + +#define BTNXPUART_TX_STATE_ACTIVE 1 +#define BTNXPUART_FW_DOWNLOADING 2 +#define BTNXPUART_CHECK_BOOT_SIGNATURE 3 +#define BTNXPUART_SERDEV_OPEN 4 + +#define FIRMWARE_W8987 "nxp/uartuart8987_bt.bin" +#define FIRMWARE_W8997 "nxp/uartuart8997_bt_v4.bin" +#define FIRMWARE_W9098 "nxp/uartuart9098_bt_v1.bin" +#define FIRMWARE_IW416 "nxp/uartiw416_bt_v0.bin" +#define FIRMWARE_IW612 "nxp/uartspi_n61x_v1.bin.se" +#define FIRMWARE_HELPER "nxp/helper_uart_3000000.bin" + +#define CHIP_ID_W9098 0x5c03 +#define CHIP_ID_IW416 0x7201 +#define CHIP_ID_IW612 0x7601 + +#define HCI_NXP_PRI_BAUDRATE 115200 +#define HCI_NXP_SEC_BAUDRATE 3000000 + +#define MAX_FW_FILE_NAME_LEN 50 + +/* Default ps timeout period in milliseconds */ +#define PS_DEFAULT_TIMEOUT_PERIOD_MS 2000 + +/* wakeup methods */ +#define WAKEUP_METHOD_DTR 0 +#define WAKEUP_METHOD_BREAK 1 +#define WAKEUP_METHOD_EXT_BREAK 2 +#define WAKEUP_METHOD_RTS 3 +#define WAKEUP_METHOD_INVALID 0xff + +/* power save mode status */ +#define PS_MODE_DISABLE 0 +#define PS_MODE_ENABLE 1 + +/* Power Save Commands to ps_work_func */ +#define PS_CMD_EXIT_PS 1 +#define PS_CMD_ENTER_PS 2 + +/* power save state */ +#define PS_STATE_AWAKE 0 +#define PS_STATE_SLEEP 1 + +/* Bluetooth vendor command : Sleep mode */ +#define HCI_NXP_AUTO_SLEEP_MODE 0xfc23 +/* Bluetooth vendor command : Wakeup method */ +#define HCI_NXP_WAKEUP_METHOD 0xfc53 +/* Bluetooth vendor command : Set operational baudrate */ +#define HCI_NXP_SET_OPER_SPEED 0xfc09 +/* Bluetooth vendor command: Independent Reset */ +#define HCI_NXP_IND_RESET 0xfcfc + +/* Bluetooth Power State : Vendor cmd params */ +#define BT_PS_ENABLE 0x02 +#define BT_PS_DISABLE 0x03 + +/* Bluetooth Host Wakeup Methods */ +#define BT_HOST_WAKEUP_METHOD_NONE 0x00 +#define BT_HOST_WAKEUP_METHOD_DTR 0x01 +#define BT_HOST_WAKEUP_METHOD_BREAK 0x02 +#define BT_HOST_WAKEUP_METHOD_GPIO 0x03 + +/* Bluetooth Chip Wakeup Methods */ +#define BT_CTRL_WAKEUP_METHOD_DSR 0x00 +#define BT_CTRL_WAKEUP_METHOD_BREAK 0x01 +#define BT_CTRL_WAKEUP_METHOD_GPIO 0x02 +#define BT_CTRL_WAKEUP_METHOD_EXT_BREAK 0x04 +#define BT_CTRL_WAKEUP_METHOD_RTS 0x05 + +struct ps_data { + u8 target_ps_mode; /* ps mode to be set */ + u8 cur_psmode; /* current ps_mode */ + u8 ps_state; /* controller's power save state */ + u8 ps_cmd; + u8 h2c_wakeupmode; + u8 cur_h2c_wakeupmode; + u8 c2h_wakeupmode; + u8 c2h_wakeup_gpio; + u8 h2c_wakeup_gpio; + bool driver_sent_cmd; + u16 h2c_ps_interval; + u16 c2h_ps_interval; + struct hci_dev *hdev; + struct work_struct work; + struct timer_list ps_timer; +}; + +struct wakeup_cmd_payload { + u8 c2h_wakeupmode; + u8 c2h_wakeup_gpio; + u8 h2c_wakeupmode; + u8 h2c_wakeup_gpio; +} __packed; + +struct psmode_cmd_payload { + u8 ps_cmd; + __le16 c2h_ps_interval; +} __packed; + +struct btnxpuart_data { + const char *helper_fw_name; + const char *fw_name; +}; + +struct btnxpuart_dev { + struct hci_dev *hdev; + struct serdev_device *serdev; + + struct work_struct tx_work; + unsigned long tx_state; + struct sk_buff_head txq; + struct sk_buff *rx_skb; + + const struct firmware *fw; + u8 fw_name[MAX_FW_FILE_NAME_LEN]; + u32 fw_dnld_v1_offset; + u32 fw_v1_sent_bytes; + u32 fw_v3_offset_correction; + u32 fw_v1_expected_len; + wait_queue_head_t fw_dnld_done_wait_q; + wait_queue_head_t check_boot_sign_wait_q; + + u32 new_baudrate; + u32 current_baudrate; + u32 fw_init_baudrate; + bool timeout_changed; + bool baudrate_changed; + bool helper_downloaded; + + struct ps_data psdata; + struct btnxpuart_data *nxp_data; +}; + +#define NXP_V1_FW_REQ_PKT 0xa5 +#define NXP_V1_CHIP_VER_PKT 0xaa +#define NXP_V3_FW_REQ_PKT 0xa7 +#define NXP_V3_CHIP_VER_PKT 0xab + +#define NXP_ACK_V1 0x5a +#define NXP_NAK_V1 0xbf +#define NXP_ACK_V3 0x7a +#define NXP_NAK_V3 0x7b +#define NXP_CRC_ERROR_V3 0x7c + +#define HDR_LEN 16 + +#define NXP_RECV_CHIP_VER_V1 \ + .type = NXP_V1_CHIP_VER_PKT, \ + .hlen = 4, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 4 + +#define NXP_RECV_FW_REQ_V1 \ + .type = NXP_V1_FW_REQ_PKT, \ + .hlen = 4, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 4 + +#define NXP_RECV_CHIP_VER_V3 \ + .type = NXP_V3_CHIP_VER_PKT, \ + .hlen = 4, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 4 + +#define NXP_RECV_FW_REQ_V3 \ + .type = NXP_V3_FW_REQ_PKT, \ + .hlen = 9, \ + .loff = 0, \ + .lsize = 0, \ + .maxlen = 9 + +struct v1_data_req { + __le16 len; + __le16 len_comp; +} __packed; + +struct v1_start_ind { + __le16 chip_id; + __le16 chip_id_comp; +} __packed; + +struct v3_data_req { + __le16 len; + __le32 offset; + __le16 error; + u8 crc; +} __packed; + +struct v3_start_ind { + __le16 chip_id; + u8 loader_ver; + u8 crc; +} __packed; + +/* UART register addresses of BT chip */ +#define CLKDIVADDR 0x7f00008f +#define UARTDIVADDR 0x7f000090 +#define UARTMCRADDR 0x7f000091 +#define UARTREINITADDR 0x7f000092 +#define UARTICRADDR 0x7f000093 +#define UARTFCRADDR 0x7f000094 + +#define MCR 0x00000022 +#define INIT 0x00000001 +#define ICR 0x000000c7 +#define FCR 0x000000c7 + +#define POLYNOMIAL8 0x07 + +struct uart_reg { + __le32 address; + __le32 value; +} __packed; + +struct uart_config { + struct uart_reg clkdiv; + struct uart_reg uartdiv; + struct uart_reg mcr; + struct uart_reg re_init; + struct uart_reg icr; + struct uart_reg fcr; + __be32 crc; +} __packed; + +struct nxp_bootloader_cmd { + __le32 header; + __le32 arg; + __le32 payload_len; + __be32 crc; +} __packed; + +static u8 crc8_table[CRC8_TABLE_SIZE]; + +/* Default configurations */ +#define DEFAULT_H2C_WAKEUP_MODE WAKEUP_METHOD_BREAK +#define DEFAULT_PS_MODE PS_MODE_DISABLE +#define FW_INIT_BAUDRATE HCI_NXP_PRI_BAUDRATE + +static struct sk_buff *nxp_drv_send_cmd(struct hci_dev *hdev, u16 opcode, + u32 plen, + void *param) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct sk_buff *skb; + + /* set flag to prevent nxp_enqueue from parsing values from this command and + * calling hci_cmd_sync_queue() again. + */ + psdata->driver_sent_cmd = true; + skb = __hci_cmd_sync(hdev, opcode, plen, param, HCI_CMD_TIMEOUT); + psdata->driver_sent_cmd = false; + + return skb; +} + +static void btnxpuart_tx_wakeup(struct btnxpuart_dev *nxpdev) +{ + if (schedule_work(&nxpdev->tx_work)) + set_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state); +} + +/* NXP Power Save Feature */ +static void ps_start_timer(struct btnxpuart_dev *nxpdev) +{ + struct ps_data *psdata = &nxpdev->psdata; + + if (!psdata) + return; + + if (psdata->cur_psmode == PS_MODE_ENABLE) + mod_timer(&psdata->ps_timer, jiffies + msecs_to_jiffies(psdata->h2c_ps_interval)); +} + +static void ps_cancel_timer(struct btnxpuart_dev *nxpdev) +{ + struct ps_data *psdata = &nxpdev->psdata; + + flush_work(&psdata->work); + del_timer_sync(&psdata->ps_timer); +} + +static void ps_control(struct hci_dev *hdev, u8 ps_state) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + int status; + + if (psdata->ps_state == ps_state || + !test_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state)) + return; + + switch (psdata->cur_h2c_wakeupmode) { + case WAKEUP_METHOD_DTR: + if (ps_state == PS_STATE_AWAKE) + status = serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0); + else + status = serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR); + break; + case WAKEUP_METHOD_BREAK: + default: + if (ps_state == PS_STATE_AWAKE) + status = serdev_device_break_ctl(nxpdev->serdev, 0); + else + status = serdev_device_break_ctl(nxpdev->serdev, -1); + bt_dev_dbg(hdev, "Set UART break: %s, status=%d", + str_on_off(ps_state == PS_STATE_SLEEP), status); + break; + } + if (!status) + psdata->ps_state = ps_state; + if (ps_state == PS_STATE_AWAKE) + btnxpuart_tx_wakeup(nxpdev); +} + +static void ps_work_func(struct work_struct *work) +{ + struct ps_data *data = container_of(work, struct ps_data, work); + + if (data->ps_cmd == PS_CMD_ENTER_PS && data->cur_psmode == PS_MODE_ENABLE) + ps_control(data->hdev, PS_STATE_SLEEP); + else if (data->ps_cmd == PS_CMD_EXIT_PS) + ps_control(data->hdev, PS_STATE_AWAKE); +} + +static void ps_timeout_func(struct timer_list *t) +{ + struct ps_data *data = from_timer(data, t, ps_timer); + struct hci_dev *hdev = data->hdev; + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + if (test_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state)) { + ps_start_timer(nxpdev); + } else { + data->ps_cmd = PS_CMD_ENTER_PS; + schedule_work(&data->work); + } +} + +static int ps_init_work(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + + psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS; + psdata->ps_state = PS_STATE_AWAKE; + psdata->target_ps_mode = DEFAULT_PS_MODE; + psdata->hdev = hdev; + psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE; + psdata->c2h_wakeup_gpio = 0xff; + + switch (DEFAULT_H2C_WAKEUP_MODE) { + case WAKEUP_METHOD_DTR: + psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR; + break; + case WAKEUP_METHOD_BREAK: + default: + psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK; + break; + } + psdata->cur_psmode = PS_MODE_DISABLE; + psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID; + INIT_WORK(&psdata->work, ps_work_func); + + return 0; +} + +static void ps_init_timer(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + + timer_setup(&psdata->ps_timer, ps_timeout_func, 0); +} + +static void ps_wakeup(struct btnxpuart_dev *nxpdev) +{ + struct ps_data *psdata = &nxpdev->psdata; + + if (psdata->ps_state != PS_STATE_AWAKE) { + psdata->ps_cmd = PS_CMD_EXIT_PS; + schedule_work(&psdata->work); + } +} + +static int send_ps_cmd(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct psmode_cmd_payload pcmd; + struct sk_buff *skb; + u8 *status; + + if (psdata->target_ps_mode == PS_MODE_ENABLE) + pcmd.ps_cmd = BT_PS_ENABLE; + else + pcmd.ps_cmd = BT_PS_DISABLE; + pcmd.c2h_ps_interval = __cpu_to_le16(psdata->c2h_ps_interval); + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_AUTO_SLEEP_MODE, sizeof(pcmd), &pcmd); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Setting Power Save mode failed (%ld)", PTR_ERR(skb)); + return PTR_ERR(skb); + } + + status = skb_pull_data(skb, 1); + if (status) { + if (!*status) + psdata->cur_psmode = psdata->target_ps_mode; + else + psdata->target_ps_mode = psdata->cur_psmode; + if (psdata->cur_psmode == PS_MODE_ENABLE) + ps_start_timer(nxpdev); + else + ps_wakeup(nxpdev); + bt_dev_dbg(hdev, "Power Save mode response: status=%d, ps_mode=%d", + *status, psdata->cur_psmode); + } + kfree_skb(skb); + + return 0; +} + +static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct wakeup_cmd_payload pcmd; + struct sk_buff *skb; + u8 *status; + + pcmd.c2h_wakeupmode = psdata->c2h_wakeupmode; + pcmd.c2h_wakeup_gpio = psdata->c2h_wakeup_gpio; + switch (psdata->h2c_wakeupmode) { + case WAKEUP_METHOD_DTR: + pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_DSR; + break; + case WAKEUP_METHOD_BREAK: + default: + pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_BREAK; + break; + } + pcmd.h2c_wakeup_gpio = 0xff; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_WAKEUP_METHOD, sizeof(pcmd), &pcmd); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Setting wake-up method failed (%ld)", PTR_ERR(skb)); + return PTR_ERR(skb); + } + + status = skb_pull_data(skb, 1); + if (status) { + if (*status == 0) + psdata->cur_h2c_wakeupmode = psdata->h2c_wakeupmode; + else + psdata->h2c_wakeupmode = psdata->cur_h2c_wakeupmode; + bt_dev_dbg(hdev, "Set Wakeup Method response: status=%d, h2c_wakeupmode=%d", + *status, psdata->cur_h2c_wakeupmode); + } + kfree_skb(skb); + + return 0; +} + +static void ps_init(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + + serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_RTS); + usleep_range(5000, 10000); + serdev_device_set_tiocm(nxpdev->serdev, TIOCM_RTS, 0); + usleep_range(5000, 10000); + + switch (psdata->h2c_wakeupmode) { + case WAKEUP_METHOD_DTR: + serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR); + serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0); + break; + case WAKEUP_METHOD_BREAK: + default: + serdev_device_break_ctl(nxpdev->serdev, -1); + usleep_range(5000, 10000); + serdev_device_break_ctl(nxpdev->serdev, 0); + usleep_range(5000, 10000); + break; + } + if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode) + hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL); + if (psdata->cur_psmode != psdata->target_ps_mode) + hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL); +} + +/* NXP Firmware Download Feature */ +static int nxp_download_firmware(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + nxpdev->fw_dnld_v1_offset = 0; + nxpdev->fw_v1_sent_bytes = 0; + nxpdev->fw_v1_expected_len = HDR_LEN; + nxpdev->fw_v3_offset_correction = 0; + nxpdev->baudrate_changed = false; + nxpdev->timeout_changed = false; + nxpdev->helper_downloaded = false; + + serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, false); + nxpdev->current_baudrate = HCI_NXP_PRI_BAUDRATE; + + /* Wait till FW is downloaded and CTS becomes low */ + err = wait_event_interruptible_timeout(nxpdev->fw_dnld_done_wait_q, + !test_bit(BTNXPUART_FW_DOWNLOADING, + &nxpdev->tx_state), + msecs_to_jiffies(60000)); + if (err == 0) { + bt_dev_err(hdev, "FW Download Timeout."); + return -ETIMEDOUT; + } + + serdev_device_set_flow_control(nxpdev->serdev, true); + err = serdev_device_wait_for_cts(nxpdev->serdev, 1, 60000); + if (err < 0) { + bt_dev_err(hdev, "CTS is still high. FW Download failed."); + return err; + } + release_firmware(nxpdev->fw); + memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name)); + + /* Allow the downloaded FW to initialize */ + usleep_range(800 * USEC_PER_MSEC, 1 * USEC_PER_SEC); + + return 0; +} + +static void nxp_send_ack(u8 ack, struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u8 ack_nak[2]; + int len = 1; + + ack_nak[0] = ack; + if (ack == NXP_ACK_V3) { + ack_nak[1] = crc8(crc8_table, ack_nak, 1, 0xff); + len = 2; + } + serdev_device_write_buf(nxpdev->serdev, ack_nak, len); +} + +static bool nxp_fw_change_baudrate(struct hci_dev *hdev, u16 req_len) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_bootloader_cmd nxp_cmd5; + struct uart_config uart_config; + + if (req_len == sizeof(nxp_cmd5)) { + nxp_cmd5.header = __cpu_to_le32(5); + nxp_cmd5.arg = 0; + nxp_cmd5.payload_len = __cpu_to_le32(sizeof(uart_config)); + /* FW expects swapped CRC bytes */ + nxp_cmd5.crc = __cpu_to_be32(crc32_be(0UL, (char *)&nxp_cmd5, + sizeof(nxp_cmd5) - 4)); + + serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd5, sizeof(nxp_cmd5)); + nxpdev->fw_v3_offset_correction += req_len; + } else if (req_len == sizeof(uart_config)) { + uart_config.clkdiv.address = __cpu_to_le32(CLKDIVADDR); + uart_config.clkdiv.value = __cpu_to_le32(0x00c00000); + uart_config.uartdiv.address = __cpu_to_le32(UARTDIVADDR); + uart_config.uartdiv.value = __cpu_to_le32(1); + uart_config.mcr.address = __cpu_to_le32(UARTMCRADDR); + uart_config.mcr.value = __cpu_to_le32(MCR); + uart_config.re_init.address = __cpu_to_le32(UARTREINITADDR); + uart_config.re_init.value = __cpu_to_le32(INIT); + uart_config.icr.address = __cpu_to_le32(UARTICRADDR); + uart_config.icr.value = __cpu_to_le32(ICR); + uart_config.fcr.address = __cpu_to_le32(UARTFCRADDR); + uart_config.fcr.value = __cpu_to_le32(FCR); + /* FW expects swapped CRC bytes */ + uart_config.crc = __cpu_to_be32(crc32_be(0UL, (char *)&uart_config, + sizeof(uart_config) - 4)); + + serdev_device_write_buf(nxpdev->serdev, (u8 *)&uart_config, sizeof(uart_config)); + serdev_device_wait_until_sent(nxpdev->serdev, 0); + nxpdev->fw_v3_offset_correction += req_len; + return true; + } + return false; +} + +static bool nxp_fw_change_timeout(struct hci_dev *hdev, u16 req_len) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct nxp_bootloader_cmd nxp_cmd7; + + if (req_len != sizeof(nxp_cmd7)) + return false; + + nxp_cmd7.header = __cpu_to_le32(7); + nxp_cmd7.arg = __cpu_to_le32(0x70); + nxp_cmd7.payload_len = 0; + /* FW expects swapped CRC bytes */ + nxp_cmd7.crc = __cpu_to_be32(crc32_be(0UL, (char *)&nxp_cmd7, + sizeof(nxp_cmd7) - 4)); + serdev_device_write_buf(nxpdev->serdev, (u8 *)&nxp_cmd7, sizeof(nxp_cmd7)); + serdev_device_wait_until_sent(nxpdev->serdev, 0); + nxpdev->fw_v3_offset_correction += req_len; + return true; +} + +static u32 nxp_get_data_len(const u8 *buf) +{ + struct nxp_bootloader_cmd *hdr = (struct nxp_bootloader_cmd *)buf; + + return __le32_to_cpu(hdr->payload_len); +} + +static bool is_fw_downloading(struct btnxpuart_dev *nxpdev) +{ + return test_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); +} + +static bool process_boot_signature(struct btnxpuart_dev *nxpdev) +{ + if (test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state)) { + clear_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->check_boot_sign_wait_q); + return false; + } + return is_fw_downloading(nxpdev); +} + +static int nxp_request_firmware(struct hci_dev *hdev, const char *fw_name) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + if (!strlen(nxpdev->fw_name)) { + snprintf(nxpdev->fw_name, MAX_FW_FILE_NAME_LEN, "%s", fw_name); + + bt_dev_dbg(hdev, "Request Firmware: %s", nxpdev->fw_name); + err = request_firmware(&nxpdev->fw, nxpdev->fw_name, &hdev->dev); + if (err < 0) { + bt_dev_err(hdev, "Firmware file %s not found", nxpdev->fw_name); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + } + } + return err; +} + +/* for legacy chipsets with V1 bootloader */ +static int nxp_recv_chip_ver_v1(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct v1_start_ind *req; + __u16 chip_id; + + req = skb_pull_data(skb, sizeof(*req)); + if (!req) + goto free_skb; + + chip_id = le16_to_cpu(req->chip_id ^ req->chip_id_comp); + if (chip_id == 0xffff) { + nxpdev->fw_dnld_v1_offset = 0; + nxpdev->fw_v1_sent_bytes = 0; + nxpdev->fw_v1_expected_len = HDR_LEN; + release_firmware(nxpdev->fw); + memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name)); + nxp_send_ack(NXP_ACK_V1, hdev); + } + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct btnxpuart_data *nxp_data = nxpdev->nxp_data; + struct v1_data_req *req; + __u16 len; + + if (!process_boot_signature(nxpdev)) + goto free_skb; + + req = skb_pull_data(skb, sizeof(*req)); + if (!req) + goto free_skb; + + len = __le16_to_cpu(req->len ^ req->len_comp); + if (len != 0xffff) { + bt_dev_dbg(hdev, "ERR: Send NAK"); + nxp_send_ack(NXP_NAK_V1, hdev); + goto free_skb; + } + nxp_send_ack(NXP_ACK_V1, hdev); + + len = __le16_to_cpu(req->len); + + if (!nxp_data->helper_fw_name) { + if (!nxpdev->timeout_changed) { + nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, + len); + goto free_skb; + } + if (!nxpdev->baudrate_changed) { + nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, + len); + if (nxpdev->baudrate_changed) { + serdev_device_set_baudrate(nxpdev->serdev, + HCI_NXP_SEC_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, true); + nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + } + goto free_skb; + } + } + + if (!nxp_data->helper_fw_name || nxpdev->helper_downloaded) { + if (nxp_request_firmware(hdev, nxp_data->fw_name)) + goto free_skb; + } else if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) { + if (nxp_request_firmware(hdev, nxp_data->helper_fw_name)) + goto free_skb; + } + + if (!len) { + bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", + nxpdev->fw->size); + if (nxp_data->helper_fw_name && !nxpdev->helper_downloaded) { + nxpdev->helper_downloaded = true; + serdev_device_wait_until_sent(nxpdev->serdev, 0); + serdev_device_set_baudrate(nxpdev->serdev, + HCI_NXP_SEC_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, true); + } else { + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); + } + goto free_skb; + } + if (len & 0x01) { + /* The CRC did not match at the other end. + * Simply send the same bytes again. + */ + len = nxpdev->fw_v1_sent_bytes; + bt_dev_dbg(hdev, "CRC error. Resend %d bytes of FW.", len); + } else { + nxpdev->fw_dnld_v1_offset += nxpdev->fw_v1_sent_bytes; + + /* The FW bin file is made up of many blocks of + * 16 byte header and payload data chunks. If the + * FW has requested a header, read the payload length + * info from the header, before sending the header. + * In the next iteration, the FW should request the + * payload data chunk, which should be equal to the + * payload length read from header. If there is a + * mismatch, clearly the driver and FW are out of sync, + * and we need to re-send the previous header again. + */ + if (len == nxpdev->fw_v1_expected_len) { + if (len == HDR_LEN) + nxpdev->fw_v1_expected_len = nxp_get_data_len(nxpdev->fw->data + + nxpdev->fw_dnld_v1_offset); + else + nxpdev->fw_v1_expected_len = HDR_LEN; + } else if (len == HDR_LEN) { + /* FW download out of sync. Send previous chunk again */ + nxpdev->fw_dnld_v1_offset -= nxpdev->fw_v1_sent_bytes; + nxpdev->fw_v1_expected_len = HDR_LEN; + } + } + + if (nxpdev->fw_dnld_v1_offset + len <= nxpdev->fw->size) + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + + nxpdev->fw_dnld_v1_offset, len); + nxpdev->fw_v1_sent_bytes = len; + +free_skb: + kfree_skb(skb); + return 0; +} + +static char *nxp_get_fw_name_from_chipid(struct hci_dev *hdev, u16 chipid) +{ + char *fw_name = NULL; + + switch (chipid) { + case CHIP_ID_W9098: + fw_name = FIRMWARE_W9098; + break; + case CHIP_ID_IW416: + fw_name = FIRMWARE_IW416; + break; + case CHIP_ID_IW612: + fw_name = FIRMWARE_IW612; + break; + default: + bt_dev_err(hdev, "Unknown chip signature %04x", chipid); + break; + } + return fw_name; +} + +static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct v3_start_ind *req = skb_pull_data(skb, sizeof(*req)); + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + u16 chip_id; + + if (!process_boot_signature(nxpdev)) + goto free_skb; + + chip_id = le16_to_cpu(req->chip_id); + if (!nxp_request_firmware(hdev, nxp_get_fw_name_from_chipid(hdev, + chip_id))) + nxp_send_ack(NXP_ACK_V3, hdev); + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct v3_data_req *req; + __u16 len; + __u32 offset; + + if (!process_boot_signature(nxpdev)) + goto free_skb; + + req = skb_pull_data(skb, sizeof(*req)); + if (!req || !nxpdev->fw) + goto free_skb; + + nxp_send_ack(NXP_ACK_V3, hdev); + + len = __le16_to_cpu(req->len); + + if (!nxpdev->timeout_changed) { + nxpdev->timeout_changed = nxp_fw_change_timeout(hdev, len); + goto free_skb; + } + + if (!nxpdev->baudrate_changed) { + nxpdev->baudrate_changed = nxp_fw_change_baudrate(hdev, len); + if (nxpdev->baudrate_changed) { + serdev_device_set_baudrate(nxpdev->serdev, + HCI_NXP_SEC_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, true); + nxpdev->current_baudrate = HCI_NXP_SEC_BAUDRATE; + } + goto free_skb; + } + + if (req->len == 0) { + bt_dev_dbg(hdev, "FW Downloaded Successfully: %zu bytes", + nxpdev->fw->size); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q); + goto free_skb; + } + if (req->error) + bt_dev_dbg(hdev, "FW Download received err 0x%02x from chip", + req->error); + + offset = __le32_to_cpu(req->offset); + if (offset < nxpdev->fw_v3_offset_correction) { + /* This scenario should ideally never occur. But if it ever does, + * FW is out of sync and needs a power cycle. + */ + bt_dev_err(hdev, "Something went wrong during FW download"); + bt_dev_err(hdev, "Please power cycle and try again"); + goto free_skb; + } + + serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + offset - + nxpdev->fw_v3_offset_correction, len); + +free_skb: + kfree_skb(skb); + return 0; +} + +static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + __le32 new_baudrate = __cpu_to_le32(nxpdev->new_baudrate); + struct ps_data *psdata = &nxpdev->psdata; + struct sk_buff *skb; + u8 *status; + + if (!psdata) + return 0; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_SET_OPER_SPEED, 4, (u8 *)&new_baudrate); + if (IS_ERR(skb)) { + bt_dev_err(hdev, "Setting baudrate failed (%ld)", PTR_ERR(skb)); + return PTR_ERR(skb); + } + + status = (u8 *)skb_pull_data(skb, 1); + if (status) { + if (*status == 0) { + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->new_baudrate); + nxpdev->current_baudrate = nxpdev->new_baudrate; + } + bt_dev_dbg(hdev, "Set baudrate response: status=%d, baudrate=%d", + *status, nxpdev->new_baudrate); + } + kfree_skb(skb); + + return 0; +} + +static int nxp_set_ind_reset(struct hci_dev *hdev, void *data) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct sk_buff *skb; + u8 *status; + u8 pcmd = 0; + int err = 0; + + skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + status = skb_pull_data(skb, 1); + if (!status || *status) + goto free_skb; + + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + err = nxp_download_firmware(hdev); + if (err < 0) + goto free_skb; + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); + nxpdev->current_baudrate = nxpdev->fw_init_baudrate; + if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) { + nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE; + nxp_set_baudrate_cmd(hdev, NULL); + } + hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL); + hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL); + +free_skb: + kfree_skb(skb); + return err; +} + +/* NXP protocol */ +static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev) +{ + serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE); + serdev_device_set_flow_control(nxpdev->serdev, true); + set_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state); + + return wait_event_interruptible_timeout(nxpdev->check_boot_sign_wait_q, + !test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, + &nxpdev->tx_state), + msecs_to_jiffies(1000)); +} + +static int nxp_setup(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q); + init_waitqueue_head(&nxpdev->check_boot_sign_wait_q); + + if (nxp_check_boot_sign(nxpdev)) { + bt_dev_dbg(hdev, "Need FW Download."); + err = nxp_download_firmware(hdev); + if (err < 0) + return err; + } else { + bt_dev_dbg(hdev, "FW already running."); + clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state); + } + + device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate", + &nxpdev->fw_init_baudrate); + if (!nxpdev->fw_init_baudrate) + nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE; + serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate); + nxpdev->current_baudrate = nxpdev->fw_init_baudrate; + + if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) { + nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE; + hci_cmd_sync_queue(hdev, nxp_set_baudrate_cmd, NULL, NULL); + } + + ps_init(hdev); + + return 0; +} + +static int btnxpuart_queue_skb(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); + skb_queue_tail(&nxpdev->txq, skb); + btnxpuart_tx_wakeup(nxpdev); + return 0; +} + +static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + struct ps_data *psdata = &nxpdev->psdata; + struct hci_command_hdr *hdr; + struct psmode_cmd_payload ps_parm; + struct wakeup_cmd_payload wakeup_parm; + __le32 baudrate_parm; + + /* if vendor commands are received from user space (e.g. hcitool), update + * driver flags accordingly and ask driver to re-send the command to FW. + * In case the payload for any command does not match expected payload + * length, let the firmware and user space program handle it, or throw + * an error. + */ + if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT && !psdata->driver_sent_cmd) { + hdr = (struct hci_command_hdr *)skb->data; + if (hdr->plen != (skb->len - HCI_COMMAND_HDR_SIZE)) + return btnxpuart_queue_skb(hdev, skb); + + switch (__le16_to_cpu(hdr->opcode)) { + case HCI_NXP_AUTO_SLEEP_MODE: + if (hdr->plen == sizeof(ps_parm)) { + memcpy(&ps_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen); + if (ps_parm.ps_cmd == BT_PS_ENABLE) + psdata->target_ps_mode = PS_MODE_ENABLE; + else if (ps_parm.ps_cmd == BT_PS_DISABLE) + psdata->target_ps_mode = PS_MODE_DISABLE; + psdata->c2h_ps_interval = __le16_to_cpu(ps_parm.c2h_ps_interval); + hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL); + goto free_skb; + } + break; + case HCI_NXP_WAKEUP_METHOD: + if (hdr->plen == sizeof(wakeup_parm)) { + memcpy(&wakeup_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen); + psdata->c2h_wakeupmode = wakeup_parm.c2h_wakeupmode; + psdata->c2h_wakeup_gpio = wakeup_parm.c2h_wakeup_gpio; + psdata->h2c_wakeup_gpio = wakeup_parm.h2c_wakeup_gpio; + switch (wakeup_parm.h2c_wakeupmode) { + case BT_CTRL_WAKEUP_METHOD_DSR: + psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR; + break; + case BT_CTRL_WAKEUP_METHOD_BREAK: + default: + psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK; + break; + } + hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL); + goto free_skb; + } + break; + case HCI_NXP_SET_OPER_SPEED: + if (hdr->plen == sizeof(baudrate_parm)) { + memcpy(&baudrate_parm, skb->data + HCI_COMMAND_HDR_SIZE, hdr->plen); + nxpdev->new_baudrate = __le32_to_cpu(baudrate_parm); + hci_cmd_sync_queue(hdev, nxp_set_baudrate_cmd, NULL, NULL); + goto free_skb; + } + break; + case HCI_NXP_IND_RESET: + if (hdr->plen == 1) { + hci_cmd_sync_queue(hdev, nxp_set_ind_reset, NULL, NULL); + goto free_skb; + } + break; + default: + break; + } + } + + return btnxpuart_queue_skb(hdev, skb); + +free_skb: + kfree_skb(skb); + return 0; +} + +static struct sk_buff *nxp_dequeue(void *data) +{ + struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)data; + + ps_wakeup(nxpdev); + ps_start_timer(nxpdev); + return skb_dequeue(&nxpdev->txq); +} + +/* btnxpuart based on serdev */ +static void btnxpuart_tx_work(struct work_struct *work) +{ + struct btnxpuart_dev *nxpdev = container_of(work, struct btnxpuart_dev, + tx_work); + struct serdev_device *serdev = nxpdev->serdev; + struct hci_dev *hdev = nxpdev->hdev; + struct sk_buff *skb; + int len; + + while ((skb = nxp_dequeue(nxpdev))) { + 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(&nxpdev->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); + } + clear_bit(BTNXPUART_TX_STATE_ACTIVE, &nxpdev->tx_state); +} + +static int btnxpuart_open(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + int err = 0; + + err = serdev_device_open(nxpdev->serdev); + if (err) { + bt_dev_err(hdev, "Unable to open UART device %s", + dev_name(&nxpdev->serdev->dev)); + } else { + set_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state); + } + return err; +} + +static int btnxpuart_close(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + ps_wakeup(nxpdev); + serdev_device_close(nxpdev->serdev); + clear_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state); + return 0; +} + +static int btnxpuart_flush(struct hci_dev *hdev) +{ + struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev); + + /* Flush any pending characters */ + serdev_device_write_flush(nxpdev->serdev); + skb_queue_purge(&nxpdev->txq); + + cancel_work_sync(&nxpdev->tx_work); + + kfree_skb(nxpdev->rx_skb); + nxpdev->rx_skb = NULL; + + return 0; +} + +static const struct h4_recv_pkt nxp_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, + { NXP_RECV_CHIP_VER_V1, .recv = nxp_recv_chip_ver_v1 }, + { NXP_RECV_FW_REQ_V1, .recv = nxp_recv_fw_req_v1 }, + { NXP_RECV_CHIP_VER_V3, .recv = nxp_recv_chip_ver_v3 }, + { NXP_RECV_FW_REQ_V3, .recv = nxp_recv_fw_req_v3 }, +}; + +static int btnxpuart_receive_buf(struct serdev_device *serdev, const u8 *data, + size_t count) +{ + struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); + + ps_start_timer(nxpdev); + + nxpdev->rx_skb = h4_recv_buf(nxpdev->hdev, nxpdev->rx_skb, data, count, + nxp_recv_pkts, ARRAY_SIZE(nxp_recv_pkts)); + if (IS_ERR(nxpdev->rx_skb)) { + int err = PTR_ERR(nxpdev->rx_skb); + /* Safe to ignore out-of-sync bootloader signatures */ + if (is_fw_downloading(nxpdev)) + return count; + bt_dev_err(nxpdev->hdev, "Frame reassembly failed (%d)", err); + nxpdev->rx_skb = NULL; + return err; + } + nxpdev->hdev->stat.byte_rx += count; + return count; +} + +static void btnxpuart_write_wakeup(struct serdev_device *serdev) +{ + serdev_device_write_wakeup(serdev); +} + +static const struct serdev_device_ops btnxpuart_client_ops = { + .receive_buf = btnxpuart_receive_buf, + .write_wakeup = btnxpuart_write_wakeup, +}; + +static int nxp_serdev_probe(struct serdev_device *serdev) +{ + struct hci_dev *hdev; + struct btnxpuart_dev *nxpdev; + + nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL); + if (!nxpdev) + return -ENOMEM; + + nxpdev->nxp_data = (struct btnxpuart_data *)device_get_match_data(&serdev->dev); + + nxpdev->serdev = serdev; + serdev_device_set_drvdata(serdev, nxpdev); + + serdev_device_set_client_ops(serdev, &btnxpuart_client_ops); + + INIT_WORK(&nxpdev->tx_work, btnxpuart_tx_work); + skb_queue_head_init(&nxpdev->txq); + + crc8_populate_msb(crc8_table, POLYNOMIAL8); + + /* Initialize and register HCI device */ + hdev = hci_alloc_dev(); + if (!hdev) { + dev_err(&serdev->dev, "Can't allocate HCI device\n"); + return -ENOMEM; + } + + nxpdev->hdev = hdev; + + hdev->bus = HCI_UART; + hci_set_drvdata(hdev, nxpdev); + + hdev->manufacturer = MANUFACTURER_NXP; + hdev->open = btnxpuart_open; + hdev->close = btnxpuart_close; + hdev->flush = btnxpuart_flush; + hdev->setup = nxp_setup; + hdev->send = nxp_enqueue; + SET_HCIDEV_DEV(hdev, &serdev->dev); + + if (hci_register_dev(hdev) < 0) { + dev_err(&serdev->dev, "Can't register HCI device\n"); + hci_free_dev(hdev); + return -ENODEV; + } + + ps_init_work(hdev); + ps_init_timer(hdev); + + return 0; +} + +static void nxp_serdev_remove(struct serdev_device *serdev) +{ + struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev); + struct hci_dev *hdev = nxpdev->hdev; + + /* Restore FW baudrate to fw_init_baudrate if changed. + * This will ensure FW baudrate is in sync with + * driver baudrate in case this driver is re-inserted. + */ + if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) { + nxpdev->new_baudrate = nxpdev->fw_init_baudrate; + nxp_set_baudrate_cmd(hdev, NULL); + } + + ps_cancel_timer(nxpdev); + hci_unregister_dev(hdev); + hci_free_dev(hdev); +} + +static struct btnxpuart_data w8987_data = { + .helper_fw_name = NULL, + .fw_name = FIRMWARE_W8987, +}; + +static struct btnxpuart_data w8997_data = { + .helper_fw_name = FIRMWARE_HELPER, + .fw_name = FIRMWARE_W8997, +}; + +static const struct of_device_id nxpuart_of_match_table[] = { + { .compatible = "nxp,88w8987-bt", .data = &w8987_data }, + { .compatible = "nxp,88w8997-bt", .data = &w8997_data }, + { } +}; +MODULE_DEVICE_TABLE(of, nxpuart_of_match_table); + +static struct serdev_device_driver nxp_serdev_driver = { + .probe = nxp_serdev_probe, + .remove = nxp_serdev_remove, + .driver = { + .name = "btnxpuart", + .of_match_table = of_match_ptr(nxpuart_of_match_table), + }, +}; + +module_serdev_device_driver(nxp_serdev_driver); + +MODULE_AUTHOR("Neeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>"); +MODULE_DESCRIPTION("NXP Bluetooth Serial driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index c9064d34d830..fd0941fe8608 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -614,6 +614,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, config.type = ELF_TYPE_PATCH; snprintf(config.fwname, sizeof(config.fwname), "qca/msbtfw%02x.mbn", rom_ver); + } else if (soc_type == QCA_WCN6855) { + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpbtfw%02x.tlv", rom_ver); } else { snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.bin", soc_ver); @@ -648,6 +651,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, else if (soc_type == QCA_WCN6750) snprintf(config.fwname, sizeof(config.fwname), "qca/msnv%02x.bin", rom_ver); + else if (soc_type == QCA_WCN6855) + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpnv%02x.bin", rom_ver); else snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin", soc_ver); @@ -685,11 +691,17 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, return err; } - if (soc_type == QCA_WCN3991 || soc_type == QCA_WCN6750) { + switch (soc_type) { + case QCA_WCN3991: + case QCA_WCN6750: + case QCA_WCN6855: /* get fw build info */ err = qca_read_fw_build_info(hdev); if (err < 0) return err; + break; + default: + break; } bt_dev_info(hdev, "QCA setup on UART is completed"); diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index 61e9a50e66ae..b884095bcd9d 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -147,6 +147,7 @@ enum qca_btsoc_type { QCA_WCN3991, QCA_QCA6390, QCA_WCN6750, + QCA_WCN6855, }; #if IS_ENABLED(CONFIG_BT_QCA) @@ -168,6 +169,10 @@ static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type) { return soc_type == QCA_WCN6750; } +static inline bool qca_is_wcn6855(enum qca_btsoc_type soc_type) +{ + return soc_type == QCA_WCN6855; +} #else @@ -206,6 +211,11 @@ static inline bool qca_is_wcn6750(enum qca_btsoc_type soc_type) return false; } +static inline bool qca_is_wcn6855(enum qca_btsoc_type soc_type) +{ + return false; +} + static inline int qca_send_pre_shutdown_cmd(struct hci_dev *hdev) { return -EOPNOTSUPP; diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 69c3fe649ca7..2915c82d719d 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -17,19 +17,26 @@ #define VERSION "0.1" +#define RTL_CHIP_8723CS_CG 3 +#define RTL_CHIP_8723CS_VF 4 +#define RTL_CHIP_8723CS_XX 5 #define RTL_EPATCH_SIGNATURE "Realtech" +#define RTL_EPATCH_SIGNATURE_V2 "RTBTCore" +#define RTL_ROM_LMP_8703B 0x8703 #define RTL_ROM_LMP_8723A 0x1200 #define RTL_ROM_LMP_8723B 0x8723 #define RTL_ROM_LMP_8821A 0x8821 #define RTL_ROM_LMP_8761A 0x8761 #define RTL_ROM_LMP_8822B 0x8822 #define RTL_ROM_LMP_8852A 0x8852 +#define RTL_ROM_LMP_8851B 0x8851 #define RTL_CONFIG_MAGIC 0x8723ab55 #define IC_MATCH_FL_LMPSUBV (1 << 0) #define IC_MATCH_FL_HCIREV (1 << 1) #define IC_MATCH_FL_HCIVER (1 << 2) #define IC_MATCH_FL_HCIBUS (1 << 3) +#define IC_MATCH_FL_CHIP_TYPE (1 << 4) #define IC_INFO(lmps, hcir, hciv, bus) \ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | \ IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS, \ @@ -38,6 +45,14 @@ .hci_ver = (hciv), \ .hci_bus = (bus) +#define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}}) +#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}}) +#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}}) + +#define RTL_PATCH_SNIPPETS 0x01 +#define RTL_PATCH_DUMMY_HEADER 0x02 +#define RTL_PATCH_SECURITY_HEADER 0x03 + enum btrtl_chip_id { CHIP_ID_8723A, CHIP_ID_8723B, @@ -51,6 +66,7 @@ enum btrtl_chip_id { CHIP_ID_8852A = 18, CHIP_ID_8852B = 20, CHIP_ID_8852C = 25, + CHIP_ID_8851B = 36, }; struct id_table { @@ -59,6 +75,7 @@ struct id_table { __u16 hci_rev; __u8 hci_ver; __u8 hci_bus; + __u8 chip_type; bool config_needed; bool has_rom_version; bool has_msft_ext; @@ -75,6 +92,8 @@ struct btrtl_device_info { int cfg_len; bool drop_fw; int project_id; + u8 key_id; + struct list_head patch_subsecs; }; static const struct id_table ic_id_table[] = { @@ -99,6 +118,39 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8723b_fw.bin", .cfg_name = "rtl_bt/rtl8723b_config" }, + /* 8723CS-CG */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_CG, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_cg_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_cg_config" }, + + /* 8723CS-VF */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_VF, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_vf_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_vf_config" }, + + /* 8723CS-XX */ + { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_CHIP_TYPE | + IC_MATCH_FL_HCIBUS, + .lmp_subver = RTL_ROM_LMP_8703B, + .chip_type = RTL_CHIP_8723CS_XX, + .hci_bus = HCI_UART, + .config_needed = true, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8723cs_xx_fw.bin", + .cfg_name = "rtl_bt/rtl8723cs_xx_config" }, + /* 8723D */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd, 0x8, HCI_USB), .config_needed = true, @@ -128,6 +180,14 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8821c_fw.bin", .cfg_name = "rtl_bt/rtl8821c_config" }, + /* 8821CS */ + { IC_INFO(RTL_ROM_LMP_8821A, 0xc, 0x8, HCI_UART), + .config_needed = true, + .has_rom_version = true, + .has_msft_ext = true, + .fw_name = "rtl_bt/rtl8821cs_fw.bin", + .cfg_name = "rtl_bt/rtl8821cs_config" }, + /* 8761A */ { IC_INFO(RTL_ROM_LMP_8761A, 0xa, 0x6, HCI_USB), .config_needed = false, @@ -190,6 +250,14 @@ static const struct id_table ic_id_table[] = { .fw_name = "rtl_bt/rtl8852au_fw.bin", .cfg_name = "rtl_bt/rtl8852au_config" }, + /* 8852B with UART interface */ + { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_UART), + .config_needed = true, + .has_rom_version = true, + .has_msft_ext = true, + .fw_name = "rtl_bt/rtl8852bs_fw.bin", + .cfg_name = "rtl_bt/rtl8852bs_config" }, + /* 8852B */ { IC_INFO(RTL_ROM_LMP_8852A, 0xb, 0xb, HCI_USB), .config_needed = false, @@ -205,10 +273,19 @@ static const struct id_table ic_id_table[] = { .has_msft_ext = true, .fw_name = "rtl_bt/rtl8852cu_fw.bin", .cfg_name = "rtl_bt/rtl8852cu_config" }, + + /* 8851B */ + { IC_INFO(RTL_ROM_LMP_8851B, 0xb, 0xc, HCI_USB), + .config_needed = false, + .has_rom_version = true, + .has_msft_ext = false, + .fw_name = "rtl_bt/rtl8851bu_fw.bin", + .cfg_name = "rtl_bt/rtl8851bu_config" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, - u8 hci_ver, u8 hci_bus) + u8 hci_ver, u8 hci_bus, + u8 chip_type) { int i; @@ -225,6 +302,9 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && (ic_id_table[i].hci_bus != hci_bus)) continue; + if ((ic_id_table[i].match_flags & IC_MATCH_FL_CHIP_TYPE) && + (ic_id_table[i].chip_type != chip_type)) + continue; break; } @@ -284,6 +364,227 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) return 0; } +static int btrtl_vendor_read_reg16(struct hci_dev *hdev, + struct rtl_vendor_cmd *cmd, u8 *rp) +{ + struct sk_buff *skb; + int err = 0; + + skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + rtl_dev_err(hdev, "RTL: Read reg16 failed (%d)", err); + return err; + } + + if (skb->len != 3 || skb->data[0]) { + bt_dev_err(hdev, "RTL: Read reg16 length mismatch"); + kfree_skb(skb); + return -EIO; + } + + if (rp) + memcpy(rp, skb->data + 1, 2); + + kfree_skb(skb); + + return 0; +} + +static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len) +{ + void *data = iov->data; + + if (iov->len < len) + return NULL; + + iov->data += len; + iov->len -= len; + + return data; +} + +static void btrtl_insert_ordered_subsec(struct rtl_subsection *node, + struct btrtl_device_info *btrtl_dev) +{ + struct list_head *pos; + struct list_head *next; + struct rtl_subsection *subsec; + + list_for_each_safe(pos, next, &btrtl_dev->patch_subsecs) { + subsec = list_entry(pos, struct rtl_subsection, list); + if (subsec->prio >= node->prio) + break; + } + __list_add(&node->list, pos->prev, pos); +} + +static int btrtl_parse_section(struct hci_dev *hdev, + struct btrtl_device_info *btrtl_dev, u32 opcode, + u8 *data, u32 len) +{ + struct rtl_section_hdr *hdr; + struct rtl_subsection *subsec; + struct rtl_common_subsec *common_subsec; + struct rtl_sec_hdr *sec_hdr; + int i; + u8 *ptr; + u16 num_subsecs; + u32 subsec_len; + int rc = 0; + struct rtl_iovec iov = { + .data = data, + .len = len, + }; + + hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); + if (!hdr) + return -EINVAL; + num_subsecs = le16_to_cpu(hdr->num); + + for (i = 0; i < num_subsecs; i++) { + common_subsec = rtl_iov_pull_data(&iov, sizeof(*common_subsec)); + if (!common_subsec) + break; + subsec_len = le32_to_cpu(common_subsec->len); + + rtl_dev_dbg(hdev, "subsec, eco 0x%02x, len %08x", + common_subsec->eco, subsec_len); + + ptr = rtl_iov_pull_data(&iov, subsec_len); + if (!ptr) + break; + + if (common_subsec->eco != btrtl_dev->rom_version + 1) + continue; + + switch (opcode) { + case RTL_PATCH_SECURITY_HEADER: + sec_hdr = (void *)common_subsec; + if (sec_hdr->key_id != btrtl_dev->key_id) + continue; + break; + } + + subsec = kzalloc(sizeof(*subsec), GFP_KERNEL); + if (!subsec) + return -ENOMEM; + subsec->opcode = opcode; + subsec->prio = common_subsec->prio; + subsec->len = subsec_len; + subsec->data = ptr; + btrtl_insert_ordered_subsec(subsec, btrtl_dev); + rc += subsec_len; + } + + return rc; +} + +static int rtlbt_parse_firmware_v2(struct hci_dev *hdev, + struct btrtl_device_info *btrtl_dev, + unsigned char **_buf) +{ + struct rtl_epatch_header_v2 *hdr; + int rc; + u8 reg_val[2]; + u8 key_id; + u32 num_sections; + struct rtl_section *section; + struct rtl_subsection *entry, *tmp; + u32 section_len; + u32 opcode; + int len = 0; + int i; + u8 *ptr; + struct rtl_iovec iov = { + .data = btrtl_dev->fw_data, + .len = btrtl_dev->fw_len - 7, /* Cut the tail */ + }; + + rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val); + if (rc < 0) + return -EIO; + key_id = reg_val[0]; + + rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id); + + btrtl_dev->key_id = key_id; + + hdr = rtl_iov_pull_data(&iov, sizeof(*hdr)); + if (!hdr) + return -EINVAL; + num_sections = le32_to_cpu(hdr->num_sections); + + rtl_dev_dbg(hdev, "FW version %08x-%08x", *((u32 *)hdr->fw_version), + *((u32 *)(hdr->fw_version + 4))); + + for (i = 0; i < num_sections; i++) { + section = rtl_iov_pull_data(&iov, sizeof(*section)); + if (!section) + break; + section_len = le32_to_cpu(section->len); + opcode = le32_to_cpu(section->opcode); + + rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode); + + ptr = rtl_iov_pull_data(&iov, section_len); + if (!ptr) + break; + + switch (opcode) { + case RTL_PATCH_SNIPPETS: + rc = btrtl_parse_section(hdev, btrtl_dev, opcode, + ptr, section_len); + break; + case RTL_PATCH_SECURITY_HEADER: + /* If key_id from chip is zero, ignore all security + * headers. + */ + if (!key_id) + break; + rc = btrtl_parse_section(hdev, btrtl_dev, opcode, + ptr, section_len); + break; + case RTL_PATCH_DUMMY_HEADER: + rc = btrtl_parse_section(hdev, btrtl_dev, opcode, + ptr, section_len); + break; + default: + rc = 0; + break; + } + if (rc < 0) { + rtl_dev_err(hdev, "RTL: Parse section (%u) err %d", + opcode, rc); + return rc; + } + len += rc; + } + + if (!len) + return -ENODATA; + + /* Allocate mem and copy all found subsecs. */ + ptr = kvmalloc(len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + len = 0; + list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) { + rtl_dev_dbg(hdev, "RTL: opcode %08x, addr %p, len 0x%x", + entry->opcode, entry->data, entry->len); + memcpy(ptr + len, entry->data, entry->len); + len += entry->len; + } + + if (!len) + return -EPERM; + + *_buf = ptr; + return len; +} + static int rtlbt_parse_firmware(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev, unsigned char **_buf) @@ -307,6 +608,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8723B, 1 }, { RTL_ROM_LMP_8821A, 2 }, { RTL_ROM_LMP_8761A, 3 }, + { RTL_ROM_LMP_8703B, 7 }, { RTL_ROM_LMP_8822B, 8 }, { RTL_ROM_LMP_8723B, 9 }, /* 8723D */ { RTL_ROM_LMP_8821A, 10 }, /* 8821C */ @@ -315,9 +617,21 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8852A, 18 }, /* 8852A */ { RTL_ROM_LMP_8852A, 20 }, /* 8852B */ { RTL_ROM_LMP_8852A, 25 }, /* 8852C */ + { RTL_ROM_LMP_8851B, 36 }, /* 8851B */ }; - min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3; + if (btrtl_dev->fw_len <= 8) + return -EINVAL; + + if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) + min_size = sizeof(struct rtl_epatch_header) + + sizeof(extension_sig) + 3; + else if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8)) + min_size = sizeof(struct rtl_epatch_header_v2) + + sizeof(extension_sig) + 3; + else + return -EINVAL; + if (btrtl_dev->fw_len < min_size) return -EINVAL; @@ -382,12 +696,14 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, return -EINVAL; } - epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data; - if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) { + if (memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8) != 0) { + if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V2, 8)) + return rtlbt_parse_firmware_v2(hdev, btrtl_dev, _buf); rtl_dev_err(hdev, "bad EPATCH signature"); return -EINVAL; } + epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data; num_patches = le16_to_cpu(epatch_info->num_patches); BT_DBG("fw_version=%x, num_patches=%d", le32_to_cpu(epatch_info->fw_version), num_patches); @@ -451,6 +767,7 @@ static int rtl_download_firmware(struct hci_dev *hdev, int frag_len = RTL_FRAG_LEN; int ret = 0; int i; + int j = 0; struct sk_buff *skb; struct hci_rp_read_local_version *rp; @@ -461,17 +778,16 @@ static int rtl_download_firmware(struct hci_dev *hdev, for (i = 0; i < frag_num; i++) { struct sk_buff *skb; - BT_DBG("download fw (%d/%d)", i, frag_num); - - if (i > 0x7f) - dl_cmd->index = (i & 0x7f) + 1; - else - dl_cmd->index = i; + dl_cmd->index = j++; + if (dl_cmd->index == 0x7f) + j = 1; if (i == (frag_num - 1)) { dl_cmd->index |= 0x80; /* data end */ frag_len = fw_len % RTL_FRAG_LEN; } + rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i, + frag_num, dl_cmd->index); memcpy(dl_cmd->data, data, frag_len); /* Send download command */ @@ -587,10 +903,60 @@ out: return ret; } +static bool rtl_has_chip_type(u16 lmp_subver) +{ + switch (lmp_subver) { + case RTL_ROM_LMP_8703B: + return true; + default: + break; + } + + return false; +} + +static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type) +{ + struct rtl_chip_type_evt *chip_type; + struct sk_buff *skb; + const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0}; + + /* Read RTL chip type command */ + skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + rtl_dev_err(hdev, "Read chip type failed (%ld)", + PTR_ERR(skb)); + return PTR_ERR(skb); + } + + chip_type = skb_pull_data(skb, sizeof(*chip_type)); + if (!chip_type) { + rtl_dev_err(hdev, "RTL chip type event length mismatch"); + kfree_skb(skb); + return -EIO; + } + + rtl_dev_info(hdev, "chip_type status=%x type=%x", + chip_type->status, chip_type->type); + + *type = chip_type->type & 0x0f; + + kfree_skb(skb); + return 0; +} + void btrtl_free(struct btrtl_device_info *btrtl_dev) { + struct rtl_subsection *entry, *tmp; + kvfree(btrtl_dev->fw_data); kvfree(btrtl_dev->cfg_data); + + list_for_each_entry_safe(entry, tmp, &btrtl_dev->patch_subsecs, list) { + list_del(&entry->list); + kfree(entry); + } + kfree(btrtl_dev); } EXPORT_SYMBOL_GPL(btrtl_free); @@ -603,10 +969,11 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, struct hci_rp_read_local_version *resp; char cfg_name[40]; u16 hci_rev, lmp_subver; - u8 hci_ver; + u8 hci_ver, lmp_ver, chip_type = 0; int ret; u16 opcode; u8 cmd[2]; + u8 reg_val[2]; btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL); if (!btrtl_dev) { @@ -614,6 +981,31 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, goto err_alloc; } + INIT_LIST_HEAD(&btrtl_dev->patch_subsecs); + +check_version: + ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val); + if (ret < 0) + goto err_free; + lmp_subver = get_unaligned_le16(reg_val); + + if (lmp_subver == RTL_ROM_LMP_8822B) { + ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_REV, reg_val); + if (ret < 0) + goto err_free; + hci_rev = get_unaligned_le16(reg_val); + + /* 8822E */ + if (hci_rev == 0x000e) { + hci_ver = 0x0c; + lmp_ver = 0x0c; + btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, + hci_ver, hdev->bus, + chip_type); + goto next; + } + } + skb = btrtl_read_local_version(hdev); if (IS_ERR(skb)) { ret = PTR_ERR(skb); @@ -621,19 +1013,32 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, } resp = (struct hci_rp_read_local_version *)skb->data; - rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", - resp->hci_ver, resp->hci_rev, - resp->lmp_ver, resp->lmp_subver); - hci_ver = resp->hci_ver; - hci_rev = le16_to_cpu(resp->hci_rev); + hci_ver = resp->hci_ver; + hci_rev = le16_to_cpu(resp->hci_rev); + lmp_ver = resp->lmp_ver; lmp_subver = le16_to_cpu(resp->lmp_subver); + kfree_skb(skb); + + if (rtl_has_chip_type(lmp_subver)) { + ret = rtl_read_chip_type(hdev, &chip_type); + if (ret) + goto err_free; + } + btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus); + hdev->bus, chip_type); - if (!btrtl_dev->ic_info) +next: + rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", + hci_ver, hci_rev, + lmp_ver, lmp_subver); + + if (!btrtl_dev->ic_info && !btrtl_dev->drop_fw) btrtl_dev->drop_fw = true; + else + btrtl_dev->drop_fw = false; if (btrtl_dev->drop_fw) { opcode = hci_opcode_pack(0x3f, 0x66); @@ -642,41 +1047,25 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL); if (!skb) - goto out_free; + goto err_free; skb_put_data(skb, cmd, sizeof(cmd)); hci_skb_pkt_type(skb) = HCI_COMMAND_PKT; - hdev->send(hdev, skb); + ret = hdev->send(hdev, skb); + if (ret < 0) { + bt_dev_err(hdev, "sending frame failed (%d)", ret); + kfree_skb(skb); + goto err_free; + } /* Ensure the above vendor command is sent to controller and * process has done. */ msleep(200); - /* Read the local version again. Expect to have the vanilla - * version as cold boot. - */ - skb = btrtl_read_local_version(hdev); - if (IS_ERR(skb)) { - ret = PTR_ERR(skb); - goto err_free; - } - - resp = (struct hci_rp_read_local_version *)skb->data; - rtl_dev_info(hdev, "examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x", - resp->hci_ver, resp->hci_rev, - resp->lmp_ver, resp->lmp_subver); - - hci_ver = resp->hci_ver; - hci_rev = le16_to_cpu(resp->hci_rev); - lmp_subver = le16_to_cpu(resp->lmp_subver); - - btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver, - hdev->bus); + goto check_version; } -out_free: - kfree_skb(skb); if (!btrtl_dev->ic_info) { rtl_dev_info(hdev, "unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x", @@ -755,6 +1144,8 @@ int btrtl_download_firmware(struct hci_dev *hdev, case RTL_ROM_LMP_8761A: case RTL_ROM_LMP_8822B: case RTL_ROM_LMP_8852A: + case RTL_ROM_LMP_8703B: + case RTL_ROM_LMP_8851B: return btrtl_setup_rtl8723b(hdev, btrtl_dev); default: rtl_dev_info(hdev, "assuming no firmware upload needed"); @@ -779,6 +1170,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) case CHIP_ID_8852A: case CHIP_ID_8852B: case CHIP_ID_8852C: + case CHIP_ID_8851B: set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks); set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); @@ -795,6 +1187,22 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) rtl_dev_dbg(hdev, "WBS supported not enabled."); break; } + + if (!btrtl_dev->ic_info) + return; + + switch (btrtl_dev->ic_info->lmp_subver) { + case RTL_ROM_LMP_8703B: + /* 8723CS reports two pages for local ext features, + * but it doesn't support any features from page 2 - + * it either responds with garbage or with error status + */ + set_bit(HCI_QUIRK_BROKEN_LOCAL_EXT_FEATURES_PAGE_2, + &hdev->quirks); + break; + default: + break; + } } EXPORT_SYMBOL_GPL(btrtl_set_quirks); @@ -953,6 +1361,12 @@ MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_cg_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_vf_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8723cs_xx_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin"); @@ -963,7 +1377,11 @@ MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852au_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852au_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8852bs_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8852bs_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852bu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852bu_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8852cu_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8851bu_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8851bu_config.bin"); diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index ebf0101c959b..adb4c2c9abc5 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -14,6 +14,11 @@ struct btrtl_device_info; +struct rtl_chip_type_evt { + __u8 status; + __u8 type; +} __packed; + struct rtl_download_cmd { __u8 index; __u8 data[RTL_FRAG_LEN]; @@ -44,7 +49,58 @@ struct rtl_vendor_config_entry { struct rtl_vendor_config { __le32 signature; __le16 total_len; - struct rtl_vendor_config_entry entry[]; + __u8 entry[]; +} __packed; + +struct rtl_epatch_header_v2 { + __u8 signature[8]; + __u8 fw_version[8]; + __le32 num_sections; +} __packed; + +struct rtl_section { + __le32 opcode; + __le32 len; + u8 data[]; +} __packed; + +struct rtl_section_hdr { + __le16 num; + __le16 reserved; +} __packed; + +struct rtl_common_subsec { + __u8 eco; + __u8 prio; + __u8 cb[2]; + __le32 len; + __u8 data[]; +}; + +struct rtl_sec_hdr { + __u8 eco; + __u8 prio; + __u8 key_id; + __u8 reserved; + __le32 len; + __u8 data[]; +} __packed; + +struct rtl_subsection { + struct list_head list; + u32 opcode; + u32 len; + u8 prio; + u8 *data; +}; + +struct rtl_iovec { + u8 *data; + u32 len; +}; + +struct rtl_vendor_cmd { + __u8 param[5]; } __packed; enum { diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 51000320e1ea..f19d31ee37ea 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -354,7 +354,6 @@ static void btsdio_remove(struct sdio_func *func) BT_DBG("func %p", func); - cancel_work_sync(&data->work); if (!data) return; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 5c536151ef83..2a8e2bb038f5 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -540,6 +540,10 @@ static const struct usb_device_id blacklist_table[] = { /* Realtek 8852BE Bluetooth devices */ { USB_DEVICE(0x0cb8, 0xc559), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0bda, 0x887b), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3571), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek Bluetooth devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01), @@ -558,6 +562,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x043e, 0x310c), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x04ca, 0x3801), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, /* Additional MediaTek MT7668 Bluetooth devices */ { USB_DEVICE(0x043e, 0x3109), .driver_info = BTUSB_MEDIATEK | @@ -612,6 +619,9 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0489, 0xe0e2), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, + { USB_DEVICE(0x0489, 0xe0e4), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH | + BTUSB_VALID_LE_STATES }, { USB_DEVICE(0x0489, 0xe0f2), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH | BTUSB_VALID_LE_STATES }, @@ -723,6 +733,16 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = { {} }; +struct qca_dump_info { + /* fields for dump collection */ + u16 id_vendor; + u16 id_product; + u32 fw_version; + u32 controller_id; + u32 ram_dump_size; + u16 ram_dump_seqno; +}; + #define BTUSB_MAX_ISOC_FRAMES 10 #define BTUSB_INTR_RUNNING 0 @@ -742,6 +762,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = { #define BTUSB_WAKEUP_AUTOSUSPEND 14 #define BTUSB_USE_ALT3_FOR_WBS 15 #define BTUSB_ALT6_CONTINUOUS_TX 16 +#define BTUSB_HW_SSR_ACTIVE 17 struct btusb_data { struct hci_dev *hdev; @@ -804,6 +825,8 @@ struct btusb_data { int oob_wake_irq; /* irq for out-of-band wake-on-bt */ unsigned cmd_timeout_cnt; + + struct qca_dump_info qca_dump; }; static void btusb_reset(struct hci_dev *hdev) @@ -894,6 +917,11 @@ static void btusb_qca_cmd_timeout(struct hci_dev *hdev) struct btusb_data *data = hci_get_drvdata(hdev); struct gpio_desc *reset_gpio = data->reset_gpio; + if (test_bit(BTUSB_HW_SSR_ACTIVE, &data->flags)) { + bt_dev_info(hdev, "Ramdump in progress, defer cmd_timeout"); + return; + } + if (++data->cmd_timeout_cnt < 5) return; @@ -2376,16 +2404,47 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer, return btusb_recv_bulk(data, buffer, count); } +static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct intel_tlv *tlv = (void *)&skb->data[5]; + + /* The first event is always an event type TLV */ + if (tlv->type != INTEL_TLV_TYPE_ID) + goto recv_frame; + + switch (tlv->val[0]) { + case INTEL_TLV_SYSTEM_EXCEPTION: + case INTEL_TLV_FATAL_EXCEPTION: + case INTEL_TLV_DEBUG_EXCEPTION: + case INTEL_TLV_TEST_EXCEPTION: + /* Generate devcoredump from exception */ + if (!hci_devcd_init(hdev, skb->len)) { + hci_devcd_append(hdev, skb); + hci_devcd_complete(hdev); + } else { + bt_dev_err(hdev, "Failed to generate devcoredump"); + kfree_skb(skb); + } + return 0; + default: + bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]); + } + +recv_frame: + return hci_recv_frame(hdev, skb); +} + static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) { - if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { - struct hci_event_hdr *hdr = (void *)skb->data; + struct hci_event_hdr *hdr = (void *)skb->data; + const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 }; - if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && - hdr->plen > 0) { - const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; - unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff && + hdr->plen > 0) { + const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1; + unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1; + if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) { switch (skb->data[2]) { case 0x02: /* When switching to the operational firmware @@ -2404,6 +2463,15 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) break; } } + + /* Handle all diagnostics events separately. May still call + * hci_recv_frame. + */ + if (len >= sizeof(diagnostics_hdr) && + memcmp(&skb->data[2], diagnostics_hdr, + sizeof(diagnostics_hdr)) == 0) { + return btusb_intel_diagnostics(hdev, skb); + } } return hci_recv_frame(hdev, skb); @@ -3244,6 +3312,202 @@ static int btusb_set_bdaddr_wcn6855(struct hci_dev *hdev, return 0; } +#define QCA_MEMDUMP_ACL_HANDLE 0x2EDD +#define QCA_MEMDUMP_SIZE_MAX 0x100000 +#define QCA_MEMDUMP_VSE_CLASS 0x01 +#define QCA_MEMDUMP_MSG_TYPE 0x08 +#define QCA_MEMDUMP_PKT_SIZE 248 +#define QCA_LAST_SEQUENCE_NUM 0xffff + +struct qca_dump_hdr { + u8 vse_class; + u8 msg_type; + __le16 seqno; + u8 reserved; + union { + u8 data[0]; + struct { + __le32 ram_dump_size; + u8 data0[0]; + } __packed; + }; +} __packed; + + +static void btusb_dump_hdr_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + char buf[128]; + struct btusb_data *btdata = hci_get_drvdata(hdev); + + snprintf(buf, sizeof(buf), "Controller Name: 0x%x\n", + btdata->qca_dump.controller_id); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: 0x%x\n", + btdata->qca_dump.fw_version); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: %s\nVendor: qca\n", + btusb_driver.name); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "VID: 0x%x\nPID:0x%x\n", + btdata->qca_dump.id_vendor, btdata->qca_dump.id_product); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Lmp Subversion: 0x%x\n", + hdev->lmp_subver); + skb_put_data(skb, buf, strlen(buf)); +} + +static void btusb_coredump_qca(struct hci_dev *hdev) +{ + static const u8 param[] = { 0x26 }; + struct sk_buff *skb; + + skb = __hci_cmd_sync(hdev, 0xfc0c, 1, param, HCI_CMD_TIMEOUT); + if (IS_ERR(skb)) + bt_dev_err(hdev, "%s: triggle crash failed (%ld)", __func__, PTR_ERR(skb)); + kfree_skb(skb); +} + +/* + * ==0: not a dump pkt. + * < 0: fails to handle a dump pkt + * > 0: otherwise. + */ +static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + int ret = 1; + u8 pkt_type; + u8 *sk_ptr; + unsigned int sk_len; + u16 seqno; + u32 dump_size; + + struct hci_event_hdr *event_hdr; + struct hci_acl_hdr *acl_hdr; + struct qca_dump_hdr *dump_hdr; + struct btusb_data *btdata = hci_get_drvdata(hdev); + struct usb_device *udev = btdata->udev; + + pkt_type = hci_skb_pkt_type(skb); + sk_ptr = skb->data; + sk_len = skb->len; + + if (pkt_type == HCI_ACLDATA_PKT) { + acl_hdr = hci_acl_hdr(skb); + if (le16_to_cpu(acl_hdr->handle) != QCA_MEMDUMP_ACL_HANDLE) + return 0; + sk_ptr += HCI_ACL_HDR_SIZE; + sk_len -= HCI_ACL_HDR_SIZE; + event_hdr = (struct hci_event_hdr *)sk_ptr; + } else { + event_hdr = hci_event_hdr(skb); + } + + if ((event_hdr->evt != HCI_VENDOR_PKT) + || (event_hdr->plen != (sk_len - HCI_EVENT_HDR_SIZE))) + return 0; + + sk_ptr += HCI_EVENT_HDR_SIZE; + sk_len -= HCI_EVENT_HDR_SIZE; + + dump_hdr = (struct qca_dump_hdr *)sk_ptr; + if ((sk_len < offsetof(struct qca_dump_hdr, data)) + || (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) + || (dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE)) + return 0; + + /*it is dump pkt now*/ + seqno = le16_to_cpu(dump_hdr->seqno); + if (seqno == 0) { + set_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); + dump_size = le32_to_cpu(dump_hdr->ram_dump_size); + if (!dump_size || (dump_size > QCA_MEMDUMP_SIZE_MAX)) { + ret = -EILSEQ; + bt_dev_err(hdev, "Invalid memdump size(%u)", + dump_size); + goto out; + } + + ret = hci_devcd_init(hdev, dump_size); + if (ret < 0) { + bt_dev_err(hdev, "memdump init error(%d)", ret); + goto out; + } + + btdata->qca_dump.ram_dump_size = dump_size; + btdata->qca_dump.ram_dump_seqno = 0; + sk_ptr += offsetof(struct qca_dump_hdr, data0); + sk_len -= offsetof(struct qca_dump_hdr, data0); + + usb_disable_autosuspend(udev); + bt_dev_info(hdev, "%s memdump size(%u)\n", + (pkt_type == HCI_ACLDATA_PKT) ? "ACL" : "event", + dump_size); + } else { + sk_ptr += offsetof(struct qca_dump_hdr, data); + sk_len -= offsetof(struct qca_dump_hdr, data); + } + + if (!btdata->qca_dump.ram_dump_size) { + ret = -EINVAL; + bt_dev_err(hdev, "memdump is not active"); + goto out; + } + + if ((seqno > btdata->qca_dump.ram_dump_seqno + 1) && (seqno != QCA_LAST_SEQUENCE_NUM)) { + dump_size = QCA_MEMDUMP_PKT_SIZE * (seqno - btdata->qca_dump.ram_dump_seqno - 1); + hci_devcd_append_pattern(hdev, 0x0, dump_size); + bt_dev_err(hdev, + "expected memdump seqno(%u) is not received(%u)\n", + btdata->qca_dump.ram_dump_seqno, seqno); + btdata->qca_dump.ram_dump_seqno = seqno; + kfree_skb(skb); + return ret; + } + + skb_pull(skb, skb->len - sk_len); + hci_devcd_append(hdev, skb); + btdata->qca_dump.ram_dump_seqno++; + if (seqno == QCA_LAST_SEQUENCE_NUM) { + bt_dev_info(hdev, + "memdump done: pkts(%u), total(%u)\n", + btdata->qca_dump.ram_dump_seqno, btdata->qca_dump.ram_dump_size); + + hci_devcd_complete(hdev); + goto out; + } + return ret; + +out: + if (btdata->qca_dump.ram_dump_size) + usb_enable_autosuspend(udev); + btdata->qca_dump.ram_dump_size = 0; + btdata->qca_dump.ram_dump_seqno = 0; + clear_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags); + + if (ret < 0) + kfree_skb(skb); + return ret; +} + +static int btusb_recv_acl_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + if (handle_dump_pkt_qca(hdev, skb)) + return 0; + return hci_recv_frame(hdev, skb); +} + +static int btusb_recv_evt_qca(struct hci_dev *hdev, struct sk_buff *skb) +{ + if (handle_dump_pkt_qca(hdev, skb)) + return 0; + return hci_recv_frame(hdev, skb); +} + + #define QCA_DFU_PACKET_LEN 4096 #define QCA_GET_TARGET_VERSION 0x09 @@ -3578,6 +3842,9 @@ static int btusb_setup_qca(struct hci_dev *hdev) if (err < 0) return err; + btdata->qca_dump.fw_version = le32_to_cpu(ver.patch_version); + btdata->qca_dump.controller_id = le32_to_cpu(ver.rom_version); + if (!(status & QCA_SYSCFG_UPDATED)) { err = btusb_setup_qca_load_nvm(hdev, &ver, info); if (err < 0) @@ -3831,13 +4098,9 @@ static int btusb_probe(struct usb_interface *intf, BT_DBG("intf %p id %p", intf, id); - /* interface numbers are hardcoded in the spec */ - if (intf->cur_altsetting->desc.bInterfaceNumber != 0) { - if (!(id->driver_info & BTUSB_IFNUM_2)) - return -ENODEV; - if (intf->cur_altsetting->desc.bInterfaceNumber != 2) - return -ENODEV; - } + if ((id->driver_info & BTUSB_IFNUM_2) && + (intf->cur_altsetting->desc.bInterfaceNumber != 2)) + return -ENODEV; ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber; @@ -4012,7 +4275,7 @@ static int btusb_probe(struct usb_interface *intf, /* Combined Intel Device setup to support multiple setup routine */ if (id->driver_info & BTUSB_INTEL_COMBINED) { - err = btintel_configure_setup(hdev); + err = btintel_configure_setup(hdev, btusb_driver.name); if (err) goto out_free_dev; @@ -4071,6 +4334,11 @@ static int btusb_probe(struct usb_interface *intf, } if (id->driver_info & BTUSB_QCA_WCN6855) { + data->qca_dump.id_vendor = id->idVendor; + data->qca_dump.id_product = id->idProduct; + data->recv_event = btusb_recv_evt_qca; + data->recv_acl = btusb_recv_acl_qca; + hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL); data->setup_on_usb = btusb_setup_qca; hdev->shutdown = btusb_shutdown_qca; hdev->set_bdaddr = btusb_set_bdaddr_wcn6855; @@ -4102,6 +4370,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_ACTIONS_SEMI) { /* Support is advertised, but not implemented */ set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_READ_TRANSMIT_POWER, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_SET_RPA_TIMEOUT, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_EXT_SCAN, &hdev->quirks); } if (!reset) @@ -4389,6 +4660,17 @@ done: } #endif +#ifdef CONFIG_DEV_COREDUMP +static void btusb_coredump(struct device *dev) +{ + struct btusb_data *data = dev_get_drvdata(dev); + struct hci_dev *hdev = data->hdev; + + if (hdev->dump.coredump) + hdev->dump.coredump(hdev); +} +#endif + static struct usb_driver btusb_driver = { .name = "btusb", .probe = btusb_probe, @@ -4400,6 +4682,14 @@ static struct usb_driver btusb_driver = { .id_table = btusb_table, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, + +#ifdef CONFIG_DEV_COREDUMP + .drvwrap = { + .driver = { + .coredump = btusb_coredump, + }, + }, +#endif }; module_usb_driver(btusb_driver); diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index 2b6c0e1922cb..83bf5d4330c4 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -55,12 +55,14 @@ * @drive_rts_on_open: drive RTS signal on ->open() when platform requires it * @no_uart_clock_set: UART clock set command for >3Mbps mode is unavailable * @max_autobaud_speed: max baudrate supported by device in autobaud mode + * @max_speed: max baudrate supported */ struct bcm_device_data { bool no_early_set_baudrate; bool drive_rts_on_open; bool no_uart_clock_set; u32 max_autobaud_speed; + u32 max_speed; }; /** @@ -888,7 +890,7 @@ unlock: #endif /* Some firmware reports an IRQ which does not work (wrong pin in fw table?) */ -static struct gpiod_lookup_table asus_tf103c_irq_gpios = { +static struct gpiod_lookup_table irq_on_int33fc02_pin17_gpios = { .dev_id = "serial0-0", .table = { GPIO_LOOKUP("INT33FC:02", 17, "host-wakeup-alt", GPIO_ACTIVE_HIGH), @@ -898,12 +900,31 @@ static struct gpiod_lookup_table asus_tf103c_irq_gpios = { static const struct dmi_system_id bcm_broken_irq_dmi_table[] = { { + .ident = "Acer Iconia One 7 B1-750", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "VESPA2"), + }, + .driver_data = &irq_on_int33fc02_pin17_gpios, + }, + { .ident = "Asus TF103C", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_PRODUCT_NAME, "TF103C"), }, - .driver_data = &asus_tf103c_irq_gpios, + .driver_data = &irq_on_int33fc02_pin17_gpios, + }, + { + .ident = "Lenovo Yoga Tablet 2 830F/L / 1050F/L", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), + /* Partial match on beginning of BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), + }, + .driver_data = &irq_on_int33fc02_pin17_gpios, }, { .ident = "Meegopad T08", @@ -1300,6 +1321,12 @@ static const struct hci_uart_proto bcm_proto = { }; #ifdef CONFIG_ACPI + +/* bcm43430a0/a1 BT does not support 48MHz UART clock, limit to 2000000 baud */ +static struct bcm_device_data bcm43430_device_data = { + .max_speed = 2000000, +}; + static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E00" }, { "BCM2E01" }, @@ -1414,19 +1441,19 @@ static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E71" }, { "BCM2E72" }, { "BCM2E73" }, - { "BCM2E74" }, - { "BCM2E75" }, + { "BCM2E74", (long)&bcm43430_device_data }, + { "BCM2E75", (long)&bcm43430_device_data }, { "BCM2E76" }, { "BCM2E77" }, { "BCM2E78" }, { "BCM2E79" }, { "BCM2E7A" }, - { "BCM2E7B" }, + { "BCM2E7B", (long)&bcm43430_device_data }, { "BCM2E7C" }, { "BCM2E7D" }, { "BCM2E7E" }, { "BCM2E7F" }, - { "BCM2E80" }, + { "BCM2E80", (long)&bcm43430_device_data }, { "BCM2E81" }, { "BCM2E82" }, { "BCM2E83" }, @@ -1435,7 +1462,7 @@ static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E86" }, { "BCM2E87" }, { "BCM2E88" }, - { "BCM2E89" }, + { "BCM2E89", (long)&bcm43430_device_data }, { "BCM2E8A" }, { "BCM2E8B" }, { "BCM2E8C" }, @@ -1444,29 +1471,30 @@ static const struct acpi_device_id bcm_acpi_match[] = { { "BCM2E90" }, { "BCM2E92" }, { "BCM2E93" }, - { "BCM2E94" }, + { "BCM2E94", (long)&bcm43430_device_data }, { "BCM2E95" }, { "BCM2E96" }, { "BCM2E97" }, { "BCM2E98" }, - { "BCM2E99" }, + { "BCM2E99", (long)&bcm43430_device_data }, { "BCM2E9A" }, - { "BCM2E9B" }, + { "BCM2E9B", (long)&bcm43430_device_data }, { "BCM2E9C" }, { "BCM2E9D" }, + { "BCM2E9F", (long)&bcm43430_device_data }, { "BCM2EA0" }, { "BCM2EA1" }, - { "BCM2EA2" }, - { "BCM2EA3" }, + { "BCM2EA2", (long)&bcm43430_device_data }, + { "BCM2EA3", (long)&bcm43430_device_data }, { "BCM2EA4" }, { "BCM2EA5" }, { "BCM2EA6" }, { "BCM2EA7" }, { "BCM2EA8" }, { "BCM2EA9" }, - { "BCM2EAA" }, - { "BCM2EAB" }, - { "BCM2EAC" }, + { "BCM2EAA", (long)&bcm43430_device_data }, + { "BCM2EAB", (long)&bcm43430_device_data }, + { "BCM2EAC", (long)&bcm43430_device_data }, { }, }; MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); @@ -1535,6 +1563,8 @@ static int bcm_serdev_probe(struct serdev_device *serdev) bcmdev->no_early_set_baudrate = data->no_early_set_baudrate; bcmdev->drive_rts_on_open = data->drive_rts_on_open; bcmdev->no_uart_clock_set = data->no_uart_clock_set; + if (data->max_speed && bcmdev->oper_speed > data->max_speed) + bcmdev->oper_speed = data->max_speed; } return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 6455bc4fb5bb..fefc37b98b4a 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -463,6 +463,8 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_ack) { bt_dev_err(hu->hdev, "Out-of-order packet arrived (%u != %u)", H5_HDR_SEQ(hdr), h5->tx_ack); + set_bit(H5_TX_ACK_REQ, &h5->flags); + hci_uart_tx_wakeup(hu); h5_reset_rx(h5); return 0; } @@ -936,6 +938,8 @@ static int h5_btrtl_setup(struct h5 *h5) err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev); /* Give the device some time before the hci-core sends it a reset */ usleep_range(10000, 20000); + if (err) + goto out_free; btrtl_set_quirks(h5->hu->hdev, btrtl_dev); @@ -1100,6 +1104,8 @@ static const struct of_device_id rtl_bluetooth_of_match[] = { .data = (const void *)&h5_data_rtl8822cs }, { .compatible = "realtek,rtl8723bs-bt", .data = (const void *)&h5_data_rtl8723bs }, + { .compatible = "realtek,rtl8723cs-bt", + .data = (const void *)&h5_data_rtl8723bs }, { .compatible = "realtek,rtl8723ds-bt", .data = (const void *)&h5_data_rtl8723bs }, #endif diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 865112e96ff9..efdda2c3fce8 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -323,9 +323,9 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable) /* Disable hardware flow control */ ktermios = tty->termios; ktermios.c_cflag &= ~CRTSCTS; - status = tty_set_termios(tty, &ktermios); + tty_set_termios(tty, &ktermios); BT_DBG("Disabling hardware flow control: %s", - status ? "failed" : "success"); + (tty->termios.c_cflag & CRTSCTS) ? "failed" : "success"); /* Clear RTS to prevent the device from sending */ /* Most UARTs need OUT2 to enable interrupts */ @@ -357,9 +357,9 @@ void hci_uart_set_flow_control(struct hci_uart *hu, bool enable) /* Re-enable hardware flow control */ ktermios = tty->termios; ktermios.c_cflag |= CRTSCTS; - status = tty_set_termios(tty, &ktermios); + tty_set_termios(tty, &ktermios); BT_DBG("Enabling hardware flow control: %s", - status ? "failed" : "success"); + !(tty->termios.c_cflag & CRTSCTS) ? "failed" : "success"); } } diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 5abc01a2acf7..4a0b5c3160c2 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -786,7 +786,7 @@ MODULE_DEVICE_TABLE(of, hci_ti_of_match); static struct serdev_device_driver hci_ti_drv = { .driver = { .name = "hci-ti", - .of_match_table = of_match_ptr(hci_ti_of_match), + .of_match_table = hci_ti_of_match, }, .probe = hci_ti_probe, .remove = hci_ti_remove, diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c index fbc3f7c3a5c7..e08222395772 100644 --- a/drivers/bluetooth/hci_mrvl.c +++ b/drivers/bluetooth/hci_mrvl.c @@ -27,10 +27,12 @@ #define MRVL_ACK 0x5A #define MRVL_NAK 0xBF #define MRVL_RAW_DATA 0x1F +#define MRVL_SET_BAUDRATE 0xFC09 enum { STATE_CHIP_VER_PENDING, STATE_FW_REQ_PENDING, + STATE_FW_LOADED, }; struct mrvl_data { @@ -254,6 +256,14 @@ static int mrvl_recv(struct hci_uart *hu, const void *data, int count) if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) return -EUNATCH; + /* We might receive some noise when there is no firmware loaded. Therefore, + * we drop data if the firmware is not loaded yet and if there is no fw load + * request pending. + */ + if (!test_bit(STATE_FW_REQ_PENDING, &mrvl->flags) && + !test_bit(STATE_FW_LOADED, &mrvl->flags)) + return count; + mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count, mrvl_recv_pkts, ARRAY_SIZE(mrvl_recv_pkts)); @@ -354,6 +364,7 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name) static int mrvl_setup(struct hci_uart *hu) { int err; + struct mrvl_data *mrvl = hu->priv; hci_uart_set_flow_control(hu, true); @@ -367,9 +378,9 @@ static int mrvl_setup(struct hci_uart *hu) hci_uart_wait_until_sent(hu); if (hu->serdev) - serdev_device_set_baudrate(hu->serdev, 3000000); + serdev_device_set_baudrate(hu->serdev, hu->oper_speed); else - hci_uart_set_baudrate(hu, 3000000); + hci_uart_set_baudrate(hu, hu->oper_speed); hci_uart_set_flow_control(hu, false); @@ -377,13 +388,54 @@ static int mrvl_setup(struct hci_uart *hu) if (err) return err; + set_bit(STATE_FW_LOADED, &mrvl->flags); + + return 0; +} + +static int mrvl_set_baudrate(struct hci_uart *hu, unsigned int speed) +{ + int err; + struct mrvl_data *mrvl = hu->priv; + __le32 speed_le = cpu_to_le32(speed); + + /* The firmware might be loaded by the Wifi driver over SDIO. We wait + * up to 10s for the CTS to go up. Afterward, we know that the firmware + * is ready. + */ + err = serdev_device_wait_for_cts(hu->serdev, true, 10000); + if (err) { + bt_dev_err(hu->hdev, "Wait for CTS failed with %d\n", err); + return err; + } + + set_bit(STATE_FW_LOADED, &mrvl->flags); + + err = __hci_cmd_sync_status(hu->hdev, MRVL_SET_BAUDRATE, + sizeof(speed_le), &speed_le, + HCI_INIT_TIMEOUT); + if (err) { + bt_dev_err(hu->hdev, "send command failed: %d", err); + return err; + } + + serdev_device_set_baudrate(hu->serdev, speed); + + /* We forcefully have to send a command to the bluetooth module so that + * the driver detects it after a baudrate change. This is foreseen by + * hci_serdev by setting HCI_UART_VND_DETECT which then causes a dummy + * local version read. + */ + set_bit(HCI_UART_VND_DETECT, &hu->hdev_flags); + return 0; } -static const struct hci_uart_proto mrvl_proto = { +static const struct hci_uart_proto mrvl_proto_8897 = { .id = HCI_UART_MRVL, .name = "Marvell", .init_speed = 115200, + .oper_speed = 3000000, .open = mrvl_open, .close = mrvl_close, .flush = mrvl_flush, @@ -393,18 +445,37 @@ static const struct hci_uart_proto mrvl_proto = { .dequeue = mrvl_dequeue, }; +static const struct hci_uart_proto mrvl_proto_8997 = { + .id = HCI_UART_MRVL, + .name = "Marvell 8997", + .init_speed = 115200, + .oper_speed = 3000000, + .open = mrvl_open, + .close = mrvl_close, + .flush = mrvl_flush, + .set_baudrate = mrvl_set_baudrate, + .recv = mrvl_recv, + .enqueue = mrvl_enqueue, + .dequeue = mrvl_dequeue, +}; + static int mrvl_serdev_probe(struct serdev_device *serdev) { struct mrvl_serdev *mrvldev; + const struct hci_uart_proto *mrvl_proto = device_get_match_data(&serdev->dev); mrvldev = devm_kzalloc(&serdev->dev, sizeof(*mrvldev), GFP_KERNEL); if (!mrvldev) return -ENOMEM; + mrvldev->hu.oper_speed = mrvl_proto->oper_speed; + if (mrvl_proto->set_baudrate) + of_property_read_u32(serdev->dev.of_node, "max-speed", &mrvldev->hu.oper_speed); + mrvldev->hu.serdev = serdev; serdev_device_set_drvdata(serdev, mrvldev); - return hci_uart_register_device(&mrvldev->hu, &mrvl_proto); + return hci_uart_register_device(&mrvldev->hu, mrvl_proto); } static void mrvl_serdev_remove(struct serdev_device *serdev) @@ -414,13 +485,12 @@ static void mrvl_serdev_remove(struct serdev_device *serdev) hci_uart_unregister_device(&mrvldev->hu); } -#ifdef CONFIG_OF -static const struct of_device_id mrvl_bluetooth_of_match[] = { - { .compatible = "mrvl,88w8897" }, +static const struct of_device_id __maybe_unused mrvl_bluetooth_of_match[] = { + { .compatible = "mrvl,88w8897", .data = &mrvl_proto_8897}, + { .compatible = "mrvl,88w8997", .data = &mrvl_proto_8997}, { }, }; MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match); -#endif static struct serdev_device_driver mrvl_serdev_driver = { .probe = mrvl_serdev_probe, @@ -435,12 +505,12 @@ int __init mrvl_init(void) { serdev_device_driver_register(&mrvl_serdev_driver); - return hci_uart_register_proto(&mrvl_proto); + return hci_uart_register_proto(&mrvl_proto_8897); } int __exit mrvl_deinit(void) { serdev_device_driver_unregister(&mrvl_serdev_driver); - return hci_uart_unregister_proto(&mrvl_proto); + return hci_uart_unregister_proto(&mrvl_proto_8897); } diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 3df8c3606e93..1b064504b388 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1317,7 +1317,8 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) /* Give the controller time to process the request */ if (qca_is_wcn399x(qca_soc_type(hu)) || - qca_is_wcn6750(qca_soc_type(hu))) + qca_is_wcn6750(qca_soc_type(hu)) || + qca_is_wcn6855(qca_soc_type(hu))) usleep_range(1000, 10000); else msleep(300); @@ -1394,7 +1395,8 @@ static unsigned int qca_get_speed(struct hci_uart *hu, static int qca_check_speeds(struct hci_uart *hu) { if (qca_is_wcn399x(qca_soc_type(hu)) || - qca_is_wcn6750(qca_soc_type(hu))) { + qca_is_wcn6750(qca_soc_type(hu)) || + qca_is_wcn6855(qca_soc_type(hu))) { if (!qca_get_speed(hu, QCA_INIT_SPEED) && !qca_get_speed(hu, QCA_OPER_SPEED)) return -EINVAL; @@ -1428,7 +1430,8 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) * changing the baudrate of chip and host. */ if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) hci_uart_set_flow_control(hu, true); if (soc_type == QCA_WCN3990) { @@ -1446,7 +1449,8 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type) error: if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) hci_uart_set_flow_control(hu, false); if (soc_type == QCA_WCN3990) { @@ -1682,7 +1686,8 @@ static int qca_power_on(struct hci_dev *hdev) return 0; if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) { + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) { ret = qca_regulator_init(hu); } else { qcadev = serdev_device_get_drvdata(hu->serdev); @@ -1723,7 +1728,8 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "setting up %s", qca_is_wcn399x(soc_type) ? "wcn399x" : - (soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390"); + (soc_type == QCA_WCN6750) ? "wcn6750" : + (soc_type == QCA_WCN6855) ? "wcn6855" : "ROME/QCA6390"); qca->memdump_state = QCA_MEMDUMP_IDLE; @@ -1735,7 +1741,8 @@ retry: clear_bit(QCA_SSR_TRIGGERED, &qca->flags); if (qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type)) { + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type)) { set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks); hci_set_aosp_capable(hdev); @@ -1757,7 +1764,8 @@ retry: } if (!(qca_is_wcn399x(soc_type) || - qca_is_wcn6750(soc_type))) { + qca_is_wcn6750(soc_type) || + qca_is_wcn6855(soc_type))) { /* Get QCA version information */ ret = qca_read_soc_version(hdev, &ver, soc_type); if (ret) @@ -1827,7 +1835,7 @@ static const struct hci_uart_proto qca_proto = { .dequeue = qca_dequeue, }; -static const struct qca_device_data qca_soc_data_wcn3990 = { +static const struct qca_device_data qca_soc_data_wcn3990 __maybe_unused = { .soc_type = QCA_WCN3990, .vregs = (struct qca_vreg []) { { "vddio", 15000 }, @@ -1838,7 +1846,7 @@ static const struct qca_device_data qca_soc_data_wcn3990 = { .num_vregs = 4, }; -static const struct qca_device_data qca_soc_data_wcn3991 = { +static const struct qca_device_data qca_soc_data_wcn3991 __maybe_unused = { .soc_type = QCA_WCN3991, .vregs = (struct qca_vreg []) { { "vddio", 15000 }, @@ -1850,7 +1858,7 @@ static const struct qca_device_data qca_soc_data_wcn3991 = { .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, }; -static const struct qca_device_data qca_soc_data_wcn3998 = { +static const struct qca_device_data qca_soc_data_wcn3998 __maybe_unused = { .soc_type = QCA_WCN3998, .vregs = (struct qca_vreg []) { { "vddio", 10000 }, @@ -1861,12 +1869,12 @@ static const struct qca_device_data qca_soc_data_wcn3998 = { .num_vregs = 4, }; -static const struct qca_device_data qca_soc_data_qca6390 = { +static const struct qca_device_data qca_soc_data_qca6390 __maybe_unused = { .soc_type = QCA_QCA6390, .num_vregs = 0, }; -static const struct qca_device_data qca_soc_data_wcn6750 = { +static const struct qca_device_data qca_soc_data_wcn6750 __maybe_unused = { .soc_type = QCA_WCN6750, .vregs = (struct qca_vreg []) { { "vddio", 5000 }, @@ -1883,6 +1891,20 @@ static const struct qca_device_data qca_soc_data_wcn6750 = { .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, }; +static const struct qca_device_data qca_soc_data_wcn6855 __maybe_unused = { + .soc_type = QCA_WCN6855, + .vregs = (struct qca_vreg []) { + { "vddio", 5000 }, + { "vddbtcxmx", 126000 }, + { "vddrfacmn", 12500 }, + { "vddrfa0p8", 102000 }, + { "vddrfa1p7", 302000 }, + { "vddrfa1p2", 257000 }, + }, + .num_vregs = 6, + .capabilities = QCA_CAP_WIDEBAND_SPEECH | QCA_CAP_VALID_LE_STATES, +}; + static void qca_power_shutdown(struct hci_uart *hu) { struct qca_serdev *qcadev; @@ -1912,7 +1934,7 @@ static void qca_power_shutdown(struct hci_uart *hu) host_set_baudrate(hu, 2400); qca_send_power_pulse(hu, false); qca_regulator_disable(qcadev); - } else if (soc_type == QCA_WCN6750) { + } else if (soc_type == QCA_WCN6750 || soc_type == QCA_WCN6855) { gpiod_set_value_cansleep(qcadev->bt_en, 0); msleep(100); qca_regulator_disable(qcadev); @@ -2047,7 +2069,8 @@ static int qca_serdev_probe(struct serdev_device *serdev) if (data && (qca_is_wcn399x(data->soc_type) || - qca_is_wcn6750(data->soc_type))) { + qca_is_wcn6750(data->soc_type) || + qca_is_wcn6855(data->soc_type))) { qcadev->btsoc_type = data->soc_type; qcadev->bt_power = devm_kzalloc(&serdev->dev, sizeof(struct qca_power), @@ -2067,14 +2090,18 @@ static int qca_serdev_probe(struct serdev_device *serdev) qcadev->bt_en = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR_OR_NULL(qcadev->bt_en) && data->soc_type == QCA_WCN6750) { + if (IS_ERR_OR_NULL(qcadev->bt_en) && + (data->soc_type == QCA_WCN6750 || + data->soc_type == QCA_WCN6855)) { dev_err(&serdev->dev, "failed to acquire BT_EN gpio\n"); power_ctrl_enabled = false; } qcadev->sw_ctrl = devm_gpiod_get_optional(&serdev->dev, "swctrl", GPIOD_IN); - if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && data->soc_type == QCA_WCN6750) + if (IS_ERR_OR_NULL(qcadev->sw_ctrl) && + (data->soc_type == QCA_WCN6750 || + data->soc_type == QCA_WCN6855)) dev_warn(&serdev->dev, "failed to acquire SW_CTRL gpio\n"); qcadev->susclk = devm_clk_get_optional(&serdev->dev, NULL); @@ -2150,8 +2177,9 @@ static void qca_serdev_remove(struct serdev_device *serdev) struct qca_power *power = qcadev->bt_power; if ((qca_is_wcn399x(qcadev->btsoc_type) || - qca_is_wcn6750(qcadev->btsoc_type)) && - power->vregs_on) + qca_is_wcn6750(qcadev->btsoc_type) || + qca_is_wcn6855(qcadev->btsoc_type)) && + power->vregs_on) qca_power_shutdown(&qcadev->serdev_hu); else if (qcadev->susclk) clk_disable_unprepare(qcadev->susclk); @@ -2335,6 +2363,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = { { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991}, { .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998}, { .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750}, + { .compatible = "qcom,wcn6855-bt", .data = &qca_soc_data_wcn6855}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index c443c3b0a4da..40e2b9fa11a2 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -278,6 +278,104 @@ static int vhci_setup(struct hci_dev *hdev) return 0; } +static void vhci_coredump(struct hci_dev *hdev) +{ + /* No need to do anything */ +} + +static void vhci_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb) +{ + char buf[80]; + + snprintf(buf, sizeof(buf), "Controller Name: vhci_ctrl\n"); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: vhci_fw\n"); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: vhci_drv\n"); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Vendor: vhci\n"); + skb_put_data(skb, buf, strlen(buf)); +} + +#define MAX_COREDUMP_LINE_LEN 40 + +struct devcoredump_test_data { + enum devcoredump_state state; + unsigned int timeout; + char data[MAX_COREDUMP_LINE_LEN]; +}; + +static inline void force_devcd_timeout(struct hci_dev *hdev, + unsigned int timeout) +{ +#ifdef CONFIG_DEV_COREDUMP + hdev->dump.timeout = msecs_to_jiffies(timeout * 1000); +#endif +} + +static ssize_t force_devcd_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct vhci_data *data = file->private_data; + struct hci_dev *hdev = data->hdev; + struct sk_buff *skb = NULL; + struct devcoredump_test_data dump_data; + size_t data_size; + int ret; + + if (count < offsetof(struct devcoredump_test_data, data) || + count > sizeof(dump_data)) + return -EINVAL; + + if (copy_from_user(&dump_data, user_buf, count)) + return -EFAULT; + + data_size = count - offsetof(struct devcoredump_test_data, data); + skb = alloc_skb(data_size, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + skb_put_data(skb, &dump_data.data, data_size); + + hci_devcd_register(hdev, vhci_coredump, vhci_coredump_hdr, NULL); + + /* Force the devcoredump timeout */ + if (dump_data.timeout) + force_devcd_timeout(hdev, dump_data.timeout); + + ret = hci_devcd_init(hdev, skb->len); + if (ret) { + BT_ERR("Failed to generate devcoredump"); + kfree_skb(skb); + return ret; + } + + hci_devcd_append(hdev, skb); + + switch (dump_data.state) { + case HCI_DEVCOREDUMP_DONE: + hci_devcd_complete(hdev); + break; + case HCI_DEVCOREDUMP_ABORT: + hci_devcd_abort(hdev); + break; + case HCI_DEVCOREDUMP_TIMEOUT: + /* Do nothing */ + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations force_devcoredump_fops = { + .open = simple_open, + .write = force_devcd_write, +}; + static int __vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; @@ -355,6 +453,9 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data, &aosp_capable_fops); + debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data, + &force_devcoredump_fops); + hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; skb_put_u8(skb, 0xff); |