summaryrefslogtreecommitdiff
path: root/drivers/bluetooth
diff options
context:
space:
mode:
authorZhang Jiejing <jiejing.zhang@freescale.com>2011-04-01 10:22:19 +0800
committerZhang Jiejing <jiejing.zhang@freescale.com>2011-04-01 19:57:39 +0800
commit1895a5df9b541fba44cd033a265460dc7261d521 (patch)
treef5aeee549b85e970815bbf26655e092877fa829a /drivers/bluetooth
parentacba90905508678b5d54938bc726d8091cca38d8 (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/Kconfig23
-rw-r--r--drivers/bluetooth/Makefile3
-rw-r--r--drivers/bluetooth/ath3k.c82
-rw-r--r--drivers/bluetooth/hci_ath.c407
-rw-r--r--drivers/bluetooth/hci_ldisc.c39
-rw-r--r--drivers/bluetooth/hci_uart.h12
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
-