diff options
Diffstat (limited to 'drivers/bluetooth')
| -rw-r--r-- | drivers/bluetooth/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/bluetooth/btbcm.c | 4 | ||||
| -rw-r--r-- | drivers/bluetooth/btintel_pcie.c | 175 | ||||
| -rw-r--r-- | drivers/bluetooth/btintel_pcie.h | 4 | ||||
| -rw-r--r-- | drivers/bluetooth/btmtksdio.c | 1 | ||||
| -rw-r--r-- | drivers/bluetooth/btrtl.c | 16 | ||||
| -rw-r--r-- | drivers/bluetooth/btusb.c | 47 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_bcm.c | 6 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_h5.c | 53 | ||||
| -rw-r--r-- | drivers/bluetooth/hci_intel.c | 3 |
10 files changed, 266 insertions, 44 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 7df69ccb6600..c5d45cf91f88 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -188,6 +188,7 @@ config BT_HCIUART_3WIRE bool "Three-wire UART (H5) protocol support" depends on BT_HCIUART depends on BT_HCIUART_SERDEV + select CRC_CCITT help The HCI Three-wire UART Transport Layer makes it possible to user the Bluetooth HCI over a serial port interface. The HCI diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index 3a3a56ddbb06..d33cc70eec66 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -642,7 +642,9 @@ int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid); } - fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL); + fw_name = kmalloc_array(BCM_FW_NAME_COUNT_MAX, + sizeof(*fw_name), + GFP_KERNEL); if (!fw_name) return -ENOMEM; diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index a075d8ec4677..2936b535479f 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -19,6 +19,7 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include <net/bluetooth/hci_drv.h> #include "btintel.h" #include "btintel_pcie.h" @@ -825,6 +826,11 @@ static inline bool btintel_pcie_in_d0(struct btintel_pcie_data *data) return !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY); } +static inline bool btintel_pcie_in_device_halt(struct btintel_pcie_data *data) +{ + return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED; +} + static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data, u32 dxstate) { @@ -2355,6 +2361,63 @@ static bool btintel_pcie_wakeup(struct hci_dev *hdev) return device_may_wakeup(&data->pdev->dev); } +static const struct { + u16 opcode; + const char *desc; +} btintel_pcie_hci_drv_supported_commands[] = { + /* Common commands */ + { HCI_DRV_OP_READ_INFO, "Read Info" }, +}; + +static int btintel_pcie_hci_drv_read_info(struct hci_dev *hdev, void *data, + u16 data_len) +{ + struct hci_drv_rp_read_info *rp; + size_t rp_size; + int err, i; + u16 opcode, num_supported_commands = + ARRAY_SIZE(btintel_pcie_hci_drv_supported_commands); + + rp_size = sizeof(*rp) + num_supported_commands * 2; + + rp = kmalloc(rp_size, GFP_KERNEL); + if (!rp) + return -ENOMEM; + + strscpy_pad(rp->driver_name, KBUILD_MODNAME); + + rp->num_supported_commands = cpu_to_le16(num_supported_commands); + for (i = 0; i < num_supported_commands; i++) { + opcode = btintel_pcie_hci_drv_supported_commands[i].opcode; + bt_dev_dbg(hdev, + "Supported HCI Drv command (0x%02x|0x%04x): %s", + hci_opcode_ogf(opcode), + hci_opcode_ocf(opcode), + btintel_pcie_hci_drv_supported_commands[i].desc); + rp->supported_commands[i] = cpu_to_le16(opcode); + } + + err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO, + HCI_DRV_STATUS_SUCCESS, + rp, rp_size); + + kfree(rp); + return err; +} + +static const struct hci_drv_handler btintel_pcie_hci_drv_common_handlers[] = { + { btintel_pcie_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE }, +}; + +static const struct hci_drv_handler btintel_pcie_hci_drv_specific_handlers[] = {}; + +static struct hci_drv btintel_pcie_hci_drv = { + .common_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_common_handlers), + .common_handlers = btintel_pcie_hci_drv_common_handlers, + .specific_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_specific_handlers), + .specific_handlers = btintel_pcie_hci_drv_specific_handlers, +}; + static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) { int err; @@ -2381,6 +2444,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data) hdev->set_bdaddr = btintel_set_bdaddr; hdev->reset = btintel_pcie_reset; hdev->wakeup = btintel_pcie_wakeup; + hdev->hci_drv = &btintel_pcie_hci_drv; err = hci_register_dev(hdev); if (err < 0) { @@ -2519,6 +2583,48 @@ static void btintel_pcie_coredump(struct device *dev) } #endif +static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate) +{ + int retry = 0, status; + u32 dx_intr_timeout_ms = 200; + + do { + data->gp0_received = false; + + btintel_pcie_wr_sleep_cntrl(data, dxstate); + + status = wait_event_timeout(data->gp0_wait_q, data->gp0_received, + msecs_to_jiffies(dx_intr_timeout_ms)); + + if (status) + return 0; + + bt_dev_warn(data->hdev, + "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d", + dx_intr_timeout_ms, dxstate, retry); + + /* clear gp0 cause */ + btintel_pcie_clr_reg_bits(data, + BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES, + BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0); + + /* A hardware bug may cause the alive interrupt to be missed. + * Check if the controller reached the expected state and retry + * the operation only if it hasn't. + */ + if (dxstate == BTINTEL_PCIE_STATE_D0) { + if (btintel_pcie_in_d0(data)) + return 0; + } else { + if (btintel_pcie_in_d3(data)) + return 0; + } + + } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES); + + return -EBUSY; +} + static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) { struct pci_dev *pdev = to_pci_dev(dev); @@ -2532,26 +2638,20 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg) dxstate = (mesg.event == PM_EVENT_SUSPEND ? BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD); - data->gp0_received = false; + data->pm_sx_event = mesg.event; start = ktime_get(); /* Refer: 6.4.11.7 -> Platform power management */ - btintel_pcie_wr_sleep_cntrl(data, dxstate); - err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, - msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); - if (err == 0) { - bt_dev_err(data->hdev, - "Timeout (%u ms) on alive interrupt for D3 entry", - BTINTEL_DEFAULT_INTR_TIMEOUT_MS); - return -EBUSY; - } + err = btintel_pcie_set_dxstate(data, dxstate); + + if (err) + return err; bt_dev_dbg(data->hdev, "device entered into d3 state from d0 in %lld us", ktime_to_us(ktime_get() - start)); - - return 0; + return err; } static int btintel_pcie_suspend(struct device *dev) @@ -2581,21 +2681,50 @@ static int btintel_pcie_resume(struct device *dev) start = ktime_get(); + /* When the system enters S4 (hibernate) mode, bluetooth device loses + * power, which results in the erasure of its loaded firmware. + * Consequently, function level reset (flr) is required on system + * resume to bring the controller back into an operational state by + * initiating a new firmware download. + */ + + if (data->pm_sx_event == PM_EVENT_FREEZE || + data->pm_sx_event == PM_EVENT_HIBERNATE) { + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + btintel_pcie_reset(data->hdev); + return 0; + } + /* Refer: 6.4.11.7 -> Platform power management */ - btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0); - err = wait_event_timeout(data->gp0_wait_q, data->gp0_received, - msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS)); + err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0); + if (err == 0) { - bt_dev_err(data->hdev, - "Timeout (%u ms) on alive interrupt for D0 entry", - BTINTEL_DEFAULT_INTR_TIMEOUT_MS); - return -EBUSY; + bt_dev_dbg(data->hdev, + "device entered into d0 state from d3 in %lld us", + ktime_to_us(ktime_get() - start)); + return err; } - bt_dev_dbg(data->hdev, - "device entered into d0 state from d3 in %lld us", - ktime_to_us(ktime_get() - start)); - return 0; + /* Trigger function level reset if the controller is in error + * state during resume() to bring back the controller to + * operational mode + */ + + data->boot_stage_cache = btintel_pcie_rd_reg32(data, + BTINTEL_PCIE_CSR_BOOT_STAGE_REG); + if (btintel_pcie_in_error(data) || + btintel_pcie_in_device_halt(data)) { + bt_dev_err(data->hdev, "Controller in error state for D0 entry"); + if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS, + &data->flags)) { + data->dmp_hdr.trigger_reason = + BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT; + queue_work(data->workqueue, &data->rx_work); + } + set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags); + btintel_pcie_reset(data->hdev); + } + return err; } static const struct dev_pm_ops btintel_pcie_pm_ops = { diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 04b21f968ad3..e3d941ffef4a 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -158,6 +158,8 @@ enum msix_mbox_int_causes { /* Default interrupt timeout in msec */ #define BTINTEL_DEFAULT_INTR_TIMEOUT_MS 3000 +#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3 + /* The number of descriptors in TX queues */ #define BTINTEL_PCIE_TX_DESCS_COUNT 32 @@ -464,6 +466,7 @@ struct btintel_pcie_dump_header { * @txq: TX Queue struct * @rxq: RX Queue struct * @alive_intr_ctxt: Alive interrupt context + * @pm_sx_event: PM event on which system got suspended */ struct btintel_pcie_data { struct pci_dev *pdev; @@ -513,6 +516,7 @@ struct btintel_pcie_data { u32 alive_intr_ctxt; struct btintel_pcie_dbgc dbgc; struct btintel_pcie_dump_header dmp_hdr; + u8 pm_sx_event; }; static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data, diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c index 62db31bd6592..fba3ab6d30a5 100644 --- a/drivers/bluetooth/btmtksdio.c +++ b/drivers/bluetooth/btmtksdio.c @@ -615,7 +615,6 @@ static void btmtksdio_txrx_work(struct work_struct *work) sdio_release_host(bdev->func); - pm_runtime_mark_last_busy(bdev->dev); pm_runtime_put_autosuspend(bdev->dev); } diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 52794db2739b..5603b282f9bc 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -72,6 +72,7 @@ enum btrtl_chip_id { CHIP_ID_8851B = 36, CHIP_ID_8922A = 44, CHIP_ID_8852BT = 47, + CHIP_ID_8761C = 51, }; struct id_table { @@ -230,6 +231,14 @@ static const struct id_table ic_id_table[] = { .cfg_name = "rtl_bt/rtl8761bu_config", .hw_info = "rtl8761bu" }, + /* 8761CU */ + { IC_INFO(RTL_ROM_LMP_8761A, 0x0e, 0, HCI_USB), + .config_needed = false, + .has_rom_version = true, + .fw_name = "rtl_bt/rtl8761cu_fw", + .cfg_name = "rtl_bt/rtl8761cu_config", + .hw_info = "rtl8761cu" }, + /* 8822C with UART interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART), .config_needed = true, @@ -344,7 +353,8 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, (ic_id_table[i].hci_rev != hci_rev)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) && - (ic_id_table[i].hci_ver != hci_ver)) + (ic_id_table[i].hci_ver != hci_ver) && + (ic_id_table[i].hci_ver != 0)) continue; if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) && (ic_id_table[i].hci_bus != hci_bus)) @@ -662,6 +672,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev, { RTL_ROM_LMP_8851B, 36 }, /* 8851B */ { RTL_ROM_LMP_8922A, 44 }, /* 8922A */ { RTL_ROM_LMP_8852A, 47 }, /* 8852BT */ + { RTL_ROM_LMP_8761A, 51 }, /* 8761C */ }; if (btrtl_dev->fw_len <= 8) @@ -1305,6 +1316,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev) case CHIP_ID_8851B: case CHIP_ID_8922A: case CHIP_ID_8852BT: + case CHIP_ID_8761C: hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED); /* RTL8852C needs to transmit mSBC data continuously without @@ -1524,6 +1536,8 @@ MODULE_FIRMWARE("rtl_bt/rtl8761b_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8761b_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8761bu_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8761bu_config.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8761cu_fw.bin"); +MODULE_FIRMWARE("rtl_bt/rtl8761cu_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin"); MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin"); MODULE_FIRMWARE("rtl_bt/rtl8821c_fw.bin"); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index fa683bb7f0b4..8ed3883ab8ee 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -504,6 +504,8 @@ static const struct usb_device_id quirks_table[] = { /* Realtek 8821CE Bluetooth devices */ { USB_DEVICE(0x13d3, 0x3529), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3533), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8822CE Bluetooth devices */ { USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK | @@ -585,6 +587,12 @@ static const struct usb_device_id quirks_table[] = { /* Realtek 8852BT/8852BE-VT Bluetooth devices */ { USB_DEVICE(0x0bda, 0x8520), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe12f), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3618), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x13d3, 0x3619), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Realtek 8922AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK | @@ -621,6 +629,8 @@ static const struct usb_device_id quirks_table[] = { /* Additional MediaTek MT7920 Bluetooth devices */ { USB_DEVICE(0x0489, 0xe134), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe135), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3620), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3621), .driver_info = BTUSB_MEDIATEK | @@ -685,6 +695,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x0489, 0xe153), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x0489, 0xe170), .driver_info = BTUSB_MEDIATEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x3804), .driver_info = BTUSB_MEDIATEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK | @@ -781,6 +793,8 @@ static const struct usb_device_id quirks_table[] = { BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x2b89, 0x8761), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + { USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, /* Additional Realtek 8821AE Bluetooth devices */ { USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK }, @@ -1131,6 +1145,24 @@ static void btusb_qca_reset(struct hci_dev *hdev) btusb_reset(hdev); } +static u8 btusb_classify_qca_pkt_type(struct hci_dev *hdev, struct sk_buff *skb) +{ + /* Some Qualcomm controllers, e.g., QCNFA765 with WCN6855 chip, send debug + * packets as ACL frames with connection handle 0x2EDC. These are not real + * ACL packets and should be reclassified as HCI_DIAG_PKT to prevent + * "ACL packet for unknown connection handle 3804" errors. + */ + if (skb->len >= 2) { + u16 handle = get_unaligned_le16(skb->data); + + if (handle == 0x2EDC) + return HCI_DIAG_PKT; + } + + /* Use default packet type for other packets */ + return hci_skb_pkt_type(skb); +} + static inline void btusb_free_frags(struct btusb_data *data) { unsigned long flags; @@ -2808,6 +2840,19 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data) btusb_stop_traffic(data); usb_kill_anchored_urbs(&data->tx_anchor); + /* Toggle the hard reset line. The MediaTek device is going to + * yank itself off the USB and then replug. The cleanup is handled + * correctly on the way out (standard USB disconnect), and the new + * device is detected cleanly and bound to the driver again like + * it should be. + */ + if (data->reset_gpio) { + gpiod_set_value_cansleep(data->reset_gpio, 1); + msleep(200); + gpiod_set_value_cansleep(data->reset_gpio, 0); + return 0; + } + err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id); usb_queue_reset_device(data->intf); @@ -3261,6 +3306,7 @@ static const struct qca_device_info qca_devices_table[] = { static const struct qca_custom_firmware qca_custom_btfws[] = { { 0x00130201, 0x030A, "QCA2066" }, + { 0x00130201, 0x030B, "QCA2066" }, { }, }; @@ -4226,6 +4272,7 @@ static int btusb_probe(struct usb_interface *intf, 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->classify_pkt_type = btusb_classify_qca_pkt_type; hdev->shutdown = btusb_shutdown_qca; hdev->set_bdaddr = btusb_set_bdaddr_wcn6855; hdev->reset = btusb_qca_reset; diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index fff845ed44e3..9286a5f40f55 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -326,7 +326,6 @@ static irqreturn_t bcm_host_wake(int irq, void *data) bt_dev_dbg(bdev, "Host wake IRQ"); pm_runtime_get(bdev->dev); - pm_runtime_mark_last_busy(bdev->dev); pm_runtime_put_autosuspend(bdev->dev); return IRQ_HANDLED; @@ -710,7 +709,6 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count) mutex_lock(&bcm_device_lock); if (bcm->dev && bcm_device_exists(bcm->dev)) { pm_runtime_get(bcm->dev->dev); - pm_runtime_mark_last_busy(bcm->dev->dev); pm_runtime_put_autosuspend(bcm->dev->dev); } mutex_unlock(&bcm_device_lock); @@ -748,10 +746,8 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu) skb = skb_dequeue(&bcm->txq); - if (bdev) { - pm_runtime_mark_last_busy(bdev->dev); + if (bdev) pm_runtime_put_autosuspend(bdev->dev); - } mutex_unlock(&bcm_device_lock); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index d0d4420c1a0f..96e20a66ecd1 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -7,6 +7,8 @@ */ #include <linux/acpi.h> +#include <linux/bitrev.h> +#include <linux/crc-ccitt.h> #include <linux/errno.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> @@ -58,6 +60,7 @@ enum { H5_TX_ACK_REQ, /* Pending ack to send */ H5_WAKEUP_DISABLE, /* Device cannot wake host */ H5_HW_FLOW_CONTROL, /* Use HW flow control */ + H5_CRC, /* Use CRC */ }; struct h5 { @@ -141,8 +144,8 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) static u8 h5_cfg_field(struct h5 *h5) { - /* Sliding window size (first 3 bits) */ - return h5->tx_win & 0x07; + /* Sliding window size (first 3 bits) and CRC request (fifth bit). */ + return (h5->tx_win & 0x07) | 0x10; } static void h5_timed_event(struct timer_list *t) @@ -213,7 +216,6 @@ static void h5_peer_reset(struct hci_uart *hu) static int h5_open(struct hci_uart *hu) { struct h5 *h5; - const unsigned char sync[] = { 0x01, 0x7e }; BT_DBG("hu %p", hu); @@ -243,9 +245,11 @@ static int h5_open(struct hci_uart *hu) set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags); - /* Send initial sync request */ - h5_link_control(hu, sync, sizeof(sync)); - mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT); + /* + * Wait one jiffy because the UART layer won't set HCI_UART_PROTO_READY, + * which allows us to send link packets, until this function returns. + */ + mod_timer(&h5->timer, jiffies + 1); return 0; } @@ -360,8 +364,10 @@ static void h5_handle_internal_rx(struct hci_uart *hu) h5_link_control(hu, conf_rsp, 2); h5_link_control(hu, conf_req, 3); } else if (memcmp(data, conf_rsp, 2) == 0) { - if (H5_HDR_LEN(hdr) > 2) + if (H5_HDR_LEN(hdr) > 2) { h5->tx_win = (data[2] & 0x07); + assign_bit(H5_CRC, &h5->flags, data[2] & 0x10); + } BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win); h5->state = H5_ACTIVE; hci_uart_init_ready(hu); @@ -425,7 +431,24 @@ static void h5_complete_rx_pkt(struct hci_uart *hu) static int h5_rx_crc(struct hci_uart *hu, unsigned char c) { - h5_complete_rx_pkt(hu); + struct h5 *h5 = hu->priv; + const unsigned char *hdr = h5->rx_skb->data; + u16 crc; + __be16 crc_be; + + crc = crc_ccitt(0xffff, hdr, 4 + H5_HDR_LEN(hdr)); + crc = bitrev16(crc); + + crc_be = cpu_to_be16(crc); + + if (memcmp(&crc_be, hdr + 4 + H5_HDR_LEN(hdr), 2) != 0) { + bt_dev_err(hu->hdev, "Received packet with invalid CRC"); + h5_reset_rx(h5); + } else { + /* Remove CRC bytes */ + skb_trim(h5->rx_skb, 4 + H5_HDR_LEN(hdr)); + h5_complete_rx_pkt(hu); + } return 0; } @@ -556,6 +579,7 @@ static void h5_reset_rx(struct h5 *h5) h5->rx_func = h5_rx_delimiter; h5->rx_pending = 0; clear_bit(H5_RX_ESC, &h5->flags); + clear_bit(H5_CRC, &h5->flags); } static int h5_recv(struct hci_uart *hu, const void *data, int count) @@ -592,7 +616,6 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count) if (hu->serdev) { pm_runtime_get(&hu->serdev->dev); - pm_runtime_mark_last_busy(&hu->serdev->dev); pm_runtime_put_autosuspend(&hu->serdev->dev); } @@ -634,7 +657,6 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) if (hu->serdev) { pm_runtime_get_sync(&hu->serdev->dev); - pm_runtime_mark_last_busy(&hu->serdev->dev); pm_runtime_put_autosuspend(&hu->serdev->dev); } @@ -686,6 +708,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type, struct h5 *h5 = hu->priv; struct sk_buff *nskb; u8 hdr[4]; + u16 crc; int i; if (!valid_packet_type(pkt_type)) { @@ -713,6 +736,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type, /* Reliable packet? */ if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) { hdr[0] |= 1 << 7; + hdr[0] |= (test_bit(H5_CRC, &h5->flags) && 1) << 6; hdr[0] |= h5->tx_seq; h5->tx_seq = (h5->tx_seq + 1) % 8; } @@ -732,6 +756,15 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type, for (i = 0; i < len; i++) h5_slip_one_byte(nskb, data[i]); + if (H5_HDR_CRC(hdr)) { + crc = crc_ccitt(0xffff, hdr, 4); + crc = crc_ccitt(crc, data, len); + crc = bitrev16(crc); + + h5_slip_one_byte(nskb, (crc >> 8) & 0xff); + h5_slip_one_byte(nskb, crc & 0xff); + } + h5_slip_delim(nskb); return nskb; diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index 1d6e09508f1f..20baf2895dec 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -280,7 +280,6 @@ static irqreturn_t intel_irq(int irq, void *dev_id) /* Host/Controller are now LPM resumed, trigger a new delayed suspend */ pm_runtime_get(&idev->pdev->dev); - pm_runtime_mark_last_busy(&idev->pdev->dev); pm_runtime_put_autosuspend(&idev->pdev->dev); return IRQ_HANDLED; @@ -371,7 +370,6 @@ static void intel_busy_work(struct work_struct *work) list_for_each_entry(idev, &intel_device_list, list) { if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) { pm_runtime_get(&idev->pdev->dev); - pm_runtime_mark_last_busy(&idev->pdev->dev); pm_runtime_put_autosuspend(&idev->pdev->dev); break; } @@ -1003,7 +1001,6 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) list_for_each_entry(idev, &intel_device_list, list) { if (hu->tty->dev->parent == idev->pdev->dev.parent) { pm_runtime_get_sync(&idev->pdev->dev); - pm_runtime_mark_last_busy(&idev->pdev->dev); pm_runtime_put_autosuspend(&idev->pdev->dev); break; } |
