diff options
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/ath3k.c | 5 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_drv.h | 4 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_main.c | 19 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.c | 103 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_sdio.h | 3 | ||||
-rw-r--r-- | drivers/bluetooth/btusb.c | 160 | ||||
-rw-r--r-- | drivers/bluetooth/hci_h4.c | 7 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ldisc.c | 24 | ||||
-rw-r--r-- | drivers/bluetooth/hci_uart.h | 1 |
9 files changed, 286 insertions, 40 deletions
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index be571fef185..f98380648cb 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -82,6 +82,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x04CA, 0x3004) }, { USB_DEVICE(0x04CA, 0x3005) }, { USB_DEVICE(0x04CA, 0x3006) }, + { USB_DEVICE(0x04CA, 0x3007) }, { USB_DEVICE(0x04CA, 0x3008) }, { USB_DEVICE(0x04CA, 0x300b) }, { USB_DEVICE(0x0930, 0x0219) }, @@ -131,6 +132,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, @@ -191,9 +193,10 @@ static int ath3k_load_firmware(struct usb_device *udev, sent += 20; count -= 20; + pipe = usb_sndbulkpipe(udev, 0x02); + while (count) { size = min_t(uint, count, BULK_SIZE); - pipe = usb_sndbulkpipe(udev, 0x02); memcpy(send_buf, firmware->data + sent, size); err = usb_bulk_msg(udev, pipe, send_buf, size, diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 7399303d7d9..dc79f88f871 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -59,6 +59,8 @@ struct btmrvl_device { }; struct btmrvl_adapter { + void *hw_regs_buf; + u8 *hw_regs; u32 int_count; struct sk_buff_head tx_queue; u8 psmode; @@ -140,7 +142,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv); bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb); -int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd); +int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd); int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 2c4997ce248..e9dbddb0b8f 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -24,6 +24,7 @@ #include <net/bluetooth/hci_core.h> #include "btmrvl_drv.h" +#include "btmrvl_sdio.h" #define VERSION "1.0" @@ -201,7 +202,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, return 0; } -int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) +int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd) { int ret; @@ -337,10 +338,25 @@ static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) static void btmrvl_init_adapter(struct btmrvl_private *priv) { + int buf_size; + skb_queue_head_init(&priv->adapter->tx_queue); priv->adapter->ps_state = PS_AWAKE; + buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN); + priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL); + if (!priv->adapter->hw_regs_buf) { + priv->adapter->hw_regs = NULL; + BT_ERR("Unable to allocate buffer for hw_regs."); + } else { + priv->adapter->hw_regs = + (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, + BTSDIO_DMA_ALIGN); + BT_DBG("hw_regs_buf=%p hw_regs=%p", + priv->adapter->hw_regs_buf, priv->adapter->hw_regs); + } + init_waitqueue_head(&priv->adapter->cmd_wait_q); } @@ -348,6 +364,7 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv) { skb_queue_purge(&priv->adapter->tx_queue); + kfree(priv->adapter->hw_regs_buf); kfree(priv->adapter); priv->adapter = NULL; diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 1b52c9f5230..9dedca516ff 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -64,6 +64,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8688 = { .io_port_0 = 0x00, .io_port_1 = 0x01, .io_port_2 = 0x02, + .int_read_to_clear = false, }; static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = { .cfg = 0x00, @@ -80,6 +81,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_87xx = { .io_port_0 = 0x78, .io_port_1 = 0x79, .io_port_2 = 0x7a, + .int_read_to_clear = false, }; static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = { @@ -97,6 +99,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = { .io_port_0 = 0xd8, .io_port_1 = 0xd9, .io_port_2 = 0xda, + .int_read_to_clear = true, + .host_int_rsr = 0x01, + .card_misc_cfg = 0xcc, }; static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { @@ -667,46 +672,78 @@ static int btmrvl_sdio_process_int_status(struct btmrvl_private *priv) return 0; } -static void btmrvl_sdio_interrupt(struct sdio_func *func) +static int btmrvl_sdio_read_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) { - struct btmrvl_private *priv; - struct btmrvl_sdio_card *card; - ulong flags; - u8 ireg = 0; + struct btmrvl_adapter *adapter = card->priv->adapter; int ret; - card = sdio_get_drvdata(func); - if (!card || !card->priv) { - BT_ERR("sbi_interrupt(%p) card or priv is " - "NULL, card=%p\n", func, card); - return; + ret = sdio_readsb(card->func, adapter->hw_regs, 0, SDIO_BLOCK_SIZE); + if (ret) { + BT_ERR("sdio_readsb: read int hw_regs failed: %d", ret); + return ret; } - priv = card->priv; + *ireg = adapter->hw_regs[card->reg->host_intstatus]; + BT_DBG("hw_regs[%#x]=%#x", card->reg->host_intstatus, *ireg); + + return 0; +} - ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret); +static int btmrvl_sdio_write_to_clear(struct btmrvl_sdio_card *card, u8 *ireg) +{ + int ret; + + *ireg = sdio_readb(card->func, card->reg->host_intstatus, &ret); if (ret) { - BT_ERR("sdio_readb: read int status register failed"); - return; + BT_ERR("sdio_readb: read int status failed: %d", ret); + return ret; } - if (ireg != 0) { + if (*ireg) { /* * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS * Clear the interrupt status register and re-enable the * interrupt. */ - BT_DBG("ireg = 0x%x", ireg); + BT_DBG("int_status = 0x%x", *ireg); - sdio_writeb(card->func, ~(ireg) & (DN_LD_HOST_INT_STATUS | - UP_LD_HOST_INT_STATUS), - card->reg->host_intstatus, &ret); + sdio_writeb(card->func, ~(*ireg) & (DN_LD_HOST_INT_STATUS | + UP_LD_HOST_INT_STATUS), + card->reg->host_intstatus, &ret); if (ret) { - BT_ERR("sdio_writeb: clear int status register failed"); - return; + BT_ERR("sdio_writeb: clear int status failed: %d", ret); + return ret; } } + return 0; +} + +static void btmrvl_sdio_interrupt(struct sdio_func *func) +{ + struct btmrvl_private *priv; + struct btmrvl_sdio_card *card; + ulong flags; + u8 ireg = 0; + int ret; + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + BT_ERR("sbi_interrupt(%p) card or priv is " + "NULL, card=%p\n", func, card); + return; + } + + priv = card->priv; + + if (card->reg->int_read_to_clear) + ret = btmrvl_sdio_read_to_clear(card, &ireg); + else + ret = btmrvl_sdio_write_to_clear(card, &ireg); + + if (ret) + return; + spin_lock_irqsave(&priv->driver_lock, flags); sdio_ireg |= ireg; spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -777,6 +814,30 @@ static int btmrvl_sdio_register_dev(struct btmrvl_sdio_card *card) BT_DBG("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport); + if (card->reg->int_read_to_clear) { + reg = sdio_readb(func, card->reg->host_int_rsr, &ret); + if (ret < 0) { + ret = -EIO; + goto release_irq; + } + sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret); + if (ret < 0) { + ret = -EIO; + goto release_irq; + } + + reg = sdio_readb(func, card->reg->card_misc_cfg, &ret); + if (ret < 0) { + ret = -EIO; + goto release_irq; + } + sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret); + if (ret < 0) { + ret = -EIO; + goto release_irq; + } + } + sdio_set_drvdata(func, card); sdio_release_host(func); diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h index 43d35a609ca..d4dd3b0fa53 100644 --- a/drivers/bluetooth/btmrvl_sdio.h +++ b/drivers/bluetooth/btmrvl_sdio.h @@ -78,6 +78,9 @@ struct btmrvl_sdio_card_reg { u8 io_port_0; u8 io_port_1; u8 io_port_2; + bool int_read_to_clear; + u8 host_int_rsr; + u8 card_misc_cfg; }; struct btmrvl_sdio_card { diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f338b0c5a8d..a1c80b0c766 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -49,6 +49,7 @@ static struct usb_driver btusb_driver; #define BTUSB_WRONG_SCO_MTU 0x40 #define BTUSB_ATH3012 0x80 #define BTUSB_INTEL 0x100 +#define BTUSB_BCM_PATCHRAM 0x200 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -111,7 +112,8 @@ static const struct usb_device_id btusb_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, /* Broadcom devices with vendor specific id */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* Belkin F8065bf - Broadcom based */ { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, @@ -152,6 +154,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 }, @@ -1380,6 +1383,154 @@ exit_mfg_deactivate: return 0; } +static int btusb_setup_bcm_patchram(struct hci_dev *hdev) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + struct usb_device *udev = data->udev; + char fw_name[64]; + const struct firmware *fw; + const u8 *fw_ptr; + size_t fw_size; + const struct hci_command_hdr *cmd; + const u8 *cmd_param; + u16 opcode; + struct sk_buff *skb; + struct hci_rp_read_local_version *ver; + long ret; + + snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd", + udev->product ? udev->product : "BCM", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + ret = request_firmware(&fw, fw_name, &hdev->dev); + if (ret < 0) { + BT_INFO("%s: BCM: patch %s not found", hdev->name, + fw_name); + return 0; + } + + /* Reset */ + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); + goto done; + } + kfree_skb(skb); + + /* Read Local Version Info */ + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", + hdev->name, ret); + goto done; + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", + hdev->name); + kfree_skb(skb); + ret = -EIO; + goto done; + } + + ver = (struct hci_rp_read_local_version *) skb->data; + BT_INFO("%s: BCM: patching hci_ver=%02x hci_rev=%04x lmp_ver=%02x " + "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, + ver->lmp_ver, ver->lmp_subver); + kfree_skb(skb); + + /* Start Download */ + skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: BCM: Download Minidrv command failed (%ld)", + hdev->name, ret); + goto reset_fw; + } + kfree_skb(skb); + + /* 50 msec delay after Download Minidrv completes */ + msleep(50); + + fw_ptr = fw->data; + fw_size = fw->size; + + while (fw_size >= sizeof(*cmd)) { + cmd = (struct hci_command_hdr *) fw_ptr; + fw_ptr += sizeof(*cmd); + fw_size -= sizeof(*cmd); + + if (fw_size < cmd->plen) { + BT_ERR("%s: BCM: patch %s is corrupted", + hdev->name, fw_name); + ret = -EINVAL; + goto reset_fw; + } + + cmd_param = fw_ptr; + fw_ptr += cmd->plen; + fw_size -= cmd->plen; + + opcode = le16_to_cpu(cmd->opcode); + + skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: BCM: patch command %04x failed (%ld)", + hdev->name, opcode, ret); + goto reset_fw; + } + kfree_skb(skb); + } + + /* 250 msec delay after Launch Ram completes */ + msleep(250); + +reset_fw: + /* Reset */ + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_RESET failed (%ld)", hdev->name, ret); + goto done; + } + kfree_skb(skb); + + /* Read Local Version Info */ + skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)", + hdev->name, ret); + goto done; + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch", + hdev->name); + kfree_skb(skb); + ret = -EIO; + goto done; + } + + ver = (struct hci_rp_read_local_version *) skb->data; + BT_INFO("%s: BCM: firmware hci_ver=%02x hci_rev=%04x lmp_ver=%02x " + "lmp_subver=%04x", hdev->name, ver->hci_ver, ver->hci_rev, + ver->lmp_ver, ver->lmp_subver); + kfree_skb(skb); + +done: + release_firmware(fw); + + return ret; +} + static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1485,10 +1636,11 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_BCM92035) hdev->setup = btusb_setup_bcm92035; - if (id->driver_info & BTUSB_INTEL) { - usb_enable_autosuspend(data->udev); + if (id->driver_info & BTUSB_BCM_PATCHRAM) + hdev->setup = btusb_setup_bcm_patchram; + + if (id->driver_info & BTUSB_INTEL) hdev->setup = btusb_setup_intel; - } /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 7048a583fe5..66db9a80337 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -55,13 +55,6 @@ struct h4_struct { struct sk_buff_head txq; }; -/* H4 receiver States */ -#define H4_W4_PACKET_TYPE 0 -#define H4_W4_EVENT_HDR 1 -#define H4_W4_ACL_HDR 2 -#define H4_W4_SCO_HDR 3 -#define H4_W4_DATA 4 - /* Initialize protocol */ static int h4_open(struct hci_uart *hu) { diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index f1fbf4f1e5b..e00f8f5b5c8 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -118,10 +118,6 @@ static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu) int hci_uart_tx_wakeup(struct hci_uart *hu) { - struct tty_struct *tty = hu->tty; - struct hci_dev *hdev = hu->hdev; - struct sk_buff *skb; - if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) { set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); return 0; @@ -129,6 +125,22 @@ int hci_uart_tx_wakeup(struct hci_uart *hu) BT_DBG(""); + schedule_work(&hu->write_work); + + return 0; +} + +static void hci_uart_write_work(struct work_struct *work) +{ + struct hci_uart *hu = container_of(work, struct hci_uart, write_work); + struct tty_struct *tty = hu->tty; + struct hci_dev *hdev = hu->hdev; + struct sk_buff *skb; + + /* REVISIT: should we cope with bad skbs or ->write() returning + * and error value ? + */ + restart: clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state); @@ -153,7 +165,6 @@ restart: goto restart; clear_bit(HCI_UART_SENDING, &hu->tx_state); - return 0; } static void hci_uart_init_work(struct work_struct *work) @@ -282,6 +293,7 @@ static int hci_uart_tty_open(struct tty_struct *tty) tty->receive_room = 65536; INIT_WORK(&hu->init_ready, hci_uart_init_work); + INIT_WORK(&hu->write_work, hci_uart_write_work); spin_lock_init(&hu->rx_lock); @@ -319,6 +331,8 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hdev) hci_uart_close(hdev); + cancel_work_sync(&hu->write_work); + if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index fffa61ff5cb..12df101ca94 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -68,6 +68,7 @@ struct hci_uart { unsigned long hdev_flags; struct work_struct init_ready; + struct work_struct write_work; struct hci_uart_proto *proto; void *priv; |