diff options
author | Zhang Jiejing <jiejing.zhang@freescale.com> | 2011-04-01 10:22:19 +0800 |
---|---|---|
committer | Zhang Jiejing <jiejing.zhang@freescale.com> | 2011-04-01 19:57:39 +0800 |
commit | 1895a5df9b541fba44cd033a265460dc7261d521 (patch) | |
tree | f5aeee549b85e970815bbf26655e092877fa829a /drivers/bluetooth | |
parent | acba90905508678b5d54938bc726d8091cca38d8 (diff) |
ENGR00140702-1 BT: backport atheros 2.6.38 AR3001 bt driver
Atheros AR3001 UART bt driver already upstream in 2.6.38
backport these driver to our kernel to fix file transfer
data lost bug.
Signed-off-by: Zhang Jiejing <jiejing.zhang@freescale.com>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/Kconfig | 23 | ||||
-rw-r--r-- | drivers/bluetooth/Makefile | 3 | ||||
-rw-r--r-- | drivers/bluetooth/ath3k.c | 82 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ath.c | 407 | ||||
-rw-r--r-- | drivers/bluetooth/hci_ldisc.c | 39 | ||||
-rw-r--r-- | drivers/bluetooth/hci_uart.h | 12 |
6 files changed, 285 insertions, 281 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index bf28acc117ba..02deef424926 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -58,6 +58,18 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_ATH3K + bool "Atheros AR300x serial support" + depends on BT_HCIUART + help + HCIATH3K (HCI Atheros AR300x) is a serial protocol for + communication between host and Atheros AR300x Bluetooth devices. + This protocol enables AR300x chips to be enabled with + power management support. + Enable this if you have Atheros AR300x serial Bluetooth device. + + Say Y here to compile support for HCI UART ATH3K protocol. + config BT_HCIUART_LL bool "HCILL protocol support" depends on BT_HCIUART @@ -207,15 +219,4 @@ config BT_ATH3K Say Y here to compile support for "Atheros firmware download driver" into the kernel or say M to compile it as module (ath3k). -config BT_HCIUART_ATH - bool "Atheros AR300x Board support" - depends on BT_HCIUART - help - HCIATH (HCI Atheros) is a serial protocol for communication - between Bluetooth device and host with support for Atheros AR300x - power management feature. This protocol is required for - serial Bluetooth devices that are based on Atheros AR300x chips. - - Say Y here to compile support for HCIATH protocol. endmenu - diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 45d61b7a3a5b..71bdf13287c4 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -23,9 +23,8 @@ btmrvl-y := btmrvl_main.o btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o hci_uart-y := hci_ldisc.o -hci_uart-$(CONFIG_BT_HCIUART_ATH) += hci_ath.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o +hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o hci_uart-objs := $(hci_uart-y) - diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 128cae4e8629..051981a98b95 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -35,6 +35,10 @@ static struct usb_device_id ath3k_table[] = { /* Atheros AR3011 */ { USB_DEVICE(0x0CF3, 0x3000) }, + + /* Atheros AR3011 with sflash firmware*/ + { USB_DEVICE(0x0CF3, 0x3002) }, + { } /* Terminating entry */ }; @@ -43,33 +47,16 @@ MODULE_DEVICE_TABLE(usb, ath3k_table); #define USB_REQ_DFU_DNLOAD 1 #define BULK_SIZE 4096 -struct ath3k_data { - struct usb_device *udev; - u8 *fw_data; - u32 fw_size; - u32 fw_sent; -}; - -static int ath3k_load_firmware(struct ath3k_data *data, - unsigned char *firmware, - int count) +static int ath3k_load_firmware(struct usb_device *udev, + const struct firmware *firmware) { u8 *send_buf; int err, pipe, len, size, sent = 0; + int count = firmware->size; - BT_DBG("ath3k %p udev %p", data, data->udev); + BT_DBG("udev %p", udev); - pipe = usb_sndctrlpipe(data->udev, 0); - - if ((usb_control_msg(data->udev, pipe, - USB_REQ_DFU_DNLOAD, - USB_TYPE_VENDOR, 0, 0, - firmware, 20, USB_CTRL_SET_TIMEOUT)) < 0) { - BT_ERR("Can't change to loading configuration err"); - return -EBUSY; - } - sent += 20; - count -= 20; + pipe = usb_sndctrlpipe(udev, 0); send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC); if (!send_buf) { @@ -77,12 +64,24 @@ static int ath3k_load_firmware(struct ath3k_data *data, return -ENOMEM; } + memcpy(send_buf, firmware->data, 20); + err = usb_control_msg(udev, pipe, + USB_REQ_DFU_DNLOAD, + USB_TYPE_VENDOR, 0, 0, + send_buf, 20, USB_CTRL_SET_TIMEOUT); + if (err < 0) { + BT_ERR("Can't change to loading configuration err"); + goto error; + } + sent += 20; + count -= 20; + while (count) { size = min_t(uint, count, BULK_SIZE); - pipe = usb_sndbulkpipe(data->udev, 0x02); - memcpy(send_buf, firmware + sent, size); + pipe = usb_sndbulkpipe(udev, 0x02); + memcpy(send_buf, firmware->data + sent, size); - err = usb_bulk_msg(data->udev, pipe, send_buf, size, + err = usb_bulk_msg(udev, pipe, send_buf, size, &len, 3000); if (err || (len != size)) { @@ -108,57 +107,28 @@ static int ath3k_probe(struct usb_interface *intf, { const struct firmware *firmware; struct usb_device *udev = interface_to_usbdev(intf); - struct ath3k_data *data; - int size; BT_DBG("intf %p id %p", intf, id); if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; - data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->udev = udev; - if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { - kfree(data); return -EIO; } - size = max_t(uint, firmware->size, 4096); - data->fw_data = kmalloc(size, GFP_KERNEL); - if (!data->fw_data) { + if (ath3k_load_firmware(udev, firmware)) { release_firmware(firmware); - kfree(data); - return -ENOMEM; - } - - memcpy(data->fw_data, firmware->data, firmware->size); - data->fw_size = firmware->size; - data->fw_sent = 0; - release_firmware(firmware); - - usb_set_intfdata(intf, data); - if (ath3k_load_firmware(data, data->fw_data, data->fw_size)) { - usb_set_intfdata(intf, NULL); - kfree(data->fw_data); - kfree(data); return -EIO; } + release_firmware(firmware); return 0; } static void ath3k_disconnect(struct usb_interface *intf) { - struct ath3k_data *data = usb_get_intfdata(intf); - BT_DBG("ath3k_disconnect intf %p", intf); - - kfree(data->fw_data); - kfree(data); } static struct usb_driver ath3k_driver = { diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 959b044404f0..6e18028fc298 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -1,5 +1,14 @@ /* - * Copyright (c) 2009-2010 Atheros Communications Inc. + * Atheros Communication Bluetooth HCIATH3K UART protocol + * + * HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's + * power management protocol extension to H4 to support AR300x Bluetooth Chip. + * + * Copyright (c) 2009-2010 Atheros Communications Inc. + * + * Acknowledgements: + * This file is based on hci_h4.c, which was written + * by Maxim Krasnyansky and Marcel Holtmann. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +28,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/version.h> #include <linux/init.h> #include <linux/slab.h> @@ -32,85 +42,53 @@ #include "hci_uart.h" -#ifdef DEBUG -#define ATH_DBG(fmt, arg...) printk(KERN_ERR "[ATH_DBG] (%s) <%s>: " fmt "\n" , __FILE__ , __func__ , ## arg) -#define ATH_INFO(fmt, arg...) printk(KERN_INFO "[ATH_DBG] (%s) <%s>: " fmt "\n" , __FILE__ , __func__ , ## arg) -#else -#define ATH_DBG(fmt, arg...) -#define ATH_INFO(fmt, arg...) -#endif - - -/* HCIATH receiver States */ -#define HCIATH_W4_PACKET_TYPE 0 -#define HCIATH_W4_EVENT_HDR 1 -#define HCIATH_W4_ACL_HDR 2 -#define HCIATH_W4_SCO_HDR 3 -#define HCIATH_W4_DATA 4 - struct ath_struct { struct hci_uart *hu; - unsigned int rx_state; - unsigned int rx_count; unsigned int cur_sleep; - spinlock_t hciath_lock; - struct sk_buff *rx_skb; struct sk_buff_head txq; - wait_queue_head_t wqevt; struct work_struct ctxtsw; }; -int ath_wakeup_ar3001(struct tty_struct *tty) +static int ath_wakeup_ar3k(struct tty_struct *tty) { struct termios settings; - int status = 0x00; - mm_segment_t oldfs; - status = tty->driver->ops->tiocmget(tty, NULL); - - ATH_DBG(""); + int status = tty->driver->ops->tiocmget(tty, NULL); - if ((status & TIOCM_CTS)) + if (status & TIOCM_CTS) return status; - oldfs = get_fs(); - set_fs(KERNEL_DS); + /* Disable Automatic RTSCTS */ n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); - settings.c_cflag &= ~CRTSCTS; n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); - set_fs(oldfs); - status = tty->driver->ops->tiocmget(tty, NULL); - /* Wake up board */ + /* Clear RTS first */ + status = tty->driver->ops->tiocmget(tty, NULL); tty->driver->ops->tiocmset(tty, NULL, 0x00, TIOCM_RTS); mdelay(20); + /* Set RTS, wake up board */ status = tty->driver->ops->tiocmget(tty, NULL); - tty->driver->ops->tiocmset(tty, NULL, TIOCM_RTS, 0x00); mdelay(20); status = tty->driver->ops->tiocmget(tty, NULL); - oldfs = get_fs(); - set_fs(KERNEL_DS); - n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); + n_tty_ioctl_helper(tty, NULL, TCGETS, (unsigned long)&settings); settings.c_cflag |= CRTSCTS; n_tty_ioctl_helper(tty, NULL, TCSETS, (unsigned long)&settings); - set_fs(oldfs); + return status; } -static void ath_context_switch(struct work_struct *work) +static void ath_hci_uart_work(struct work_struct *work) { int status; struct ath_struct *ath; struct hci_uart *hu; struct tty_struct *tty; - ATH_DBG(""); - ath = container_of(work, struct ath_struct, ctxtsw); hu = ath->hu; @@ -118,8 +96,7 @@ static void ath_context_switch(struct work_struct *work) /* verify and wake up controller */ if (ath->cur_sleep) { - - status = ath_wakeup_ar3001(tty); + status = ath_wakeup_ar3k(tty); if (!(status & TIOCM_CTS)) return; } @@ -129,39 +106,24 @@ static void ath_context_switch(struct work_struct *work) hci_uart_tx_wakeup(hu); } -int ath_check_sleep_cmd(struct ath_struct *ath, unsigned char *packet) -{ - ATH_DBG(""); - - if (packet[0] == 0x04 && packet[1] == 0xFC) - ath->cur_sleep = packet[3]; - - ATH_DBG("ath->cur_sleep:%d\n", ath->cur_sleep); - - return 0; -} - - /* Initialize protocol */ static int ath_open(struct hci_uart *hu) { struct ath_struct *ath; + BT_DBG("hu %p", hu); - ATH_INFO("hu %p", hu); ath = kzalloc(sizeof(*ath), GFP_ATOMIC); if (!ath) return -ENOMEM; skb_queue_head_init(&ath->txq); - spin_lock_init(&ath->hciath_lock); - ath->cur_sleep = 0; hu->priv = ath; ath->hu = hu; - init_waitqueue_head(&ath->wqevt); - INIT_WORK(&ath->ctxtsw, ath_context_switch); + INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); + return 0; } @@ -169,8 +131,9 @@ static int ath_open(struct hci_uart *hu) static int ath_flush(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; + BT_DBG("hu %p", hu); - ATH_INFO("hu %p", hu); + skb_queue_purge(&ath->txq); return 0; @@ -180,33 +143,43 @@ static int ath_flush(struct hci_uart *hu) static int ath_close(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; + BT_DBG("hu %p", hu); - ATH_INFO("hu %p", hu); skb_queue_purge(&ath->txq); - if (ath->rx_skb) - kfree_skb(ath->rx_skb); + cancel_work_sync(&ath->ctxtsw); - wake_up_interruptible(&ath->wqevt); hu->priv = NULL; kfree(ath); + return 0; } +#define HCI_OP_ATH_SLEEP 0xFC04 + /* Enqueue frame for transmittion */ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct ath_struct *ath = hu->priv; - if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { - /* Discard SCO packet.AR3001 does not support SCO over HCI */ - BT_DBG("SCO Packet over HCI received Dropping\n"); - kfree(skb); + if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { + kfree_skb(skb); return 0; } + + /* + * Update power management enable flag with parameters of + * HCI sleep enable vendor specific HCI command. + */ + if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { + struct hci_command_hdr *hdr = (void *)skb->data; + + if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP) + ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE]; + } + BT_DBG("hu %p skb %p", hu, skb); - ATH_DBG("hu %p skb %p", hu, skb); /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); @@ -215,151 +188,187 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) set_bit(HCI_UART_SENDING, &hu->tx_state); schedule_work(&ath->ctxtsw); + return 0; } static struct sk_buff *ath_dequeue(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; - struct sk_buff *skbuf; - ATH_DBG(""); + return skb_dequeue(&ath->txq); +} - skbuf = skb_dequeue(&ath->txq); - if (skbuf != NULL) - ath_check_sleep_cmd(ath, &skbuf->data[1]); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) +#define NUM_REASSEMBLY 4 + +/* Skb helpers */ +struct ath_bt_skb_cb { + __u8 pkt_type; + __u8 incoming; + __u16 expect; + __u8 tx_seq; + __u8 retries; + __u8 sar; +}; - return skbuf; -} -static inline int ath_check_data_len(struct ath_struct *ath, int len) +static int hci_reassembly(struct hci_dev *hdev, int type, void *data, + int count, __u8 index, gfp_t gfp_mask) { - register int room = skb_tailroom(ath->rx_skb); - BT_DBG("len %d room %d", len, room); - ATH_DBG("len %d room %d", len, room); - - if (len > room) { - BT_ERR("Data length is too large"); - kfree_skb(ath->rx_skb); - ath->rx_state = HCIATH_W4_PACKET_TYPE; - ath->rx_skb = NULL; - ath->rx_count = 0; - } else { - ath->rx_state = HCIATH_W4_DATA; - ath->rx_count = len; - return len; - } + int len = 0; + int hlen = 0; + int remain = count; + struct sk_buff *skb; + struct ath_bt_skb_cb *scb; - return 0; -} + if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || + index >= NUM_REASSEMBLY) + return -EILSEQ; -/* Recv data */ -static int ath_recv(struct hci_uart *hu, void *data, int count) -{ - struct ath_struct *ath = hu->priv; - register char *ptr; - struct hci_event_hdr *eh; - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - struct sk_buff *skbuf; - register int len, type, dlen; - - skbuf = NULL; - BT_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, - ath->rx_state, ath->rx_count); - ATH_DBG("hu %p count %d rx_state %d rx_count %d", hu, count, - ath->rx_state, ath->rx_count); - ptr = data; - while (count) { - if (ath->rx_count) { - - len = min_t(unsigned int, ath->rx_count, count); - memcpy(skb_put(ath->rx_skb, len), ptr, len); - ath->rx_count -= len; - count -= len; - ptr += len; - - if (ath->rx_count) - continue; - switch (ath->rx_state) { - case HCIATH_W4_DATA: - hci_recv_frame(ath->rx_skb); - ath->rx_state = HCIATH_W4_PACKET_TYPE; - ath->rx_skb = NULL; - ath->rx_count = 0; - continue; - case HCIATH_W4_EVENT_HDR: - eh = (struct hci_event_hdr *)ath->rx_skb->data; - BT_DBG("Event header: evt 0x%2.2x plen %d", - eh->evt, eh->plen); - ATH_DBG("Event header: evt 0x%2.2x plen %d", - eh->evt, eh->plen); - ath_check_data_len(ath, eh->plen); - continue; - case HCIATH_W4_ACL_HDR: - ah = (struct hci_acl_hdr *)ath->rx_skb->data; - dlen = __le16_to_cpu(ah->dlen); - BT_DBG("ACL header: dlen %d", dlen); - ATH_DBG("ACL header: dlen %d", dlen); - ath_check_data_len(ath, dlen); - continue; - case HCIATH_W4_SCO_HDR: - sh = (struct hci_sco_hdr *)ath->rx_skb->data; - BT_DBG("SCO header: dlen %d", sh->dlen); - ATH_DBG("SCO header: dlen %d", sh->dlen); - ath_check_data_len(ath, sh->dlen); - continue; - } + skb = hdev->reassembly[index]; + + if (!skb) { + switch (type) { + case HCI_ACLDATA_PKT: + len = HCI_MAX_FRAME_SIZE; + hlen = HCI_ACL_HDR_SIZE; + break; + case HCI_EVENT_PKT: + len = HCI_MAX_EVENT_SIZE; + hlen = HCI_EVENT_HDR_SIZE; + break; + case HCI_SCODATA_PKT: + len = HCI_MAX_SCO_SIZE; + hlen = HCI_SCO_HDR_SIZE; + break; } - /* HCIATH_W4_PACKET_TYPE */ - switch (*ptr) { + skb = bt_skb_alloc(len, gfp_mask); + if (!skb) + return -ENOMEM; + + scb = (void *) skb->cb; + scb->expect = hlen; + scb->pkt_type = type; + skb->dev = (void *) hdev; + hdev->reassembly[index] = skb; + } + + while (count) { + scb = (void *) skb->cb; + len = min(scb->expect, (__u16)count); + + memcpy(skb_put(skb, len), data, len); + + count -= len; + data += len; + scb->expect -= len; + remain = count; + + switch (type) { case HCI_EVENT_PKT: - BT_DBG("Event packet"); - ATH_DBG("Event packet"); - ath->rx_state = HCIATH_W4_EVENT_HDR; - ath->rx_count = HCI_EVENT_HDR_SIZE; - type = HCI_EVENT_PKT; + if (skb->len == HCI_EVENT_HDR_SIZE) { + struct hci_event_hdr *h = hci_event_hdr(skb); + scb->expect = h->plen; + + if (skb_tailroom(skb) < scb->expect) { + kfree_skb(skb); + hdev->reassembly[index] = NULL; + return -ENOMEM; + } + } break; + case HCI_ACLDATA_PKT: - BT_DBG("ACL packet"); - ATH_DBG("ACL packet"); - ath->rx_state = HCIATH_W4_ACL_HDR; - ath->rx_count = HCI_ACL_HDR_SIZE; - type = HCI_ACLDATA_PKT; + if (skb->len == HCI_ACL_HDR_SIZE) { + struct hci_acl_hdr *h = hci_acl_hdr(skb); + scb->expect = __le16_to_cpu(h->dlen); + + if (skb_tailroom(skb) < scb->expect) { + kfree_skb(skb); + hdev->reassembly[index] = NULL; + return -ENOMEM; + } + } break; + case HCI_SCODATA_PKT: - BT_DBG("SCO packet"); - ATH_DBG("SCO packet"); - ath->rx_state = HCIATH_W4_SCO_HDR; - ath->rx_count = HCI_SCO_HDR_SIZE; - type = HCI_SCODATA_PKT; + if (skb->len == HCI_SCO_HDR_SIZE) { + struct hci_sco_hdr *h = hci_sco_hdr(skb); + scb->expect = h->dlen; + + if (skb_tailroom(skb) < scb->expect) { + kfree_skb(skb); + hdev->reassembly[index] = NULL; + return -ENOMEM; + } + } break; - default: - BT_ERR("Unknown HCI packet type %2.2x", (__u8) *ptr); - hu->hdev->stat.err_rx++; - ptr++; - count--; - continue; - }; - ptr++; - count--; - - /* Allocate packet */ - ath->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!ath->rx_skb) { - BT_ERR("Can't allocate mem for new packet"); - ath->rx_state = HCIATH_W4_PACKET_TYPE; - ath->rx_count = 0; - return -ENOMEM; } - ath->rx_skb->dev = (void *)hu->hdev; - bt_cb(ath->rx_skb)->pkt_type = type; - } return count; + + if (scb->expect == 0) { + /* Complete frame */ + + bt_cb(skb)->pkt_type = type; + hci_recv_frame(skb); + + hdev->reassembly[index] = NULL; + return remain; + } + } + + return remain; +} + +#define STREAM_REASSEMBLY 0 + +static int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) +{ + int type; + int rem = 0; + + while (count) { + struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; + + if (!skb) { + struct { char type; } *pkt; + + /* Start of the frame */ + pkt = data; + type = pkt->type; + + data++; + count--; + } else + type = bt_cb(skb)->pkt_type; + + rem = hci_reassembly(hdev, type, data, + count, STREAM_REASSEMBLY, GFP_KERNEL); + if (rem < 0) + return rem; + + data += (count - rem); + count = rem; + }; + + return rem; +} +#endif + +/* Recv data */ +static int ath_recv(struct hci_uart *hu, void *data, int count) +{ + int ret; + ret = hci_recv_stream_fragment(hu->hdev, data, count); + if (ret < 0) + BT_ERR("Frame Reassembly Failed: %d", ret); + + return count; } static struct hci_uart_proto athp = { - .id = HCI_UART_ATH, + .id = HCI_UART_ATH3K, .open = ath_open, .close = ath_close, .recv = ath_recv, @@ -368,19 +377,19 @@ static struct hci_uart_proto athp = { .flush = ath_flush, }; -int ath_init(void) +int __init ath_init(void) { int err = hci_uart_register_proto(&athp); - ATH_INFO(""); + if (!err) - BT_INFO("HCIATH protocol initialized"); + BT_INFO("HCIATH3K protocol initialized"); else - BT_ERR("HCIATH protocol registration failed with err %d", err); + BT_ERR("HCIATH3K protocol registration failed"); + return err; } -int ath_deinit(void) +int __exit ath_deinit(void) { - ATH_INFO(""); return hci_uart_unregister_proto(&athp); } diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index b5c2401ee870..3c6cabcb7d84 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -101,7 +101,7 @@ static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type) break; case HCI_SCODATA_PKT: - hdev->stat.cmd_tx++; + hdev->stat.sco_tx++; break; } } @@ -210,7 +210,6 @@ static int hci_uart_close(struct hci_dev *hdev) static int hci_uart_send_frame(struct sk_buff *skb) { struct hci_dev* hdev = (struct hci_dev *) skb->dev; - struct tty_struct *tty; struct hci_uart *hu; if (!hdev) { @@ -222,7 +221,6 @@ static int hci_uart_send_frame(struct sk_buff *skb) return -EBUSY; hu = (struct hci_uart *) hdev->driver_data; - tty = hu->tty; BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); @@ -258,9 +256,16 @@ static int hci_uart_tty_open(struct tty_struct *tty) BT_DBG("tty %p", tty); + /* FIXME: This btw is bogus, nothing requires the old ldisc to clear + the pointer */ if (hu) return -EEXIST; + /* Error if the tty has no write op instead of leaving an exploitable + hole */ + if (tty->ops->write == NULL) + return -EOPNOTSUPP; + if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) { BT_ERR("Can't allocate control structure"); return -ENFILE; @@ -306,8 +311,10 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { hu->proto->close(hu); - hci_unregister_dev(hdev); - hci_free_dev(hdev); + if (hdev) { + hci_unregister_dev(hdev); + hci_free_dev(hdev); + } } } } @@ -397,6 +404,9 @@ static int hci_uart_register_dev(struct hci_uart *hu) if (!reset) set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) + set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); @@ -477,6 +487,15 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, return hu->hdev->id; return -EUNATCH; + case HCIUARTSETFLAGS: + if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return -EBUSY; + hu->hdev_flags = arg; + break; + + case HCIUARTGETFLAGS: + return hu->hdev_flags; + default: err = n_tty_ioctl_helper(tty, file, cmd, arg); break; @@ -542,9 +561,10 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_LL ll_init(); #endif -#ifdef CONFIG_BT_HCIUART_ATH - ath_init(); +#ifdef CONFIG_BT_HCIUART_ATH3K + ath_init(); #endif + return 0; } @@ -561,9 +581,10 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_LL ll_deinit(); #endif -#ifdef CONFIG_BT_HCIUART_ATH - ath_deinit(); +#ifdef CONFIG_BT_HCIUART_ATH3K + ath_deinit(); #endif + /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) BT_ERR("Can't unregister HCI line discipline (%d)", err); diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 94dd9c529e93..99fb35239d1f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -31,6 +31,8 @@ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) #define HCIUARTGETDEVICE _IOR('U', 202, int) +#define HCIUARTSETFLAGS _IOW('U', 203, int) +#define HCIUARTGETFLAGS _IOR('U', 204, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 6 @@ -40,7 +42,9 @@ #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 -#define HCI_UART_ATH 5 +#define HCI_UART_ATH3K 5 + +#define HCI_UART_RAW_DEVICE 0 struct hci_uart; @@ -58,6 +62,7 @@ struct hci_uart { struct tty_struct *tty; struct hci_dev *hdev; unsigned long flags; + unsigned long hdev_flags; struct hci_uart_proto *proto; void *priv; @@ -67,7 +72,7 @@ struct hci_uart { spinlock_t rx_lock; }; -/* HCI_UART flag bits */ +/* HCI_UART proto flag bits */ #define HCI_UART_PROTO_SET 0 /* TX states */ @@ -93,8 +98,7 @@ int ll_init(void); int ll_deinit(void); #endif -#ifdef CONFIG_BT_HCIUART_ATH +#ifdef CONFIG_BT_HCIUART_ATH3K int ath_init(void); int ath_deinit(void); #endif - |