summaryrefslogtreecommitdiffstats
path: root/drivers/bluetooth
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
committerTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
commitfcbb27b0ec6dcbc5a5108cb8fb19eae64593d204 (patch)
tree22962a4387943edc841c72a4e636a068c66d58fd /drivers/bluetooth
downloadast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.zip
ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.tar.gz
Initial import of modified Linux 2.6.28 tree
Original upstream URL: git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git | branch linux-2.6.28.y
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r--drivers/bluetooth/Kconfig194
-rw-r--r--drivers/bluetooth/Makefile23
-rw-r--r--drivers/bluetooth/bcm203x.c306
-rw-r--r--drivers/bluetooth/bfusb.c797
-rw-r--r--drivers/bluetooth/bluecard_cs.c980
-rw-r--r--drivers/bluetooth/bpa10x.c545
-rw-r--r--drivers/bluetooth/bt3c_cs.c811
-rw-r--r--drivers/bluetooth/btsdio.c405
-rw-r--r--drivers/bluetooth/btuart_cs.c739
-rw-r--r--drivers/bluetooth/btusb.c995
-rw-r--r--drivers/bluetooth/dtl1_cs.c689
-rw-r--r--drivers/bluetooth/hci_bcsp.c767
-rw-r--r--drivers/bluetooth/hci_h4.c296
-rw-r--r--drivers/bluetooth/hci_ldisc.c584
-rw-r--r--drivers/bluetooth/hci_ll.c536
-rw-r--r--drivers/bluetooth/hci_uart.h93
-rw-r--r--drivers/bluetooth/hci_usb.c1136
-rw-r--r--drivers/bluetooth/hci_usb.h129
-rw-r--r--drivers/bluetooth/hci_vhci.c383
19 files changed, 10408 insertions, 0 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
new file mode 100644
index 0000000..7cb4029
--- /dev/null
+++ b/drivers/bluetooth/Kconfig
@@ -0,0 +1,194 @@
+
+menu "Bluetooth device drivers"
+ depends on BT
+
+config BT_HCIUSB
+ tristate "HCI USB driver (old version)"
+ depends on USB && BT_HCIBTUSB=n
+ help
+ Bluetooth HCI USB driver.
+ This driver is required if you want to use Bluetooth devices with
+ USB interface.
+
+ Say Y here to compile support for Bluetooth USB devices into the
+ kernel or say M to compile it as module (hci_usb).
+
+config BT_HCIUSB_SCO
+ bool "SCO (voice) support"
+ depends on BT_HCIUSB
+ help
+ This option enables the SCO support in the HCI USB driver. You need this
+ to transmit voice data with your Bluetooth USB device.
+
+ Say Y here to compile support for SCO over HCI USB.
+
+config BT_HCIBTUSB
+ tristate "HCI USB driver"
+ depends on USB
+ help
+ Bluetooth HCI USB driver.
+ This driver is required if you want to use Bluetooth devices with
+ USB interface.
+
+ Say Y here to compile support for Bluetooth USB devices into the
+ kernel or say M to compile it as module (btusb).
+
+config BT_HCIBTSDIO
+ tristate "HCI SDIO driver"
+ depends on MMC
+ help
+ Bluetooth HCI SDIO driver.
+ This driver is required if you want to use Bluetooth device with
+ SDIO interface.
+
+ Say Y here to compile support for Bluetooth SDIO devices into the
+ kernel or say M to compile it as module (btsdio).
+
+config BT_HCIUART
+ tristate "HCI UART driver"
+ help
+ Bluetooth HCI UART driver.
+ This driver is required if you want to use Bluetooth devices with
+ serial port interface. You will also need this driver if you have
+ UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card
+ adapter and BrainBoxes Bluetooth PC Card.
+
+ Say Y here to compile support for Bluetooth UART devices into the
+ kernel or say M to compile it as module (hci_uart).
+
+config BT_HCIUART_H4
+ bool "UART (H4) protocol support"
+ depends on BT_HCIUART
+ help
+ UART (H4) is serial protocol for communication between Bluetooth
+ device and host. This protocol is required for most Bluetooth devices
+ with UART interface, including PCMCIA and CF cards.
+
+ Say Y here to compile support for HCI UART (H4) protocol.
+
+config BT_HCIUART_BCSP
+ bool "BCSP protocol support"
+ depends on BT_HCIUART
+ select BITREVERSE
+ help
+ BCSP (BlueCore Serial Protocol) is serial protocol for communication
+ between Bluetooth device and host. This protocol is required for non
+ USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
+ CF cards.
+
+ Say Y here to compile support for HCI BCSP protocol.
+
+config BT_HCIUART_LL
+ bool "HCILL protocol support"
+ depends on BT_HCIUART
+ help
+ HCILL (HCI Low Level) is a serial protocol for communication
+ between Bluetooth device and host. This protocol is required for
+ serial Bluetooth devices that are based on Texas Instruments'
+ BRF chips.
+
+ Say Y here to compile support for HCILL protocol.
+
+config BT_HCIBCM203X
+ tristate "HCI BCM203x USB driver"
+ depends on USB
+ select FW_LOADER
+ help
+ Bluetooth HCI BCM203x USB driver.
+ This driver provides the firmware loading mechanism for the Broadcom
+ Blutonium based devices.
+
+ Say Y here to compile support for HCI BCM203x devices into the
+ kernel or say M to compile it as module (bcm203x).
+
+config BT_HCIBPA10X
+ tristate "HCI BPA10x USB driver"
+ depends on USB
+ help
+ Bluetooth HCI BPA10x USB driver.
+ This driver provides support for the Digianswer BPA 100/105 Bluetooth
+ sniffer devices.
+
+ Say Y here to compile support for HCI BPA10x devices into the
+ kernel or say M to compile it as module (bpa10x).
+
+config BT_HCIBFUSB
+ tristate "HCI BlueFRITZ! USB driver"
+ depends on USB
+ select FW_LOADER
+ help
+ Bluetooth HCI BlueFRITZ! USB driver.
+ This driver provides support for Bluetooth USB devices with AVM
+ interface:
+ AVM BlueFRITZ! USB
+
+ Say Y here to compile support for HCI BFUSB devices into the
+ kernel or say M to compile it as module (bfusb).
+
+config BT_HCIDTL1
+ tristate "HCI DTL1 (PC Card) driver"
+ depends on PCMCIA
+ help
+ Bluetooth HCI DTL1 (PC Card) driver.
+ This driver provides support for Bluetooth PCMCIA devices with
+ Nokia DTL1 interface:
+ Nokia Bluetooth Card
+ Socket Bluetooth CF Card
+
+ Say Y here to compile support for HCI DTL1 devices into the
+ kernel or say M to compile it as module (dtl1_cs).
+
+config BT_HCIBT3C
+ tristate "HCI BT3C (PC Card) driver"
+ depends on PCMCIA
+ select FW_LOADER
+ help
+ Bluetooth HCI BT3C (PC Card) driver.
+ This driver provides support for Bluetooth PCMCIA devices with
+ 3Com BT3C interface:
+ 3Com Bluetooth Card (3CRWB6096)
+ HP Bluetooth Card
+
+ Say Y here to compile support for HCI BT3C devices into the
+ kernel or say M to compile it as module (bt3c_cs).
+
+config BT_HCIBLUECARD
+ tristate "HCI BlueCard (PC Card) driver"
+ depends on PCMCIA
+ help
+ Bluetooth HCI BlueCard (PC Card) driver.
+ This driver provides support for Bluetooth PCMCIA devices with
+ Anycom BlueCard interface:
+ Anycom Bluetooth PC Card
+ Anycom Bluetooth CF Card
+
+ Say Y here to compile support for HCI BlueCard devices into the
+ kernel or say M to compile it as module (bluecard_cs).
+
+config BT_HCIBTUART
+ tristate "HCI UART (PC Card) device driver"
+ depends on PCMCIA
+ help
+ Bluetooth HCI UART (PC Card) driver.
+ This driver provides support for Bluetooth PCMCIA devices with
+ an UART interface:
+ Xircom CreditCard Bluetooth Adapter
+ Xircom RealPort2 Bluetooth Adapter
+ Sphinx PICO Card
+ H-Soft blue+Card
+ Cyber-blue Compact Flash Card
+
+ Say Y here to compile support for HCI UART devices into the
+ kernel or say M to compile it as module (btuart_cs).
+
+config BT_HCIVHCI
+ tristate "HCI VHCI (Virtual HCI device) driver"
+ help
+ Bluetooth Virtual HCI device driver.
+ This driver is required if you want to use HCI Emulation software.
+
+ Say Y here to compile support for virtual HCI devices into the
+ kernel or say M to compile it as module (hci_vhci).
+
+endmenu
+
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
new file mode 100644
index 0000000..77444af
--- /dev/null
+++ b/drivers/bluetooth/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for the Linux Bluetooth HCI device drivers.
+#
+
+obj-$(CONFIG_BT_HCIUSB) += hci_usb.o
+obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
+obj-$(CONFIG_BT_HCIUART) += hci_uart.o
+obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
+obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o
+obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
+obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
+obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
+obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
+obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
+
+obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
+obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
+
+hci_uart-y := hci_ldisc.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-objs := $(hci_uart-y)
diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c
new file mode 100644
index 0000000..ee40201
--- /dev/null
+++ b/drivers/bluetooth/bcm203x.c
@@ -0,0 +1,306 @@
+/*
+ *
+ * Broadcom Blutonium firmware driver
+ *
+ * Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#ifndef CONFIG_BT_HCIBCM203X_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.2"
+
+static struct usb_device_id bcm203x_table[] = {
+ /* Broadcom Blutonium (BCM2033) */
+ { USB_DEVICE(0x0a5c, 0x2033) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bcm203x_table);
+
+#define BCM203X_ERROR 0
+#define BCM203X_RESET 1
+#define BCM203X_LOAD_MINIDRV 2
+#define BCM203X_SELECT_MEMORY 3
+#define BCM203X_CHECK_MEMORY 4
+#define BCM203X_LOAD_FIRMWARE 5
+#define BCM203X_CHECK_FIRMWARE 6
+
+#define BCM203X_IN_EP 0x81
+#define BCM203X_OUT_EP 0x02
+
+struct bcm203x_data {
+ struct usb_device *udev;
+
+ unsigned long state;
+
+ struct work_struct work;
+
+ struct urb *urb;
+ unsigned char *buffer;
+
+ unsigned char *fw_data;
+ unsigned int fw_size;
+ unsigned int fw_sent;
+};
+
+static void bcm203x_complete(struct urb *urb)
+{
+ struct bcm203x_data *data = urb->context;
+ struct usb_device *udev = urb->dev;
+ int len;
+
+ BT_DBG("udev %p urb %p", udev, urb);
+
+ if (urb->status) {
+ BT_ERR("URB failed with status %d", urb->status);
+ data->state = BCM203X_ERROR;
+ return;
+ }
+
+ switch (data->state) {
+ case BCM203X_LOAD_MINIDRV:
+ memcpy(data->buffer, "#", 1);
+
+ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, 1, bcm203x_complete, data);
+
+ data->state = BCM203X_SELECT_MEMORY;
+
+ schedule_work(&data->work);
+ break;
+
+ case BCM203X_SELECT_MEMORY:
+ usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32, bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_MEMORY;
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_MEMORY:
+ if (data->buffer[0] != '#') {
+ BT_ERR("Memory select failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_LOAD_FIRMWARE;
+
+ case BCM203X_LOAD_FIRMWARE:
+ if (data->fw_sent == data->fw_size) {
+ usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
+ data->buffer, 32, bcm203x_complete, data, 1);
+
+ data->state = BCM203X_CHECK_FIRMWARE;
+ } else {
+ len = min_t(uint, data->fw_size - data->fw_sent, 4096);
+
+ usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->fw_data + data->fw_sent, len, bcm203x_complete, data);
+
+ data->fw_sent += len;
+ }
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+ break;
+
+ case BCM203X_CHECK_FIRMWARE:
+ if (data->buffer[0] != '.') {
+ BT_ERR("Firmware loading failed");
+ data->state = BCM203X_ERROR;
+ break;
+ }
+
+ data->state = BCM203X_RESET;
+ break;
+ }
+}
+
+static void bcm203x_work(struct work_struct *work)
+{
+ struct bcm203x_data *data =
+ container_of(work, struct bcm203x_data, work);
+
+ if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
+ BT_ERR("Can't submit URB");
+}
+
+static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct bcm203x_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) {
+ BT_ERR("Can't allocate memory for data structure");
+ return -ENOMEM;
+ }
+
+ data->udev = udev;
+ data->state = BCM203X_LOAD_MINIDRV;
+
+ data->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!data->urb) {
+ BT_ERR("Can't allocate URB");
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
+ BT_ERR("Mini driver request failed");
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("minidrv data %p size %d", firmware->data, firmware->size);
+
+ size = max_t(uint, firmware->size, 4096);
+
+ data->buffer = kmalloc(size, GFP_KERNEL);
+ if (!data->buffer) {
+ BT_ERR("Can't allocate memory for mini driver");
+ release_firmware(firmware);
+ usb_free_urb(data->urb);
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ memcpy(data->buffer, firmware->data, firmware->size);
+
+ usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
+ data->buffer, firmware->size, bcm203x_complete, data);
+
+ release_firmware(firmware);
+
+ if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ usb_free_urb(data->urb);
+ kfree(data->buffer);
+ kfree(data);
+ return -EIO;
+ }
+
+ BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+
+ data->fw_data = kmalloc(firmware->size, GFP_KERNEL);
+ if (!data->fw_data) {
+ BT_ERR("Can't allocate memory for firmware image");
+ release_firmware(firmware);
+ usb_free_urb(data->urb);
+ kfree(data->buffer);
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ memcpy(data->fw_data, firmware->data, firmware->size);
+ data->fw_size = firmware->size;
+ data->fw_sent = 0;
+
+ release_firmware(firmware);
+
+ INIT_WORK(&data->work, bcm203x_work);
+
+ usb_set_intfdata(intf, data);
+
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void bcm203x_disconnect(struct usb_interface *intf)
+{
+ struct bcm203x_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ usb_kill_urb(data->urb);
+
+ usb_set_intfdata(intf, NULL);
+
+ usb_free_urb(data->urb);
+ kfree(data->fw_data);
+ kfree(data->buffer);
+ kfree(data);
+}
+
+static struct usb_driver bcm203x_driver = {
+ .name = "bcm203x",
+ .probe = bcm203x_probe,
+ .disconnect = bcm203x_disconnect,
+ .id_table = bcm203x_table,
+};
+
+static int __init bcm203x_init(void)
+{
+ int err;
+
+ BT_INFO("Broadcom Blutonium firmware driver ver %s", VERSION);
+
+ err = usb_register(&bcm203x_driver);
+ if (err < 0)
+ BT_ERR("Failed to register USB driver");
+
+ return err;
+}
+
+static void __exit bcm203x_exit(void)
+{
+ usb_deregister(&bcm203x_driver);
+}
+
+module_init(bcm203x_init);
+module_exit(bcm203x_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BCM2033-MD.hex");
+MODULE_FIRMWARE("BCM2033-FW.bin");
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
new file mode 100644
index 0000000..90a0946
--- /dev/null
+++ b/drivers/bluetooth/bfusb.c
@@ -0,0 +1,797 @@
+/*
+ *
+ * AVM BlueFRITZ! USB driver
+ *
+ * Copyright (C) 2003-2006 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIBFUSB_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.2"
+
+static struct usb_driver bfusb_driver;
+
+static struct usb_device_id bfusb_table[] = {
+ /* AVM BlueFRITZ! USB */
+ { USB_DEVICE(0x057c, 0x2200) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bfusb_table);
+
+#define BFUSB_MAX_BLOCK_SIZE 256
+
+#define BFUSB_BLOCK_TIMEOUT 3000
+
+#define BFUSB_TX_PROCESS 1
+#define BFUSB_TX_WAKEUP 2
+
+#define BFUSB_MAX_BULK_TX 2
+#define BFUSB_MAX_BULK_RX 2
+
+struct bfusb_data {
+ struct hci_dev *hdev;
+
+ unsigned long state;
+
+ struct usb_device *udev;
+
+ unsigned int bulk_in_ep;
+ unsigned int bulk_out_ep;
+ unsigned int bulk_pkt_size;
+
+ rwlock_t lock;
+
+ struct sk_buff_head transmit_q;
+
+ struct sk_buff *reassembly;
+
+ atomic_t pending_tx;
+ struct sk_buff_head pending_q;
+ struct sk_buff_head completed_q;
+};
+
+struct bfusb_data_scb {
+ struct urb *urb;
+};
+
+static void bfusb_tx_complete(struct urb *urb);
+static void bfusb_rx_complete(struct urb *urb);
+
+static struct urb *bfusb_get_completed(struct bfusb_data *data)
+{
+ struct sk_buff *skb;
+ struct urb *urb = NULL;
+
+ BT_DBG("bfusb %p", data);
+
+ skb = skb_dequeue(&data->completed_q);
+ if (skb) {
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+ kfree_skb(skb);
+ }
+
+ return urb;
+}
+
+static void bfusb_unlink_urbs(struct bfusb_data *data)
+{
+ struct sk_buff *skb;
+ struct urb *urb;
+
+ BT_DBG("bfusb %p", data);
+
+ while ((skb = skb_dequeue(&data->pending_q))) {
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
+ usb_kill_urb(urb);
+ skb_queue_tail(&data->completed_q, skb);
+ }
+
+ while ((urb = bfusb_get_completed(data)))
+ usb_free_urb(urb);
+}
+
+static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
+{
+ struct bfusb_data_scb *scb = (void *) skb->cb;
+ struct urb *urb = bfusb_get_completed(data);
+ int err, pipe;
+
+ BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
+
+ if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
+ bfusb_tx_complete, skb);
+
+ scb->urb = urb;
+
+ skb_queue_tail(&data->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk tx submit failed urb %p err %d",
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
+ usb_free_urb(urb);
+ } else
+ atomic_inc(&data->pending_tx);
+
+ return err;
+}
+
+static void bfusb_tx_wakeup(struct bfusb_data *data)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("bfusb %p", data);
+
+ if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
+ set_bit(BFUSB_TX_WAKEUP, &data->state);
+ return;
+ }
+
+ do {
+ clear_bit(BFUSB_TX_WAKEUP, &data->state);
+
+ while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
+ (skb = skb_dequeue(&data->transmit_q))) {
+ if (bfusb_send_bulk(data, skb) < 0) {
+ skb_queue_head(&data->transmit_q, skb);
+ break;
+ }
+ }
+
+ } while (test_bit(BFUSB_TX_WAKEUP, &data->state));
+
+ clear_bit(BFUSB_TX_PROCESS, &data->state);
+}
+
+static void bfusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+
+ BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
+
+ atomic_dec(&data->pending_tx);
+
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+ return;
+
+ if (!urb->status)
+ data->hdev->stat.byte_tx += skb->len;
+ else
+ data->hdev->stat.err_tx++;
+
+ read_lock(&data->lock);
+
+ skb_unlink(skb, &data->pending_q);
+ skb_queue_tail(&data->completed_q, skb);
+
+ bfusb_tx_wakeup(data);
+
+ read_unlock(&data->lock);
+}
+
+
+static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
+{
+ struct bfusb_data_scb *scb;
+ struct sk_buff *skb;
+ int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
+
+ BT_DBG("bfusb %p urb %p", bfusb, urb);
+
+ if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+ return -ENOMEM;
+
+ skb = bt_skb_alloc(size, GFP_ATOMIC);
+ if (!skb) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) data;
+
+ scb = (struct bfusb_data_scb *) skb->cb;
+ scb->urb = urb;
+
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
+ bfusb_rx_complete, skb);
+
+ skb_queue_tail(&data->pending_q, skb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk rx submit failed urb %p err %d",
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
+ kfree_skb(skb);
+ usb_free_urb(urb);
+ }
+
+ return err;
+}
+
+static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
+{
+ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
+
+ if (hdr & 0x10) {
+ BT_ERR("%s error in block", data->hdev->name);
+ if (data->reassembly)
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
+ return -EIO;
+ }
+
+ if (hdr & 0x04) {
+ struct sk_buff *skb;
+ unsigned char pkt_type;
+ int pkt_len = 0;
+
+ if (data->reassembly) {
+ BT_ERR("%s unexpected start block", data->hdev->name);
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
+ }
+
+ if (len < 1) {
+ BT_ERR("%s no packet type found", data->hdev->name);
+ return -EPROTO;
+ }
+
+ pkt_type = *buf++; len--;
+
+ switch (pkt_type) {
+ case HCI_EVENT_PKT:
+ if (len >= HCI_EVENT_HDR_SIZE) {
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
+ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+ } else {
+ BT_ERR("%s event block is too short", data->hdev->name);
+ return -EILSEQ;
+ }
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (len >= HCI_ACL_HDR_SIZE) {
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
+ pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
+ } else {
+ BT_ERR("%s data block is too short", data->hdev->name);
+ return -EILSEQ;
+ }
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (len >= HCI_SCO_HDR_SIZE) {
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
+ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+ } else {
+ BT_ERR("%s audio block is too short", data->hdev->name);
+ return -EILSEQ;
+ }
+ break;
+ }
+
+ skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for the packet", data->hdev->name);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) data->hdev;
+ bt_cb(skb)->pkt_type = pkt_type;
+
+ data->reassembly = skb;
+ } else {
+ if (!data->reassembly) {
+ BT_ERR("%s unexpected continuation block", data->hdev->name);
+ return -EIO;
+ }
+ }
+
+ if (len > 0)
+ memcpy(skb_put(data->reassembly, len), buf, len);
+
+ if (hdr & 0x08) {
+ hci_recv_frame(data->reassembly);
+ data->reassembly = NULL;
+ }
+
+ return 0;
+}
+
+static void bfusb_rx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
+ unsigned char *buf = urb->transfer_buffer;
+ int count = urb->actual_length;
+ int err, hdr, len;
+
+ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+
+ read_lock(&data->lock);
+
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+ goto unlock;
+
+ if (urb->status || !count)
+ goto resubmit;
+
+ data->hdev->stat.byte_rx += count;
+
+ skb_put(skb, count);
+
+ while (count) {
+ hdr = buf[0] | (buf[1] << 8);
+
+ if (hdr & 0x4000) {
+ len = 0;
+ count -= 2;
+ buf += 2;
+ } else {
+ len = (buf[2] == 0) ? 256 : buf[2];
+ count -= 3;
+ buf += 3;
+ }
+
+ if (count < len) {
+ BT_ERR("%s block extends over URB buffer ranges",
+ data->hdev->name);
+ }
+
+ if ((hdr & 0xe1) == 0xc1)
+ bfusb_recv_block(data, hdr, buf, len);
+
+ count -= len;
+ buf += len;
+ }
+
+ skb_unlink(skb, &data->pending_q);
+ kfree_skb(skb);
+
+ bfusb_rx_submit(data, urb);
+
+ read_unlock(&data->lock);
+
+ return;
+
+resubmit:
+ urb->dev = data->udev;
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk resubmit failed urb %p err %d",
+ data->hdev->name, urb, err);
+ }
+
+unlock:
+ read_unlock(&data->lock);
+}
+
+static int bfusb_open(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+ unsigned long flags;
+ int i, err;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&data->lock, flags);
+
+ err = bfusb_rx_submit(data, NULL);
+ if (!err) {
+ for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
+ bfusb_rx_submit(data, NULL);
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ }
+
+ write_unlock_irqrestore(&data->lock, flags);
+
+ return err;
+}
+
+static int bfusb_flush(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ skb_queue_purge(&data->transmit_q);
+
+ return 0;
+}
+
+static int bfusb_close(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+ unsigned long flags;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&data->lock, flags);
+ write_unlock_irqrestore(&data->lock, flags);
+
+ bfusb_unlink_urbs(data);
+ bfusb_flush(hdev);
+
+ return 0;
+}
+
+static int bfusb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct bfusb_data *data;
+ struct sk_buff *nskb;
+ unsigned char buf[3];
+ int sent = 0, size, count;
+
+ BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ data = hdev->driver_data;
+
+ switch (bt_cb(skb)->pkt_type) {
+ 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;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ count = skb->len;
+
+ /* Max HCI frame size seems to be 1511 + 1 */
+ nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
+ if (!nskb) {
+ BT_ERR("Can't allocate memory for new packet");
+ return -ENOMEM;
+ }
+
+ nskb->dev = (void *) data;
+
+ while (count) {
+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
+
+ buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
+ buf[1] = 0x00;
+ buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
+
+ memcpy(skb_put(nskb, 3), buf, 3);
+ skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
+
+ sent += size;
+ count -= size;
+ }
+
+ /* Don't send frame with multiple size of bulk max packet */
+ if ((nskb->len % data->bulk_pkt_size) == 0) {
+ buf[0] = 0xdd;
+ buf[1] = 0x00;
+ memcpy(skb_put(nskb, 2), buf, 2);
+ }
+
+ read_lock(&data->lock);
+
+ skb_queue_tail(&data->transmit_q, nskb);
+ bfusb_tx_wakeup(data);
+
+ read_unlock(&data->lock);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void bfusb_destruct(struct hci_dev *hdev)
+{
+ struct bfusb_data *data = hdev->driver_data;
+
+ BT_DBG("hdev %p bfusb %p", hdev, data);
+
+ kfree(data);
+}
+
+static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+static int bfusb_load_firmware(struct bfusb_data *data,
+ const unsigned char *firmware, int count)
+{
+ unsigned char *buf;
+ int err, pipe, len, size, sent = 0;
+
+ BT_DBG("bfusb %p udev %p", data, data->udev);
+
+ BT_INFO("BlueFRITZ! USB loading firmware");
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+
+ if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
+ BT_ERR("Can't change to loading configuration");
+ return -EBUSY;
+ }
+
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+ buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
+ if (!buf) {
+ BT_ERR("Can't allocate memory chunk for firmware");
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
+
+ while (count) {
+ size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
+
+ memcpy(buf, firmware + sent, size);
+
+ err = usb_bulk_msg(data->udev, pipe, buf, size,
+ &len, BFUSB_BLOCK_TIMEOUT);
+
+ if (err || (len != size)) {
+ BT_ERR("Error in firmware loading");
+ goto error;
+ }
+
+ sent += size;
+ count -= size;
+ }
+
+ err = usb_bulk_msg(data->udev, pipe, NULL, 0,
+ &len, BFUSB_BLOCK_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("Error in null packet request");
+ goto error;
+ }
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+
+ err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ BT_ERR("Can't change to running configuration");
+ goto error;
+ }
+
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
+
+ BT_INFO("BlueFRITZ! USB device ready");
+
+ kfree(buf);
+ return 0;
+
+error:
+ kfree(buf);
+
+ pipe = usb_sndctrlpipe(data->udev, 0);
+
+ usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ return err;
+}
+
+static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ const struct firmware *firmware;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_host_endpoint *bulk_out_ep;
+ struct usb_host_endpoint *bulk_in_ep;
+ struct hci_dev *hdev;
+ struct bfusb_data *data;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ /* Check number of endpoints */
+ if (intf->cur_altsetting->desc.bNumEndpoints < 2)
+ return -EIO;
+
+ bulk_out_ep = &intf->cur_altsetting->endpoint[0];
+ bulk_in_ep = &intf->cur_altsetting->endpoint[1];
+
+ if (!bulk_out_ep || !bulk_in_ep) {
+ BT_ERR("Bulk endpoints not found");
+ goto done;
+ }
+
+ /* Initialize control structure and load firmware */
+ data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL);
+ if (!data) {
+ BT_ERR("Can't allocate memory for control structure");
+ goto done;
+ }
+
+ data->udev = udev;
+ data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
+ data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
+ data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
+
+ rwlock_init(&data->lock);
+
+ data->reassembly = NULL;
+
+ skb_queue_head_init(&data->transmit_q);
+ skb_queue_head_init(&data->pending_q);
+ skb_queue_head_init(&data->completed_q);
+
+ if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
+ BT_ERR("Firmware request failed");
+ goto error;
+ }
+
+ BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
+
+ if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
+ BT_ERR("Firmware loading failed");
+ goto release;
+ }
+
+ release_firmware(firmware);
+
+ /* Initialize and register HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ goto error;
+ }
+
+ data->hdev = hdev;
+
+ hdev->type = HCI_USB;
+ hdev->driver_data = data;
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = bfusb_open;
+ hdev->close = bfusb_close;
+ hdev->flush = bfusb_flush;
+ hdev->send = bfusb_send_frame;
+ hdev->destruct = bfusb_destruct;
+ hdev->ioctl = bfusb_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ goto error;
+ }
+
+ usb_set_intfdata(intf, data);
+
+ return 0;
+
+release:
+ release_firmware(firmware);
+
+error:
+ kfree(data);
+
+done:
+ return -EIO;
+}
+
+static void bfusb_disconnect(struct usb_interface *intf)
+{
+ struct bfusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
+
+ BT_DBG("intf %p", intf);
+
+ if (!hdev)
+ return;
+
+ usb_set_intfdata(intf, NULL);
+
+ bfusb_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+ hci_free_dev(hdev);
+}
+
+static struct usb_driver bfusb_driver = {
+ .name = "bfusb",
+ .probe = bfusb_probe,
+ .disconnect = bfusb_disconnect,
+ .id_table = bfusb_table,
+};
+
+static int __init bfusb_init(void)
+{
+ int err;
+
+ BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
+
+ err = usb_register(&bfusb_driver);
+ if (err < 0)
+ BT_ERR("Failed to register BlueFRITZ! USB driver");
+
+ return err;
+}
+
+static void __exit bfusb_exit(void)
+{
+ usb_deregister(&bfusb_driver);
+}
+
+module_init(bfusb_init);
+module_exit(bfusb_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("bfubase.frm");
diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
new file mode 100644
index 0000000..b0e569b
--- /dev/null
+++ b/drivers/bluetooth/bluecard_cs.c
@@ -0,0 +1,980 @@
+/*
+ *
+ * Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+ struct timer_list timer; /* For LED control */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+
+ unsigned char ctrl_reg;
+ unsigned long hw_state; /* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+static int bluecard_config(struct pcmcia_device *link);
+static void bluecard_release(struct pcmcia_device *link);
+
+static void bluecard_detach(struct pcmcia_device *p_dev);
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE 230400
+
+
+/* Hardware states */
+#define CARD_READY 1
+#define CARD_HAS_PCCARD_ID 4
+#define CARD_HAS_POWER_LED 5
+#define CARD_HAS_ACTIVITY_LED 6
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY 6
+#define XMIT_BUF_TWO_READY 7
+#define XMIT_SENDING_READY 8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE 0
+#define RECV_WAIT_EVENT_HEADER 1
+#define RECV_WAIT_ACL_HEADER 2
+#define RECV_WAIT_SCO_HEADER 3
+#define RECV_WAIT_DATA 4
+
+/* Special packet types */
+#define PKT_BAUD_RATE_57600 0x80
+#define PKT_BAUD_RATE_115200 0x81
+#define PKT_BAUD_RATE_230400 0x82
+#define PKT_BAUD_RATE_460800 0x83
+
+
+/* These are the register offsets */
+#define REG_COMMAND 0x20
+#define REG_INTERRUPT 0x21
+#define REG_CONTROL 0x22
+#define REG_RX_CONTROL 0x24
+#define REG_CARD_RESET 0x30
+#define REG_LED_CTRL 0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE 0x01
+#define REG_COMMAND_TX_BUF_TWO 0x02
+#define REG_COMMAND_RX_BUF_ONE 0x04
+#define REG_COMMAND_RX_BUF_TWO 0x08
+#define REG_COMMAND_RX_WIN_ONE 0x00
+#define REG_COMMAND_RX_WIN_TWO 0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600 0x00
+#define REG_CONTROL_BAUD_RATE_115200 0x01
+#define REG_CONTROL_BAUD_RATE_230400 0x02
+#define REG_CONTROL_BAUD_RATE_460800 0x03
+#define REG_CONTROL_RTS 0x04
+#define REG_CONTROL_BT_ON 0x08
+#define REG_CONTROL_BT_RESET 0x10
+#define REG_CONTROL_BT_RES_PU 0x20
+#define REG_CONTROL_INTERRUPT 0x40
+#define REG_CONTROL_CARD_RESET 0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS 0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+static void bluecard_activity_led_timeout(u_long arg)
+{
+ bluecard_info_t *info = (bluecard_info_t *)arg;
+ unsigned int iobase = info->p_dev->io.BasePort1;
+
+ if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+ return;
+
+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+ /* Disable activity LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+ } else {
+ /* Disable power LED */
+ outb(0x00, iobase + 0x30);
+ }
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t *info)
+{
+ unsigned int iobase = info->p_dev->io.BasePort1;
+
+ if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+ return;
+
+ if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+ /* Enable activity LED */
+ outb(0x10 | 0x40, iobase + 0x30);
+
+ /* Stop the LED after HZ/4 */
+ mod_timer(&(info->timer), jiffies + HZ / 4);
+ } else {
+ /* Enable power LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+
+ /* Stop the LED after HZ/2 */
+ mod_timer(&(info->timer), jiffies + HZ / 2);
+ }
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
+{
+ int i, actual;
+
+ actual = (len > 15) ? 15 : len;
+
+ outb_p(actual, iobase + offset);
+
+ for (i = 0; i < actual; i++)
+ outb_p(buf[i], iobase + offset + i + 1);
+
+ return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+ return;
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ do {
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+ register unsigned int offset;
+ register unsigned char command;
+ register unsigned long ready_bit;
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!pcmcia_dev_present(info->p_dev))
+ return;
+
+ if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+ if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
+ break;
+ offset = 0x10;
+ command = REG_COMMAND_TX_BUF_TWO;
+ ready_bit = XMIT_BUF_TWO_READY;
+ } else {
+ if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
+ break;
+ offset = 0x00;
+ command = REG_COMMAND_TX_BUF_ONE;
+ ready_bit = XMIT_BUF_ONE_READY;
+ }
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ if (bt_cb(skb)->pkt_type & 0x80) {
+ /* Disable RTS */
+ info->ctrl_reg |= REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+ }
+
+ /* Activate LED */
+ bluecard_enable_activity_led(info);
+
+ /* Send frame */
+ len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+ /* Tell the FPGA to send the data */
+ outb_p(command, iobase + REG_COMMAND);
+
+ /* Mark the buffer as dirty */
+ clear_bit(ready_bit, &(info->tx_state));
+
+ if (bt_cb(skb)->pkt_type & 0x80) {
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+ DEFINE_WAIT(wait);
+
+ unsigned char baud_reg;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case PKT_BAUD_RATE_460800:
+ baud_reg = REG_CONTROL_BAUD_RATE_460800;
+ break;
+ case PKT_BAUD_RATE_230400:
+ baud_reg = REG_CONTROL_BAUD_RATE_230400;
+ break;
+ case PKT_BAUD_RATE_115200:
+ baud_reg = REG_CONTROL_BAUD_RATE_115200;
+ break;
+ case PKT_BAUD_RATE_57600:
+ /* Fall through... */
+ default:
+ baud_reg = REG_CONTROL_BAUD_RATE_57600;
+ break;
+ }
+
+ /* Wait until the command reaches the baseband */
+ prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/10);
+ finish_wait(&wq, &wait);
+
+ /* Set baud on baseband */
+ info->ctrl_reg &= ~0x03;
+ info->ctrl_reg |= baud_reg;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable RTS */
+ info->ctrl_reg &= ~REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Wait before the next HCI packet can be send */
+ prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ finish_wait(&wq, &wait);
+ }
+
+ if (len == skb->len) {
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, len);
+ skb_queue_head(&(info->txq), skb);
+ }
+
+ info->hdev->stat.byte_tx += len;
+
+ /* Change buffer */
+ change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
+{
+ int i, n, len;
+
+ outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+ len = inb(iobase + offset);
+ n = 0;
+ i = 1;
+
+ while (n < len) {
+
+ if (i == 16) {
+ outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+ i = 0;
+ }
+
+ buf[n] = inb(iobase + offset + i);
+
+ n++;
+ i++;
+
+ }
+
+ return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t *info, unsigned int offset)
+{
+ unsigned int iobase;
+ unsigned char buf[31];
+ int i, len;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ iobase = info->p_dev->io.BasePort1;
+
+ if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+ bluecard_enable_activity_led(info);
+
+ len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+ for (i = 0; i < len; i++) {
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL) {
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return;
+ }
+ }
+
+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type = buf[i];
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+
+ case 0x00:
+ /* init packet */
+ if (offset != 0x00) {
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ set_bit(XMIT_SENDING_READY, &(info->tx_state));
+ bluecard_write_wakeup(info);
+ }
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ case HCI_EVENT_PKT:
+ info->rx_state = RECV_WAIT_EVENT_HEADER;
+ info->rx_count = HCI_EVENT_HDR_SIZE;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ info->rx_state = RECV_WAIT_ACL_HEADER;
+ info->rx_count = HCI_ACL_HDR_SIZE;
+ break;
+
+ case HCI_SCODATA_PKT:
+ info->rx_state = RECV_WAIT_SCO_HEADER;
+ info->rx_count = HCI_SCO_HDR_SIZE;
+ break;
+
+ default:
+ /* unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ info->hdev->stat.err_rx++;
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ *skb_put(info->rx_skb, 1) = buf[i];
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ int dlen;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+
+ switch (info->rx_state) {
+
+ case RECV_WAIT_EVENT_HEADER:
+ eh = hci_event_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = eh->plen;
+ break;
+
+ case RECV_WAIT_ACL_HEADER:
+ ah = hci_acl_hdr(info->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = dlen;
+ break;
+
+ case RECV_WAIT_SCO_HEADER:
+ sh = hci_sco_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = sh->dlen;
+ break;
+
+ case RECV_WAIT_DATA:
+ hci_recv_frame(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ }
+
+ }
+
+
+ }
+
+ info->hdev->stat.byte_rx += len;
+}
+
+
+static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
+{
+ bluecard_info_t *info = dev_inst;
+ unsigned int iobase;
+ unsigned char reg;
+
+ BUG_ON(!info->hdev);
+
+ if (!test_bit(CARD_READY, &(info->hw_state)))
+ return IRQ_HANDLED;
+
+ iobase = info->p_dev->io.BasePort1;
+
+ spin_lock(&(info->lock));
+
+ /* Disable interrupt */
+ info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ reg = inb(iobase + REG_INTERRUPT);
+
+ if ((reg != 0x00) && (reg != 0xff)) {
+
+ if (reg & 0x04) {
+ bluecard_receive(info, 0x00);
+ outb(0x04, iobase + REG_INTERRUPT);
+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+ }
+
+ if (reg & 0x08) {
+ bluecard_receive(info, 0x10);
+ outb(0x08, iobase + REG_INTERRUPT);
+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+ }
+
+ if (reg & 0x01) {
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ outb(0x01, iobase + REG_INTERRUPT);
+ bluecard_write_wakeup(info);
+ }
+
+ if (reg & 0x02) {
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ outb(0x02, iobase + REG_INTERRUPT);
+ bluecard_write_wakeup(info);
+ }
+
+ }
+
+ /* Enable interrupt */
+ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ spin_unlock(&(info->lock));
+
+ return IRQ_HANDLED;
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+ struct sk_buff *skb;
+
+ /* Ericsson baud rate command */
+ unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
+
+ if (!(skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return -1;
+ }
+
+ switch (baud) {
+ case 460800:
+ cmd[4] = 0x00;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_460800;
+ break;
+ case 230400:
+ cmd[4] = 0x01;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_230400;
+ break;
+ case 115200:
+ cmd[4] = 0x02;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_115200;
+ break;
+ case 57600:
+ /* Fall through... */
+ default:
+ cmd[4] = 0x03;
+ bt_cb(skb)->pkt_type = PKT_BAUD_RATE_57600;
+ break;
+ }
+
+ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+
+ skb_queue_tail(&(info->txq), skb);
+
+ bluecard_write_wakeup(info);
+
+ return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+ unsigned int iobase = info->p_dev->io.BasePort1;
+
+ if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
+ bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+ if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+ /* Enable LED */
+ outb(0x08 | 0x20, iobase + 0x30);
+ }
+
+ return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+ bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data);
+ unsigned int iobase = info->p_dev->io.BasePort1;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ bluecard_hci_flush(hdev);
+
+ if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
+ /* Disable LED */
+ outb(0x00, iobase + 0x30);
+ }
+
+ return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+ bluecard_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (bluecard_info_t *)(hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ 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;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&(info->txq), skb);
+
+ bluecard_write_wakeup(info);
+
+ return 0;
+}
+
+
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bluecard_open(bluecard_info_t *info)
+{
+ unsigned int iobase = info->p_dev->io.BasePort1;
+ struct hci_dev *hdev;
+ unsigned char id;
+
+ spin_lock_init(&(info->lock));
+
+ init_timer(&(info->timer));
+ info->timer.function = &bluecard_activity_led_timeout;
+ info->timer.data = (u_long)info;
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->type = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = bluecard_hci_open;
+ hdev->close = bluecard_hci_close;
+ hdev->flush = bluecard_hci_flush;
+ hdev->send = bluecard_hci_send_frame;
+ hdev->destruct = bluecard_hci_destruct;
+ hdev->ioctl = bluecard_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ id = inb(iobase + 0x30);
+
+ if ((id & 0x0f) == 0x02)
+ set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+ if (id & 0x10)
+ set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+ if (id & 0x20)
+ set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+ /* Reset card */
+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Turn FPGA off */
+ outb(0x80, iobase + 0x30);
+
+ /* Wait some time */
+ msleep(10);
+
+ /* Turn FPGA on */
+ outb(0x00, iobase + 0x30);
+
+ /* Activate card */
+ info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable interrupt */
+ outb(0xff, iobase + REG_INTERRUPT);
+ info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ if ((id & 0x0f) == 0x03) {
+ /* Disable RTS */
+ info->ctrl_reg |= REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Set baud rate */
+ info->ctrl_reg |= 0x03;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Enable RTS */
+ info->ctrl_reg &= ~REG_CONTROL_RTS;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+ set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+ set_bit(XMIT_SENDING_READY, &(info->tx_state));
+ }
+
+ /* Start the RX buffers */
+ outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+ outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+ /* Signal that the hardware is ready */
+ set_bit(CARD_READY, &(info->hw_state));
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ /* Control the point at which RTS is enabled */
+ outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(1250);
+
+ /* Register HCI device */
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static int bluecard_close(bluecard_info_t *info)
+{
+ unsigned int iobase = info->p_dev->io.BasePort1;
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ bluecard_hci_close(hdev);
+
+ clear_bit(CARD_READY, &(info->hw_state));
+
+ /* Reset card */
+ info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+ outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+ /* Turn FPGA off */
+ outb(0x80, iobase + 0x30);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int bluecard_probe(struct pcmcia_device *link)
+{
+ bluecard_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1 = 8;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ link->irq.Handler = bluecard_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return bluecard_config(link);
+}
+
+
+static void bluecard_detach(struct pcmcia_device *link)
+{
+ bluecard_info_t *info = link->priv;
+
+ bluecard_release(link);
+ kfree(info);
+}
+
+
+static int bluecard_config(struct pcmcia_device *link)
+{
+ bluecard_info_t *info = link->priv;
+ int i, n;
+
+ link->conf.ConfigIndex = 0x20;
+ link->io.NumPorts1 = 64;
+ link->io.IOAddrLines = 6;
+
+ for (n = 0; n < 0x400; n += 0x40) {
+ link->io.BasePort1 = n ^ 0x300;
+ i = pcmcia_request_io(link, &link->io);
+ if (i == 0)
+ break;
+ }
+
+ if (i != 0) {
+ cs_error(link, RequestIO, i);
+ goto failed;
+ }
+
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ cs_error(link, RequestIRQ, i);
+ link->irq.AssignedIRQ = 0;
+ }
+
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ cs_error(link, RequestConfiguration, i);
+ goto failed;
+ }
+
+ if (bluecard_open(info) != 0)
+ goto failed;
+
+ strcpy(info->node.dev_name, info->hdev->name);
+ link->dev_node = &info->node;
+
+ return 0;
+
+failed:
+ bluecard_release(link);
+ return -ENODEV;
+}
+
+
+static void bluecard_release(struct pcmcia_device *link)
+{
+ bluecard_info_t *info = link->priv;
+
+ bluecard_close(info);
+
+ del_timer(&(info->timer));
+
+ pcmcia_disable_device(link);
+}
+
+static struct pcmcia_device_id bluecard_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
+ PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
+ PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bluecard_ids);
+
+static struct pcmcia_driver bluecard_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "bluecard_cs",
+ },
+ .probe = bluecard_probe,
+ .remove = bluecard_detach,
+ .id_table = bluecard_ids,
+};
+
+static int __init init_bluecard_cs(void)
+{
+ return pcmcia_register_driver(&bluecard_driver);
+}
+
+
+static void __exit exit_bluecard_cs(void)
+{
+ pcmcia_unregister_driver(&bluecard_driver);
+}
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
new file mode 100644
index 0000000..b936d8c
--- /dev/null
+++ b/drivers/bluetooth/bpa10x.c
@@ -0,0 +1,545 @@
+/*
+ *
+ * Digianswer Bluetooth USB driver
+ *
+ * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIBPA10X_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "0.10"
+
+static struct usb_device_id bpa10x_table[] = {
+ /* Tektronix BPA 100/105 (Digianswer) */
+ { USB_DEVICE(0x08fd, 0x0002) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, bpa10x_table);
+
+struct bpa10x_data {
+ struct hci_dev *hdev;
+ struct usb_device *udev;
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor rx_anchor;
+
+ struct sk_buff *rx_skb[2];
+};
+
+#define HCI_VENDOR_HDR_SIZE 5
+
+struct hci_vendor_hdr {
+ __u8 type;
+ __le16 snum;
+ __le16 dlen;
+} __attribute__ ((packed));
+
+static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s queue %d buffer %p count %d", hdev->name,
+ queue, buf, count);
+
+ if (queue < 0 || queue > 1)
+ return -EILSEQ;
+
+ hdev->stat.byte_rx += count;
+
+ while (count) {
+ struct sk_buff *skb = data->rx_skb[queue];
+ struct { __u8 type; int expect; } *scb;
+ int type, len = 0;
+
+ if (!skb) {
+ /* Start of the frame */
+
+ type = *((__u8 *) buf);
+ count--; buf++;
+
+ switch (type) {
+ case HCI_EVENT_PKT:
+ if (count >= HCI_EVENT_HDR_SIZE) {
+ struct hci_event_hdr *h = buf;
+ len = HCI_EVENT_HDR_SIZE + h->plen;
+ } else
+ return -EILSEQ;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (count >= HCI_ACL_HDR_SIZE) {
+ struct hci_acl_hdr *h = buf;
+ len = HCI_ACL_HDR_SIZE +
+ __le16_to_cpu(h->dlen);
+ } else
+ return -EILSEQ;
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (count >= HCI_SCO_HDR_SIZE) {
+ struct hci_sco_hdr *h = buf;
+ len = HCI_SCO_HDR_SIZE + h->dlen;
+ } else
+ return -EILSEQ;
+ break;
+
+ case HCI_VENDOR_PKT:
+ if (count >= HCI_VENDOR_HDR_SIZE) {
+ struct hci_vendor_hdr *h = buf;
+ len = HCI_VENDOR_HDR_SIZE +
+ __le16_to_cpu(h->dlen);
+ } else
+ return -EILSEQ;
+ break;
+ }
+
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("%s no memory for packet", hdev->name);
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *) hdev;
+
+ data->rx_skb[queue] = skb;
+
+ scb = (void *) skb->cb;
+ scb->type = type;
+ scb->expect = len;
+ } else {
+ /* Continuation */
+
+ scb = (void *) skb->cb;
+ len = scb->expect;
+ }
+
+ len = min(len, count);
+
+ memcpy(skb_put(skb, len), buf, len);
+
+ scb->expect -= len;
+
+ if (scb->expect == 0) {
+ /* Complete frame */
+
+ data->rx_skb[queue] = NULL;
+
+ bt_cb(skb)->pkt_type = scb->type;
+ hci_recv_frame(skb);
+ }
+
+ count -= len; buf += len;
+ }
+
+ return 0;
+}
+
+static void bpa10x_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static void bpa10x_rx_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct bpa10x_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ BT_ERR("%s corrupted event packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+
+ usb_anchor_urb(urb, &data->rx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = 16;
+
+ BT_DBG("%s", hdev->name);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(data->udev, 0x81);
+
+ usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+ bpa10x_rx_complete, hdev, 1);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->rx_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = 64;
+
+ BT_DBG("%s", hdev->name);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(data->udev, 0x82);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ buf, size, bpa10x_rx_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->rx_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static int bpa10x_open(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ err = bpa10x_submit_intr_urb(hdev);
+ if (err < 0)
+ goto error;
+
+ err = bpa10x_submit_bulk_urb(hdev);
+ if (err < 0)
+ goto error;
+
+ return 0;
+
+error:
+ usb_kill_anchored_urbs(&data->rx_anchor);
+
+ clear_bit(HCI_RUNNING, &hdev->flags);
+
+ return err;
+}
+
+static int bpa10x_close(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ usb_kill_anchored_urbs(&data->rx_anchor);
+
+ return 0;
+}
+
+static int bpa10x_flush(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+static int bpa10x_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct bpa10x_data *data = hdev->driver_data;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ /* Prepend skb with frame type */
+ *skb_push(skb, 1) = bt_cb(skb)->pkt_type;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ dr->bRequestType = USB_TYPE_VENDOR;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+ usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+ skb->data, skb->len, bpa10x_tx_complete, skb);
+
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, bpa10x_tx_complete, skb);
+
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ pipe = usb_sndbulkpipe(data->udev, 0x02);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, bpa10x_tx_complete, skb);
+
+ hdev->stat.sco_tx++;
+ break;
+
+ default:
+ usb_free_urb(urb);
+ return -EILSEQ;
+ }
+
+ usb_anchor_urb(urb, &data->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed", hdev->name, urb);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void bpa10x_destruct(struct hci_dev *hdev)
+{
+ struct bpa10x_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree_skb(data->rx_skb[0]);
+ kfree_skb(data->rx_skb[1]);
+ kfree(data);
+}
+
+static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct bpa10x_data *data;
+ struct hci_dev *hdev;
+ int err;
+
+ 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 = interface_to_usbdev(intf);
+
+ init_usb_anchor(&data->tx_anchor);
+ init_usb_anchor(&data->rx_anchor);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->type = HCI_USB;
+ hdev->driver_data = data;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = bpa10x_open;
+ hdev->close = bpa10x_close;
+ hdev->flush = bpa10x_flush;
+ hdev->send = bpa10x_send_frame;
+ hdev->destruct = bpa10x_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ usb_set_intfdata(intf, data);
+
+ return 0;
+}
+
+static void bpa10x_disconnect(struct usb_interface *intf)
+{
+ struct bpa10x_data *data = usb_get_intfdata(intf);
+
+ BT_DBG("intf %p", intf);
+
+ if (!data)
+ return;
+
+ usb_set_intfdata(intf, NULL);
+
+ hci_unregister_dev(data->hdev);
+
+ hci_free_dev(data->hdev);
+}
+
+static struct usb_driver bpa10x_driver = {
+ .name = "bpa10x",
+ .probe = bpa10x_probe,
+ .disconnect = bpa10x_disconnect,
+ .id_table = bpa10x_table,
+};
+
+static int __init bpa10x_init(void)
+{
+ BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION);
+
+ return usb_register(&bpa10x_driver);
+}
+
+static void __exit bpa10x_exit(void)
+{
+ usb_deregister(&bpa10x_driver);
+}
+
+module_init(bpa10x_init);
+module_exit(bpa10x_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
new file mode 100644
index 0000000..b3e4d07
--- /dev/null
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -0,0 +1,811 @@
+/*
+ *
+ * Driver for the 3Com Bluetooth PCMCIA card
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ * Jose Orlando Pereira <jop@di.uminho.pt>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("BT3CPCC.bin");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bt3c_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+} bt3c_info_t;
+
+
+static int bt3c_config(struct pcmcia_device *link);
+static void bt3c_release(struct pcmcia_device *link);
+
+static void bt3c_detach(struct pcmcia_device *p_dev);
+
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_WAITING 8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE 0
+#define RECV_WAIT_EVENT_HEADER 1
+#define RECV_WAIT_ACL_HEADER 2
+#define RECV_WAIT_SCO_HEADER 3
+#define RECV_WAIT_DATA 4
+
+
+
+/* ======================== Special I/O functions ======================== */
+
+
+#define DATA_L 0
+#define DATA_H 1
+#define ADDR_L 2
+#define ADDR_H 3
+#define CONTROL 4
+
+
+static inline void bt3c_address(unsigned int iobase, unsigned short addr)
+{
+ outb(addr & 0xff, iobase + ADDR_L);
+ outb((addr >> 8) & 0xff, iobase + ADDR_H);
+}
+
+
+static inline void bt3c_put(unsigned int iobase, unsigned short value)
+{
+ outb(value & 0xff, iobase + DATA_L);
+ outb((value >> 8) & 0xff, iobase + DATA_H);
+}
+
+
+static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
+{
+ bt3c_address(iobase, addr);
+ bt3c_put(iobase, value);
+}
+
+
+static inline unsigned short bt3c_get(unsigned int iobase)
+{
+ unsigned short value = inb(iobase + DATA_L);
+
+ value |= inb(iobase + DATA_H) << 8;
+
+ return value;
+}
+
+
+static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
+{
+ bt3c_address(iobase, addr);
+
+ return bt3c_get(iobase);
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ bt3c_address(iobase, 0x7080);
+
+ /* Fill FIFO with current frame */
+ while (actual < len) {
+ /* Transmit next byte */
+ bt3c_put(iobase, buf[actual]);
+ actual++;
+ }
+
+ bt3c_io_write(iobase, 0x7005, actual);
+
+ return actual;
+}
+
+
+static void bt3c_write_wakeup(bt3c_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
+ return;
+
+ do {
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+ register struct sk_buff *skb;
+ register int len;
+
+ if (!pcmcia_dev_present(info->p_dev))
+ break;
+
+
+ if (!(skb = skb_dequeue(&(info->txq)))) {
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+ break;
+ }
+
+ /* Send frame */
+ len = bt3c_write(iobase, 256, skb->data, skb->len);
+
+ if (len != skb->len) {
+ BT_ERR("Very strange");
+ }
+
+ kfree_skb(skb);
+
+ info->hdev->stat.byte_tx += len;
+
+ } while (0);
+}
+
+
+static void bt3c_receive(bt3c_info_t *info)
+{
+ unsigned int iobase;
+ int size = 0, avail;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ iobase = info->p_dev->io.BasePort1;
+
+ avail = bt3c_read(iobase, 0x7006);
+ //printk("bt3c_cs: receiving %d bytes\n", avail);
+
+ bt3c_address(iobase, 0x7480);
+ while (size < avail) {
+ size++;
+ info->hdev->stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL) {
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return;
+ }
+ }
+
+
+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L);
+ inb(iobase + DATA_H);
+ //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type);
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+
+ case HCI_EVENT_PKT:
+ info->rx_state = RECV_WAIT_EVENT_HEADER;
+ info->rx_count = HCI_EVENT_HDR_SIZE;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ info->rx_state = RECV_WAIT_ACL_HEADER;
+ info->rx_count = HCI_ACL_HDR_SIZE;
+ break;
+
+ case HCI_SCODATA_PKT:
+ info->rx_state = RECV_WAIT_SCO_HEADER;
+ info->rx_count = HCI_SCO_HDR_SIZE;
+ break;
+
+ default:
+ /* Unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ info->hdev->stat.err_rx++;
+ clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ __u8 x = inb(iobase + DATA_L);
+
+ *skb_put(info->rx_skb, 1) = x;
+ inb(iobase + DATA_H);
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ int dlen;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+
+ switch (info->rx_state) {
+
+ case RECV_WAIT_EVENT_HEADER:
+ eh = hci_event_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = eh->plen;
+ break;
+
+ case RECV_WAIT_ACL_HEADER:
+ ah = hci_acl_hdr(info->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = dlen;
+ break;
+
+ case RECV_WAIT_SCO_HEADER:
+ sh = hci_sco_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = sh->dlen;
+ break;
+
+ case RECV_WAIT_DATA:
+ hci_recv_frame(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ bt3c_io_write(iobase, 0x7006, 0x0000);
+}
+
+
+static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
+{
+ bt3c_info_t *info = dev_inst;
+ unsigned int iobase;
+ int iir;
+ irqreturn_t r = IRQ_NONE;
+
+ BUG_ON(!info->hdev);
+
+ iobase = info->p_dev->io.BasePort1;
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + CONTROL);
+ if (iir & 0x80) {
+ int stat = bt3c_read(iobase, 0x7001);
+
+ if ((stat & 0xff) == 0x7f) {
+ BT_ERR("Very strange (stat=0x%04x)", stat);
+ } else if ((stat & 0xff) != 0xff) {
+ if (stat & 0x0020) {
+ int stat = bt3c_read(iobase, 0x7002) & 0x10;
+ BT_INFO("%s: Antenna %s", info->hdev->name,
+ stat ? "out" : "in");
+ }
+ if (stat & 0x0001)
+ bt3c_receive(info);
+ if (stat & 0x0002) {
+ //BT_ERR("Ack (stat=0x%04x)", stat);
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+ bt3c_write_wakeup(info);
+ }
+
+ bt3c_io_write(iobase, 0x7001, 0x0000);
+
+ outb(iir, iobase + CONTROL);
+ }
+ r = IRQ_HANDLED;
+ }
+
+ spin_unlock(&(info->lock));
+
+ return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bt3c_hci_flush(struct hci_dev *hdev)
+{
+ bt3c_info_t *info = (bt3c_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int bt3c_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int bt3c_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ bt3c_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int bt3c_hci_send_frame(struct sk_buff *skb)
+{
+ bt3c_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+ unsigned long flags;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (bt3c_info_t *) (hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ 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;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&(info->txq), skb);
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ bt3c_write_wakeup(info);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ return 0;
+}
+
+
+static void bt3c_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
+ int count)
+{
+ char *ptr = (char *) firmware;
+ char b[9];
+ unsigned int iobase, size, addr, fcs, tmp;
+ int i, err = 0;
+
+ iobase = info->p_dev->io.BasePort1;
+
+ /* Reset */
+ bt3c_io_write(iobase, 0x8040, 0x0404);
+ bt3c_io_write(iobase, 0x8040, 0x0400);
+
+ udelay(1);
+
+ bt3c_io_write(iobase, 0x8040, 0x0404);
+
+ udelay(17);
+
+ /* Load */
+ while (count) {
+ if (ptr[0] != 'S') {
+ BT_ERR("Bad address in firmware");
+ err = -EFAULT;
+ goto error;
+ }
+
+ memset(b, 0, sizeof(b));
+ memcpy(b, ptr + 2, 2);
+ size = simple_strtol(b, NULL, 16);
+
+ memset(b, 0, sizeof(b));
+ memcpy(b, ptr + 4, 8);
+ addr = simple_strtol(b, NULL, 16);
+
+ memset(b, 0, sizeof(b));
+ memcpy(b, ptr + (size * 2) + 2, 2);
+ fcs = simple_strtol(b, NULL, 16);
+
+ memset(b, 0, sizeof(b));
+ for (tmp = 0, i = 0; i < size; i++) {
+ memcpy(b, ptr + (i * 2) + 2, 2);
+ tmp += simple_strtol(b, NULL, 16);
+ }
+
+ if (((tmp + fcs) & 0xff) != 0xff) {
+ BT_ERR("Checksum error in firmware");
+ err = -EILSEQ;
+ goto error;
+ }
+
+ if (ptr[1] == '3') {
+ bt3c_address(iobase, addr);
+
+ memset(b, 0, sizeof(b));
+ for (i = 0; i < (size - 4) / 2; i++) {
+ memcpy(b, ptr + (i * 4) + 12, 4);
+ tmp = simple_strtol(b, NULL, 16);
+ bt3c_put(iobase, tmp);
+ }
+ }
+
+ ptr += (size * 2) + 6;
+ count -= (size * 2) + 6;
+ }
+
+ udelay(17);
+
+ /* Boot */
+ bt3c_address(iobase, 0x3000);
+ outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
+
+error:
+ udelay(17);
+
+ /* Clear */
+ bt3c_io_write(iobase, 0x7006, 0x0000);
+ bt3c_io_write(iobase, 0x7005, 0x0000);
+ bt3c_io_write(iobase, 0x7001, 0x0000);
+
+ return err;
+}
+
+
+static int bt3c_open(bt3c_info_t *info)
+{
+ const struct firmware *firmware;
+ struct hci_dev *hdev;
+ int err;
+
+ spin_lock_init(&(info->lock));
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->type = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = bt3c_hci_open;
+ hdev->close = bt3c_hci_close;
+ hdev->flush = bt3c_hci_flush;
+ hdev->send = bt3c_hci_send_frame;
+ hdev->destruct = bt3c_hci_destruct;
+ hdev->ioctl = bt3c_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ /* Load firmware */
+ err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
+ if (err < 0) {
+ BT_ERR("Firmware request failed");
+ goto error;
+ }
+
+ err = bt3c_load_firmware(info, firmware->data, firmware->size);
+
+ release_firmware(firmware);
+
+ if (err < 0) {
+ BT_ERR("Firmware loading failed");
+ goto error;
+ }
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(1000);
+
+ /* Register HCI device */
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BT_ERR("Can't register HCI device");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return err;
+}
+
+
+static int bt3c_close(bt3c_info_t *info)
+{
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ bt3c_hci_close(hdev);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int bt3c_probe(struct pcmcia_device *link)
+{
+ bt3c_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1 = 8;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ link->irq.Handler = bt3c_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return bt3c_config(link);
+}
+
+
+static void bt3c_detach(struct pcmcia_device *link)
+{
+ bt3c_info_t *info = link->priv;
+
+ bt3c_release(link);
+ kfree(info);
+}
+
+static int bt3c_check_config(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ unsigned long try = (unsigned long) priv_data;
+
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[0].base != 0)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = (try == 0) ? 16 :
+ cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
+
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int bt3c_config(struct pcmcia_device *link)
+{
+ bt3c_info_t *info = link->priv;
+ int i;
+ unsigned long try;
+
+ /* First pass: look for a config entry that looks normal.
+ Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 2; try++)
+ if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
+ goto found_port;
+
+ /* Second pass: try to find an entry that isn't picky about
+ its base address, then try to grab any standard serial port
+ address, and finally try to get any free port. */
+ if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
+ goto found_port;
+
+ BT_ERR("No usable port range found");
+ cs_error(link, RequestIO, -ENODEV);
+ goto failed;
+
+found_port:
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ cs_error(link, RequestIRQ, i);
+ link->irq.AssignedIRQ = 0;
+ }
+
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ cs_error(link, RequestConfiguration, i);
+ goto failed;
+ }
+
+ if (bt3c_open(info) != 0)
+ goto failed;
+
+ strcpy(info->node.dev_name, info->hdev->name);
+ link->dev_node = &info->node;
+
+ return 0;
+
+failed:
+ bt3c_release(link);
+ return -ENODEV;
+}
+
+
+static void bt3c_release(struct pcmcia_device *link)
+{
+ bt3c_info_t *info = link->priv;
+
+ bt3c_close(info);
+
+ pcmcia_disable_device(link);
+}
+
+
+static struct pcmcia_device_id bt3c_ids[] = {
+ PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, bt3c_ids);
+
+static struct pcmcia_driver bt3c_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "bt3c_cs",
+ },
+ .probe = bt3c_probe,
+ .remove = bt3c_detach,
+ .id_table = bt3c_ids,
+};
+
+static int __init init_bt3c_cs(void)
+{
+ return pcmcia_register_driver(&bt3c_driver);
+}
+
+
+static void __exit exit_bt3c_cs(void)
+{
+ pcmcia_unregister_driver(&bt3c_driver);
+}
+
+module_init(init_bt3c_cs);
+module_exit(exit_bt3c_cs);
diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c
new file mode 100644
index 0000000..f2ada0c
--- /dev/null
+++ b/drivers/bluetooth/btsdio.c
@@ -0,0 +1,405 @@
+/*
+ *
+ * Generic Bluetooth SDIO driver
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ * Copyright (C) 2007 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIBTSDIO_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "0.1"
+
+static const struct sdio_device_id btsdio_table[] = {
+ /* Generic Bluetooth Type-A SDIO device */
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) },
+
+ /* Generic Bluetooth Type-B SDIO device */
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btsdio_table);
+
+struct btsdio_data {
+ struct hci_dev *hdev;
+ struct sdio_func *func;
+
+ struct work_struct work;
+
+ struct sk_buff_head txq;
+};
+
+#define REG_RDAT 0x00 /* Receiver Data */
+#define REG_TDAT 0x00 /* Transmitter Data */
+#define REG_PC_RRT 0x10 /* Read Packet Control */
+#define REG_PC_WRT 0x11 /* Write Packet Control */
+#define REG_RTC_STAT 0x12 /* Retry Control Status */
+#define REG_RTC_SET 0x12 /* Retry Control Set */
+#define REG_INTRD 0x13 /* Interrupt Indication */
+#define REG_CL_INTRD 0x13 /* Interrupt Clear */
+#define REG_EN_INTRD 0x14 /* Interrupt Enable */
+#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */
+
+static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
+{
+ int err;
+
+ BT_DBG("%s", data->hdev->name);
+
+ /* Prepend Type-A header */
+ skb_push(skb, 4);
+ skb->data[0] = (skb->len & 0x0000ff);
+ skb->data[1] = (skb->len & 0x00ff00) >> 8;
+ skb->data[2] = (skb->len & 0xff0000) >> 16;
+ skb->data[3] = bt_cb(skb)->pkt_type;
+
+ err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
+ if (err < 0) {
+ skb_pull(skb, 4);
+ sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL);
+ return err;
+ }
+
+ data->hdev->stat.byte_tx += skb->len;
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+static void btsdio_work(struct work_struct *work)
+{
+ struct btsdio_data *data = container_of(work, struct btsdio_data, work);
+ struct sk_buff *skb;
+ int err;
+
+ BT_DBG("%s", data->hdev->name);
+
+ sdio_claim_host(data->func);
+
+ while ((skb = skb_dequeue(&data->txq))) {
+ err = btsdio_tx_packet(data, skb);
+ if (err < 0) {
+ data->hdev->stat.err_tx++;
+ skb_queue_head(&data->txq, skb);
+ break;
+ }
+ }
+
+ sdio_release_host(data->func);
+}
+
+static int btsdio_rx_packet(struct btsdio_data *data)
+{
+ u8 hdr[4] __attribute__ ((aligned(4)));
+ struct sk_buff *skb;
+ int err, len;
+
+ BT_DBG("%s", data->hdev->name);
+
+ err = sdio_readsb(data->func, hdr, REG_RDAT, 4);
+ if (err < 0)
+ return err;
+
+ len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16);
+ if (len < 4 || len > 65543)
+ return -EILSEQ;
+
+ skb = bt_skb_alloc(len - 4, GFP_KERNEL);
+ if (!skb) {
+ /* Out of memory. Prepare a read retry and just
+ * return with the expectation that the next time
+ * we're called we'll have more memory. */
+ return -ENOMEM;
+ }
+
+ skb_put(skb, len - 4);
+
+ err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4);
+ if (err < 0) {
+ kfree_skb(skb);
+ return err;
+ }
+
+ data->hdev->stat.byte_rx += len;
+
+ skb->dev = (void *) data->hdev;
+ bt_cb(skb)->pkt_type = hdr[3];
+
+ err = hci_recv_frame(skb);
+ if (err < 0)
+ return err;
+
+ sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL);
+
+ return 0;
+}
+
+static void btsdio_interrupt(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+ int intrd;
+
+ BT_DBG("%s", data->hdev->name);
+
+ intrd = sdio_readb(func, REG_INTRD, NULL);
+ if (intrd & 0x01) {
+ sdio_writeb(func, 0x01, REG_CL_INTRD, NULL);
+
+ if (btsdio_rx_packet(data) < 0) {
+ data->hdev->stat.err_rx++;
+ sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL);
+ }
+ }
+}
+
+static int btsdio_open(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ sdio_claim_host(data->func);
+
+ err = sdio_enable_func(data->func);
+ if (err < 0) {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ goto release;
+ }
+
+ err = sdio_claim_irq(data->func, btsdio_interrupt);
+ if (err < 0) {
+ sdio_disable_func(data->func);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ goto release;
+ }
+
+ if (data->func->class == SDIO_CLASS_BT_B)
+ sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL);
+
+ sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
+
+release:
+ sdio_release_host(data->func);
+
+ return err;
+}
+
+static int btsdio_close(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ sdio_claim_host(data->func);
+
+ sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
+
+ sdio_release_irq(data->func);
+ sdio_disable_func(data->func);
+
+ sdio_release_host(data->func);
+
+ return 0;
+}
+
+static int btsdio_flush(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ skb_queue_purge(&data->txq);
+
+ return 0;
+}
+
+static int btsdio_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ switch (bt_cb(skb)->pkt_type) {
+ 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;
+
+ default:
+ return -EILSEQ;
+ }
+
+ skb_queue_tail(&data->txq, skb);
+
+ schedule_work(&data->work);
+
+ return 0;
+}
+
+static void btsdio_destruct(struct hci_dev *hdev)
+{
+ struct btsdio_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree(data);
+}
+
+static int btsdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct btsdio_data *data;
+ struct hci_dev *hdev;
+ struct sdio_func_tuple *tuple = func->tuples;
+ int err;
+
+ BT_DBG("func %p id %p class 0x%04x", func, id, func->class);
+
+ while (tuple) {
+ BT_DBG("code 0x%x size %d", tuple->code, tuple->size);
+ tuple = tuple->next;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->func = func;
+
+ INIT_WORK(&data->work, btsdio_work);
+
+ skb_queue_head_init(&data->txq);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->type = HCI_SDIO;
+ hdev->driver_data = data;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &func->dev);
+
+ hdev->open = btsdio_open;
+ hdev->close = btsdio_close;
+ hdev->flush = btsdio_flush;
+ hdev->send = btsdio_send_frame;
+ hdev->destruct = btsdio_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ sdio_set_drvdata(func, data);
+
+ return 0;
+}
+
+static void btsdio_remove(struct sdio_func *func)
+{
+ struct btsdio_data *data = sdio_get_drvdata(func);
+ struct hci_dev *hdev;
+
+ BT_DBG("func %p", func);
+
+ if (!data)
+ return;
+
+ hdev = data->hdev;
+
+ sdio_set_drvdata(func, NULL);
+
+ hci_unregister_dev(hdev);
+
+ hci_free_dev(hdev);
+}
+
+static struct sdio_driver btsdio_driver = {
+ .name = "btsdio",
+ .probe = btsdio_probe,
+ .remove = btsdio_remove,
+ .id_table = btsdio_table,
+};
+
+static int __init btsdio_init(void)
+{
+ BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION);
+
+ return sdio_register_driver(&btsdio_driver);
+}
+
+static void __exit btsdio_exit(void)
+{
+ sdio_unregister_driver(&btsdio_driver);
+}
+
+module_init(btsdio_init);
+module_exit(btsdio_exit);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c
new file mode 100644
index 0000000..efd689a
--- /dev/null
+++ b/drivers/bluetooth/btuart_cs.c
@@ -0,0 +1,739 @@
+/*
+ *
+ * Driver for Bluetooth PCMCIA cards with HCI UART interface
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct btuart_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+} btuart_info_t;
+
+
+static int btuart_config(struct pcmcia_device *link);
+static void btuart_release(struct pcmcia_device *link);
+
+static void btuart_detach(struct pcmcia_device *p_dev);
+
+
+/* Maximum baud rate */
+#define SPEED_MAX 115200
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE 115200
+
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_WAITING 8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE 0
+#define RECV_WAIT_EVENT_HEADER 1
+#define RECV_WAIT_ACL_HEADER 2
+#define RECV_WAIT_SCO_HEADER 3
+#define RECV_WAIT_DATA 4
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty */
+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+ return 0;
+
+ /* Fill FIFO with current frame */
+ while ((fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb(buf[actual], iobase + UART_TX);
+ actual++;
+ }
+
+ return actual;
+}
+
+
+static void btuart_write_wakeup(btuart_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ do {
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!pcmcia_dev_present(info->p_dev))
+ return;
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ /* Send frame */
+ len = btuart_write(iobase, 16, skb->data, skb->len);
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (len == skb->len) {
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, len);
+ skb_queue_head(&(info->txq), skb);
+ }
+
+ info->hdev->stat.byte_tx += len;
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void btuart_receive(btuart_info_t *info)
+{
+ unsigned int iobase;
+ int boguscount = 0;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ iobase = info->p_dev->io.BasePort1;
+
+ do {
+ info->hdev->stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL) {
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ return;
+ }
+ }
+
+ if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX);
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+
+ case HCI_EVENT_PKT:
+ info->rx_state = RECV_WAIT_EVENT_HEADER;
+ info->rx_count = HCI_EVENT_HDR_SIZE;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ info->rx_state = RECV_WAIT_ACL_HEADER;
+ info->rx_count = HCI_ACL_HDR_SIZE;
+ break;
+
+ case HCI_SCODATA_PKT:
+ info->rx_state = RECV_WAIT_SCO_HEADER;
+ info->rx_count = HCI_SCO_HDR_SIZE;
+ break;
+
+ default:
+ /* Unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ info->hdev->stat.err_rx++;
+ clear_bit(HCI_RUNNING, &(info->hdev->flags));
+
+ kfree_skb(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ } else {
+
+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ int dlen;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+
+
+ switch (info->rx_state) {
+
+ case RECV_WAIT_EVENT_HEADER:
+ eh = hci_event_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = eh->plen;
+ break;
+
+ case RECV_WAIT_ACL_HEADER:
+ ah = hci_acl_hdr(info->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = dlen;
+ break;
+
+ case RECV_WAIT_SCO_HEADER:
+ sh = hci_sco_hdr(info->rx_skb);
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = sh->dlen;
+ break;
+
+ case RECV_WAIT_DATA:
+ hci_recv_frame(info->rx_skb);
+ info->rx_skb = NULL;
+ break;
+
+ }
+
+ }
+
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 16)
+ break;
+
+ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
+{
+ btuart_info_t *info = dev_inst;
+ unsigned int iobase;
+ int boguscount = 0;
+ int iir, lsr;
+ irqreturn_t r = IRQ_NONE;
+
+ BUG_ON(!info->hdev);
+
+ iobase = info->p_dev->io.BasePort1;
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+ while (iir) {
+ r = IRQ_HANDLED;
+
+ /* Clear interrupt */
+ lsr = inb(iobase + UART_LSR);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ BT_ERR("RLSI");
+ break;
+ case UART_IIR_RDI:
+ /* Receive interrupt */
+ btuart_receive(info);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE) {
+ /* Transmitter ready for data */
+ btuart_write_wakeup(info);
+ }
+ break;
+ default:
+ BT_ERR("Unhandled IIR=%#x", iir);
+ break;
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 100)
+ break;
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+ }
+
+ spin_unlock(&(info->lock));
+
+ return r;
+}
+
+
+static void btuart_change_speed(btuart_info_t *info, unsigned int speed)
+{
+ unsigned long flags;
+ unsigned int iobase;
+ int fcr; /* FIFO control reg */
+ int lcr; /* Line control reg */
+ int divisor;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ iobase = info->p_dev->io.BasePort1;
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ divisor = SPEED_MAX / speed;
+
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
+
+ /*
+ * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
+ * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
+ * about this timeout since it will always be fast enough.
+ */
+
+ if (speed < 38400)
+ fcr |= UART_FCR_TRIGGER_1;
+ else
+ fcr |= UART_FCR_TRIGGER_14;
+
+ /* Bluetooth cards use 8N1 */
+ lcr = UART_LCR_WLEN8;
+
+ outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
+ outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
+ outb(divisor >> 8, iobase + UART_DLM);
+ outb(lcr, iobase + UART_LCR); /* Set 8N1 */
+ outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
+
+ /* Turn on interrupts */
+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int btuart_hci_flush(struct hci_dev *hdev)
+{
+ btuart_info_t *info = (btuart_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int btuart_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int btuart_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ btuart_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int btuart_hci_send_frame(struct sk_buff *skb)
+{
+ btuart_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (btuart_info_t *)(hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ 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;
+ };
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&(info->txq), skb);
+
+ btuart_write_wakeup(info);
+
+ return 0;
+}
+
+
+static void btuart_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int btuart_open(btuart_info_t *info)
+{
+ unsigned long flags;
+ unsigned int iobase = info->p_dev->io.BasePort1;
+ struct hci_dev *hdev;
+
+ spin_lock_init(&(info->lock));
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_PACKET_TYPE;
+ info->rx_count = 0;
+ info->rx_skb = NULL;
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->type = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = btuart_hci_open;
+ hdev->close = btuart_hci_close;
+ hdev->flush = btuart_hci_flush;
+ hdev->send = btuart_hci_send_frame;
+ hdev->destruct = btuart_hci_destruct;
+ hdev->ioctl = btuart_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ /* Initialize UART */
+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+ /* Turn on interrupts */
+ // outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ btuart_change_speed(info, DEFAULT_BAUD_RATE);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(1000);
+
+ /* Register HCI device */
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static int btuart_close(btuart_info_t *info)
+{
+ unsigned long flags;
+ unsigned int iobase = info->p_dev->io.BasePort1;
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ btuart_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int btuart_probe(struct pcmcia_device *link)
+{
+ btuart_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1 = 8;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ link->irq.Handler = btuart_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return btuart_config(link);
+}
+
+
+static void btuart_detach(struct pcmcia_device *link)
+{
+ btuart_info_t *info = link->priv;
+
+ btuart_release(link);
+ kfree(info);
+}
+
+static int btuart_check_config(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int *try = priv_data;
+
+ if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+ if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+ (cf->io.win[0].base != 0)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.IOAddrLines = (*try == 0) ? 16 :
+ cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+ int j;
+
+ if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+ for (j = 0; j < 5; j++) {
+ p_dev->io.BasePort1 = base[j];
+ p_dev->io.IOAddrLines = base[j] ? 16 : 3;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int btuart_config(struct pcmcia_device *link)
+{
+ btuart_info_t *info = link->priv;
+ int i;
+ int try;
+
+ /* First pass: look for a config entry that looks normal.
+ Two tries: without IO aliases, then with aliases */
+ for (try = 0; try < 2; try++)
+ if (!pcmcia_loop_config(link, btuart_check_config, &try))
+ goto found_port;
+
+ /* Second pass: try to find an entry that isn't picky about
+ its base address, then try to grab any standard serial port
+ address, and finally try to get any free port. */
+ if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
+ goto found_port;
+
+ BT_ERR("No usable port range found");
+ cs_error(link, RequestIO, -ENODEV);
+ goto failed;
+
+found_port:
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ cs_error(link, RequestIRQ, i);
+ link->irq.AssignedIRQ = 0;
+ }
+
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ cs_error(link, RequestConfiguration, i);
+ goto failed;
+ }
+
+ if (btuart_open(info) != 0)
+ goto failed;
+
+ strcpy(info->node.dev_name, info->hdev->name);
+ link->dev_node = &info->node;
+
+ return 0;
+
+failed:
+ btuart_release(link);
+ return -ENODEV;
+}
+
+
+static void btuart_release(struct pcmcia_device *link)
+{
+ btuart_info_t *info = link->priv;
+
+ btuart_close(info);
+
+ pcmcia_disable_device(link);
+}
+
+static struct pcmcia_device_id btuart_ids[] = {
+ /* don't use this driver. Use serial_cs + hci_uart instead */
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, btuart_ids);
+
+static struct pcmcia_driver btuart_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "btuart_cs",
+ },
+ .probe = btuart_probe,
+ .remove = btuart_detach,
+ .id_table = btuart_ids,
+};
+
+static int __init init_btuart_cs(void)
+{
+ return pcmcia_register_driver(&btuart_driver);
+}
+
+
+static void __exit exit_btuart_cs(void)
+{
+ pcmcia_unregister_driver(&btuart_driver);
+}
+
+module_init(init_btuart_cs);
+module_exit(exit_btuart_cs);
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
new file mode 100644
index 0000000..af472e0
--- /dev/null
+++ b/drivers/bluetooth/btusb.c
@@ -0,0 +1,995 @@
+/*
+ *
+ * Generic Bluetooth USB driver
+ *
+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+//#define CONFIG_BT_HCIBTUSB_DEBUG
+#ifndef CONFIG_BT_HCIBTUSB_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "0.3"
+
+static int ignore_dga;
+static int ignore_csr;
+static int ignore_sniffer;
+static int disable_scofix;
+static int force_scofix;
+static int reset;
+
+static struct usb_driver btusb_driver;
+
+#define BTUSB_IGNORE 0x01
+#define BTUSB_RESET 0x02
+#define BTUSB_DIGIANSWER 0x04
+#define BTUSB_CSR 0x08
+#define BTUSB_SNIFFER 0x10
+#define BTUSB_BCM92035 0x20
+#define BTUSB_BROKEN_ISOC 0x40
+#define BTUSB_WRONG_SCO_MTU 0x80
+
+static struct usb_device_id btusb_table[] = {
+ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
+
+ /* AVM BlueFRITZ! USB v2.0 */
+ { USB_DEVICE(0x057c, 0x3800) },
+
+ /* Bluetooth Ultraport Module from IBM */
+ { USB_DEVICE(0x04bf, 0x030a) },
+
+ /* ALPS Modules with non-standard id */
+ { USB_DEVICE(0x044e, 0x3001) },
+ { USB_DEVICE(0x044e, 0x3002) },
+
+ /* Ericsson with non-standard id */
+ { USB_DEVICE(0x0bdb, 0x1002) },
+
+ /* Canyon CN-BTU1 with HID interfaces */
+ { USB_DEVICE(0x0c10, 0x0000), .driver_info = BTUSB_RESET },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
+static struct usb_device_id blacklist_table[] = {
+ /* CSR BlueCore devices */
+ { USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
+
+ /* Broadcom BCM2033 without firmware */
+ { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
+
+ /* Broadcom BCM2035 */
+ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Broadcom BCM2045 */
+ { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2101), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Broadcom BCM2046 */
+ { USB_DEVICE(0x0a5c, 0x2146), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x0a5c, 0x2151), .driver_info = BTUSB_RESET },
+
+ /* Apple MacBook Pro with Broadcom chip */
+ { USB_DEVICE(0x05ac, 0x820f), .driver_info = BTUSB_RESET },
+
+ /* IBM/Lenovo ThinkPad with Broadcom chip */
+ { USB_DEVICE(0x0a5c, 0x201e), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2110), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Targus ACB10US */
+ { USB_DEVICE(0x0a5c, 0x2100), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x0a5c, 0x2154), .driver_info = BTUSB_RESET },
+
+ /* ANYCOM Bluetooth USB-200 and USB-250 */
+ { USB_DEVICE(0x0a5c, 0x2111), .driver_info = BTUSB_RESET },
+
+ /* HP laptop with Broadcom chip */
+ { USB_DEVICE(0x03f0, 0x171d), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Dell laptop with Broadcom chip */
+ { USB_DEVICE(0x413c, 0x8126), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Dell Wireless 370 */
+ { USB_DEVICE(0x413c, 0x8156), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Dell Wireless 410 */
+ { USB_DEVICE(0x413c, 0x8152), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Microsoft Wireless Transceiver for Bluetooth 2.0 */
+ { USB_DEVICE(0x045e, 0x009c), .driver_info = BTUSB_RESET },
+
+ /* Kensington Bluetooth USB adapter */
+ { USB_DEVICE(0x047d, 0x105d), .driver_info = BTUSB_RESET },
+ { USB_DEVICE(0x047d, 0x105e), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* ISSC Bluetooth Adapter v3.1 */
+ { USB_DEVICE(0x1131, 0x1001), .driver_info = BTUSB_RESET },
+
+ /* RTX Telecom based adapters with buggy SCO support */
+ { USB_DEVICE(0x0400, 0x0807), .driver_info = BTUSB_BROKEN_ISOC },
+ { USB_DEVICE(0x0400, 0x080a), .driver_info = BTUSB_BROKEN_ISOC },
+
+ /* CONWISE Technology based adapters with buggy SCO support */
+ { USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
+
+ /* Belkin F8T012 and F8T013 devices */
+ { USB_DEVICE(0x050d, 0x0012), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+ { USB_DEVICE(0x050d, 0x0013), .driver_info = BTUSB_RESET | BTUSB_WRONG_SCO_MTU },
+
+ /* Belkin F8T016 device */
+ { USB_DEVICE(0x050d, 0x016a), .driver_info = BTUSB_RESET },
+
+ /* Digianswer devices */
+ { USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
+ { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE },
+
+ /* CSR BlueCore Bluetooth Sniffer */
+ { USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER },
+
+ /* Frontline ComProbe Bluetooth Sniffer */
+ { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER },
+
+ { } /* Terminating entry */
+};
+
+#define BTUSB_MAX_ISOC_FRAMES 10
+
+#define BTUSB_INTR_RUNNING 0
+#define BTUSB_BULK_RUNNING 1
+#define BTUSB_ISOC_RUNNING 2
+
+struct btusb_data {
+ struct hci_dev *hdev;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct usb_interface *isoc;
+
+ spinlock_t lock;
+
+ unsigned long flags;
+
+ struct work_struct work;
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor intr_anchor;
+ struct usb_anchor bulk_anchor;
+ struct usb_anchor isoc_anchor;
+
+ struct usb_endpoint_descriptor *intr_ep;
+ struct usb_endpoint_descriptor *bulk_tx_ep;
+ struct usb_endpoint_descriptor *bulk_rx_ep;
+ struct usb_endpoint_descriptor *isoc_tx_ep;
+ struct usb_endpoint_descriptor *isoc_rx_ep;
+
+ int isoc_altsetting;
+};
+
+static void btusb_intr_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (hci_recv_fragment(hdev, HCI_EVENT_PKT,
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ BT_ERR("%s corrupted event packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+
+ if (!test_bit(BTUSB_INTR_RUNNING, &data->flags))
+ return;
+
+ usb_anchor_urb(urb, &data->intr_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_intr_urb(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!data->intr_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
+
+ buf = kmalloc(size, GFP_ATOMIC);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+ btusb_intr_complete, hdev,
+ data->intr_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->intr_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_bulk_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT,
+ urb->transfer_buffer,
+ urb->actual_length) < 0) {
+ BT_ERR("%s corrupted ACL packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+
+ if (!test_bit(BTUSB_BULK_RUNNING, &data->flags))
+ return;
+
+ usb_anchor_urb(urb, &data->bulk_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_bulk_urb(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!data->bulk_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize);
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ buf, size, btusb_bulk_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &data->bulk_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_isoc_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btusb_data *data = hdev->driver_data;
+ int i, err;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+ unsigned int length = urb->iso_frame_desc[i].actual_length;
+
+ if (urb->iso_frame_desc[i].status)
+ continue;
+
+ hdev->stat.byte_rx += length;
+
+ if (hci_recv_fragment(hdev, HCI_SCODATA_PKT,
+ urb->transfer_buffer + offset,
+ length) < 0) {
+ BT_ERR("%s corrupted SCO packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+ }
+
+ if (!test_bit(BTUSB_ISOC_RUNNING, &data->flags))
+ return;
+
+ usb_anchor_urb(urb, &data->isoc_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static void inline __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+{
+ int i, offset = 0;
+
+ BT_DBG("len %d mtu %d", len, mtu);
+
+ for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
+ i++, offset += mtu, len -= mtu) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = mtu;
+ }
+
+ if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = len;
+ i++;
+ }
+
+ urb->number_of_packets = i;
+}
+
+static int btusb_submit_isoc_urb(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!data->isoc_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize) *
+ BTUSB_MAX_ISOC_FRAMES;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
+
+ urb->dev = data->udev;
+ urb->pipe = pipe;
+ urb->context = hdev;
+ urb->complete = btusb_isoc_complete;
+ urb->interval = data->isoc_rx_ep->bInterval;
+
+ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = size;
+
+ __fill_isoc_descriptor(urb, size,
+ le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
+
+ usb_anchor_urb(urb, &data->isoc_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+
+ BT_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static int btusb_open(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
+ return 0;
+
+ err = btusb_submit_intr_urb(hdev);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ }
+
+ return err;
+}
+
+static int btusb_close(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ cancel_work_sync(&data->work);
+
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->bulk_anchor);
+
+ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->intr_anchor);
+
+ return 0;
+}
+
+static int btusb_flush(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ usb_kill_anchored_urbs(&data->tx_anchor);
+
+ return 0;
+}
+
+static int btusb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct btusb_data *data = hdev->driver_data;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ dr->bRequestType = USB_TYPE_CLASS;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(data->udev, 0x00);
+
+ usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ if (!data->bulk_tx_ep || hdev->conn_hash.acl_num < 1)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ pipe = usb_sndbulkpipe(data->udev,
+ data->bulk_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, data->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ pipe = usb_sndisocpipe(data->udev,
+ data->isoc_tx_ep->bEndpointAddress);
+
+ urb->dev = data->udev;
+ urb->pipe = pipe;
+ urb->context = skb;
+ urb->complete = btusb_tx_complete;
+ urb->interval = data->isoc_tx_ep->bInterval;
+
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = skb->data;
+ urb->transfer_buffer_length = skb->len;
+
+ __fill_isoc_descriptor(urb, skb->len,
+ le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
+
+ hdev->stat.sco_tx++;
+ break;
+
+ default:
+ return -EILSEQ;
+ }
+
+ usb_anchor_urb(urb, &data->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ BT_ERR("%s urb %p submission failed", hdev->name, urb);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_destruct(struct hci_dev *hdev)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree(data);
+}
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+ struct btusb_data *data = hdev->driver_data;
+
+ BT_DBG("%s evt %d", hdev->name, evt);
+
+ if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL)
+ schedule_work(&data->work);
+}
+
+static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+{
+ struct btusb_data *data = hdev->driver_data;
+ struct usb_interface *intf = data->isoc;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i, err;
+
+ if (!data->isoc)
+ return -ENODEV;
+
+ err = usb_set_interface(data->udev, 1, altsetting);
+ if (err < 0) {
+ BT_ERR("%s setting interface failed (%d)", hdev->name, -err);
+ return err;
+ }
+
+ data->isoc_altsetting = altsetting;
+
+ data->isoc_tx_ep = NULL;
+ data->isoc_rx_ep = NULL;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!data->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
+ data->isoc_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
+ data->isoc_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!data->isoc_tx_ep || !data->isoc_rx_ep) {
+ BT_ERR("%s invalid SCO descriptors", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void btusb_work(struct work_struct *work)
+{
+ struct btusb_data *data = container_of(work, struct btusb_data, work);
+ struct hci_dev *hdev = data->hdev;
+
+ if (hdev->conn_hash.acl_num > 0) {
+ if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+ if (btusb_submit_bulk_urb(hdev) < 0)
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ else
+ btusb_submit_bulk_urb(hdev);
+ }
+ } else {
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->bulk_anchor);
+ }
+
+ if (hdev->conn_hash.sco_num > 0) {
+ if (data->isoc_altsetting != 2) {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ if (__set_isoc_interface(hdev, 2) < 0)
+ return;
+ }
+
+ if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
+ if (btusb_submit_isoc_urb(hdev) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ else
+ btusb_submit_isoc_urb(hdev);
+ }
+ } else {
+ clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
+ usb_kill_anchored_urbs(&data->isoc_anchor);
+
+ __set_isoc_interface(hdev, 0);
+ }
+}
+
+static int btusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct btusb_data *data;
+ struct hci_dev *hdev;
+ int i, err;
+
+ BT_DBG("intf %p id %p", intf, id);
+
+ /* interface numbers are hardcoded in the spec */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
+
+ if (!id->driver_info) {
+ const struct usb_device_id *match;
+ match = usb_match_id(intf, blacklist_table);
+ if (match)
+ id = match;
+ }
+
+ if (id->driver_info == BTUSB_IGNORE)
+ return -ENODEV;
+
+ if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER)
+ return -ENODEV;
+
+ if (ignore_csr && id->driver_info & BTUSB_CSR)
+ return -ENODEV;
+
+ if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
+ return -ENODEV;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
+ data->intr_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+ data->bulk_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+ data->bulk_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) {
+ kfree(data);
+ return -ENODEV;
+ }
+
+ data->udev = interface_to_usbdev(intf);
+ data->intf = intf;
+
+ spin_lock_init(&data->lock);
+
+ INIT_WORK(&data->work, btusb_work);
+
+ init_usb_anchor(&data->tx_anchor);
+ init_usb_anchor(&data->intr_anchor);
+ init_usb_anchor(&data->bulk_anchor);
+ init_usb_anchor(&data->isoc_anchor);
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ hdev->type = HCI_USB;
+ hdev->driver_data = data;
+
+ data->hdev = hdev;
+
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = btusb_open;
+ hdev->close = btusb_close;
+ hdev->flush = btusb_flush;
+ hdev->send = btusb_send_frame;
+ hdev->destruct = btusb_destruct;
+ hdev->notify = btusb_notify;
+
+ hdev->owner = THIS_MODULE;
+
+ /* interface numbers are hardcoded in the spec */
+ data->isoc = usb_ifnum_to_if(data->udev, 1);
+
+ if (reset || id->driver_info & BTUSB_RESET)
+ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+
+ if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) {
+ if (!disable_scofix)
+ set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
+ }
+
+ if (id->driver_info & BTUSB_BROKEN_ISOC)
+ data->isoc = NULL;
+
+ if (id->driver_info & BTUSB_SNIFFER) {
+ struct usb_device *udev = data->udev;
+
+ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
+ set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+
+ data->isoc = NULL;
+ }
+
+ if (id->driver_info & BTUSB_BCM92035) {
+ unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
+ if (skb) {
+ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+ skb_queue_tail(&hdev->driver_init, skb);
+ }
+ }
+
+ if (data->isoc) {
+ err = usb_driver_claim_interface(&btusb_driver,
+ data->isoc, data);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+ }
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ hci_free_dev(hdev);
+ kfree(data);
+ return err;
+ }
+
+ usb_set_intfdata(intf, data);
+
+ return 0;
+}
+
+static void btusb_disconnect(struct usb_interface *intf)
+{
+ struct btusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev;
+
+ BT_DBG("intf %p", intf);
+
+ if (!data)
+ return;
+
+ hdev = data->hdev;
+
+ __hci_dev_hold(hdev);
+
+ usb_set_intfdata(data->intf, NULL);
+
+ if (data->isoc)
+ usb_set_intfdata(data->isoc, NULL);
+
+ hci_unregister_dev(hdev);
+
+ if (intf == data->isoc)
+ usb_driver_release_interface(&btusb_driver, data->intf);
+ else if (data->isoc)
+ usb_driver_release_interface(&btusb_driver, data->isoc);
+
+ __hci_dev_put(hdev);
+
+ hci_free_dev(hdev);
+}
+
+static struct usb_driver btusb_driver = {
+ .name = "btusb",
+ .probe = btusb_probe,
+ .disconnect = btusb_disconnect,
+ .id_table = btusb_table,
+};
+
+static int __init btusb_init(void)
+{
+ BT_INFO("Generic Bluetooth USB driver ver %s", VERSION);
+
+ return usb_register(&btusb_driver);
+}
+
+static void __exit btusb_exit(void)
+{
+ usb_deregister(&btusb_driver);
+}
+
+module_init(btusb_init);
+module_exit(btusb_exit);
+
+module_param(ignore_dga, bool, 0644);
+MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
+
+module_param(ignore_csr, bool, 0644);
+MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
+
+module_param(ignore_sniffer, bool, 0644);
+MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
+
+module_param(disable_scofix, bool, 0644);
+MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
+
+module_param(force_scofix, bool, 0644);
+MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
+
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
new file mode 100644
index 0000000..901bdd9
--- /dev/null
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -0,0 +1,689 @@
+/*
+ *
+ * A driver for Nokia Connectivity Card DTL-1 devices
+ *
+ * Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The initial developer of the original code is David A. Hinds
+ * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/moduleparam.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/bitops.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct dtl1_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+
+ struct hci_dev *hdev;
+
+ spinlock_t lock; /* For serializing operations */
+
+ unsigned long flowmask; /* HCI flow mask */
+ int ri_latch;
+
+ struct sk_buff_head txq;
+ unsigned long tx_state;
+
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+} dtl1_info_t;
+
+
+static int dtl1_config(struct pcmcia_device *link);
+static void dtl1_release(struct pcmcia_device *link);
+
+static void dtl1_detach(struct pcmcia_device *p_dev);
+
+
+/* Transmit states */
+#define XMIT_SENDING 1
+#define XMIT_WAKEUP 2
+#define XMIT_WAITING 8
+
+/* Receiver States */
+#define RECV_WAIT_NSH 0
+#define RECV_WAIT_DATA 1
+
+
+typedef struct {
+ u8 type;
+ u8 zero;
+ u16 len;
+} __attribute__ ((packed)) nsh_t; /* Nokia Specific Header */
+
+#define NSHL 4 /* Nokia Specific Header Length */
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty */
+ if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+ return 0;
+
+ /* Fill FIFO with current frame */
+ while ((fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb(buf[actual], iobase + UART_TX);
+ actual++;
+ }
+
+ return actual;
+}
+
+
+static void dtl1_write_wakeup(dtl1_info_t *info)
+{
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ if (test_bit(XMIT_WAITING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+ set_bit(XMIT_WAKEUP, &(info->tx_state));
+ return;
+ }
+
+ do {
+ register unsigned int iobase = info->p_dev->io.BasePort1;
+ register struct sk_buff *skb;
+ register int len;
+
+ clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+ if (!pcmcia_dev_present(info->p_dev))
+ return;
+
+ if (!(skb = skb_dequeue(&(info->txq))))
+ break;
+
+ /* Send frame */
+ len = dtl1_write(iobase, 32, skb->data, skb->len);
+
+ if (len == skb->len) {
+ set_bit(XMIT_WAITING, &(info->tx_state));
+ kfree_skb(skb);
+ } else {
+ skb_pull(skb, len);
+ skb_queue_head(&(info->txq), skb);
+ }
+
+ info->hdev->stat.byte_tx += len;
+
+ } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+ clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb)
+{
+ u8 flowmask = *(u8 *)skb->data;
+ int i;
+
+ printk(KERN_INFO "Bluetooth: Nokia control data =");
+ for (i = 0; i < skb->len; i++) {
+ printk(" %02x", skb->data[i]);
+ }
+ printk("\n");
+
+ /* transition to active state */
+ if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
+ clear_bit(XMIT_WAITING, &(info->tx_state));
+ dtl1_write_wakeup(info);
+ }
+
+ info->flowmask = flowmask;
+
+ kfree_skb(skb);
+}
+
+
+static void dtl1_receive(dtl1_info_t *info)
+{
+ unsigned int iobase;
+ nsh_t *nsh;
+ int boguscount = 0;
+
+ if (!info) {
+ BT_ERR("Unknown device");
+ return;
+ }
+
+ iobase = info->p_dev->io.BasePort1;
+
+ do {
+ info->hdev->stat.byte_rx++;
+
+ /* Allocate packet */
+ if (info->rx_skb == NULL)
+ if (!(info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+ BT_ERR("Can't allocate mem for new packet");
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ return;
+ }
+
+ *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+ nsh = (nsh_t *)info->rx_skb->data;
+
+ info->rx_count--;
+
+ if (info->rx_count == 0) {
+
+ switch (info->rx_state) {
+ case RECV_WAIT_NSH:
+ info->rx_state = RECV_WAIT_DATA;
+ info->rx_count = nsh->len + (nsh->len & 0x0001);
+ break;
+ case RECV_WAIT_DATA:
+ bt_cb(info->rx_skb)->pkt_type = nsh->type;
+
+ /* remove PAD byte if it exists */
+ if (nsh->len & 0x0001) {
+ info->rx_skb->tail--;
+ info->rx_skb->len--;
+ }
+
+ /* remove NSH */
+ skb_pull(info->rx_skb, NSHL);
+
+ switch (bt_cb(info->rx_skb)->pkt_type) {
+ case 0x80:
+ /* control data for the Nokia Card */
+ dtl1_control(info, info->rx_skb);
+ break;
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ /* send frame to the HCI layer */
+ info->rx_skb->dev = (void *) info->hdev;
+ bt_cb(info->rx_skb)->pkt_type &= 0x0f;
+ hci_recv_frame(info->rx_skb);
+ break;
+ default:
+ /* unknown packet */
+ BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
+ kfree_skb(info->rx_skb);
+ break;
+ }
+
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ info->rx_skb = NULL;
+ break;
+ }
+
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 32)
+ break;
+
+ } while (inb(iobase + UART_LSR) & UART_LSR_DR);
+}
+
+
+static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
+{
+ dtl1_info_t *info = dev_inst;
+ unsigned int iobase;
+ unsigned char msr;
+ int boguscount = 0;
+ int iir, lsr;
+ irqreturn_t r = IRQ_NONE;
+
+ BUG_ON(!info->hdev);
+
+ iobase = info->p_dev->io.BasePort1;
+
+ spin_lock(&(info->lock));
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+ while (iir) {
+
+ r = IRQ_HANDLED;
+ /* Clear interrupt */
+ lsr = inb(iobase + UART_LSR);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ BT_ERR("RLSI");
+ break;
+ case UART_IIR_RDI:
+ /* Receive interrupt */
+ dtl1_receive(info);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE) {
+ /* Transmitter ready for data */
+ dtl1_write_wakeup(info);
+ }
+ break;
+ default:
+ BT_ERR("Unhandled IIR=%#x", iir);
+ break;
+ }
+
+ /* Make sure we don't stay here too long */
+ if (boguscount++ > 100)
+ break;
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+ }
+
+ msr = inb(iobase + UART_MSR);
+
+ if (info->ri_latch ^ (msr & UART_MSR_RI)) {
+ info->ri_latch = msr & UART_MSR_RI;
+ clear_bit(XMIT_WAITING, &(info->tx_state));
+ dtl1_write_wakeup(info);
+ r = IRQ_HANDLED;
+ }
+
+ spin_unlock(&(info->lock));
+
+ return r;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int dtl1_hci_open(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &(hdev->flags));
+
+ return 0;
+}
+
+
+static int dtl1_hci_flush(struct hci_dev *hdev)
+{
+ dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
+
+ /* Drop TX queue */
+ skb_queue_purge(&(info->txq));
+
+ return 0;
+}
+
+
+static int dtl1_hci_close(struct hci_dev *hdev)
+{
+ if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+ return 0;
+
+ dtl1_hci_flush(hdev);
+
+ return 0;
+}
+
+
+static int dtl1_hci_send_frame(struct sk_buff *skb)
+{
+ dtl1_info_t *info;
+ struct hci_dev *hdev = (struct hci_dev *)(skb->dev);
+ struct sk_buff *s;
+ nsh_t nsh;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ info = (dtl1_info_t *)(hdev->driver_data);
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ nsh.type = 0x81;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ nsh.type = 0x82;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ nsh.type = 0x83;
+ break;
+ };
+
+ nsh.zero = 0;
+ nsh.len = skb->len;
+
+ s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+ if (!s)
+ return -ENOMEM;
+
+ skb_reserve(s, NSHL);
+ skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
+ if (skb->len & 0x0001)
+ *skb_put(s, 1) = 0; /* PAD */
+
+ /* Prepend skb with Nokia frame header and queue */
+ memcpy(skb_push(s, NSHL), &nsh, NSHL);
+ skb_queue_tail(&(info->txq), s);
+
+ dtl1_write_wakeup(info);
+
+ kfree_skb(skb);
+
+ return 0;
+}
+
+
+static void dtl1_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+static int dtl1_open(dtl1_info_t *info)
+{
+ unsigned long flags;
+ unsigned int iobase = info->p_dev->io.BasePort1;
+ struct hci_dev *hdev;
+
+ spin_lock_init(&(info->lock));
+
+ skb_queue_head_init(&(info->txq));
+
+ info->rx_state = RECV_WAIT_NSH;
+ info->rx_count = NSHL;
+ info->rx_skb = NULL;
+
+ set_bit(XMIT_WAITING, &(info->tx_state));
+
+ /* Initialize HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ info->hdev = hdev;
+
+ hdev->type = HCI_PCCARD;
+ hdev->driver_data = info;
+ SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
+
+ hdev->open = dtl1_hci_open;
+ hdev->close = dtl1_hci_close;
+ hdev->flush = dtl1_hci_flush;
+ hdev->send = dtl1_hci_send_frame;
+ hdev->destruct = dtl1_hci_destruct;
+ hdev->ioctl = dtl1_hci_ioctl;
+
+ hdev->owner = THIS_MODULE;
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ /* Initialize UART */
+ outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+ info->ri_latch = inb(info->p_dev->io.BasePort1 + UART_MSR) & UART_MSR_RI;
+
+ /* Turn on interrupts */
+ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ /* Timeout before it is safe to send the first HCI packet */
+ msleep(2000);
+
+ /* Register HCI device */
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ info->hdev = NULL;
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static int dtl1_close(dtl1_info_t *info)
+{
+ unsigned long flags;
+ unsigned int iobase = info->p_dev->io.BasePort1;
+ struct hci_dev *hdev = info->hdev;
+
+ if (!hdev)
+ return -ENODEV;
+
+ dtl1_hci_close(hdev);
+
+ spin_lock_irqsave(&(info->lock), flags);
+
+ /* Reset UART */
+ outb(0, iobase + UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase + UART_IER);
+
+ spin_unlock_irqrestore(&(info->lock), flags);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+ hci_free_dev(hdev);
+
+ return 0;
+}
+
+static int dtl1_probe(struct pcmcia_device *link)
+{
+ dtl1_info_t *info;
+
+ /* Create new info device */
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->p_dev = link;
+ link->priv = info;
+
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.NumPorts1 = 8;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ link->irq.Handler = dtl1_interrupt;
+ link->irq.Instance = info;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return dtl1_config(link);
+}
+
+
+static void dtl1_detach(struct pcmcia_device *link)
+{
+ dtl1_info_t *info = link->priv;
+
+ dtl1_release(link);
+
+ kfree(info);
+}
+
+static int dtl1_confcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if ((cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len; /*yo */
+ p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int dtl1_config(struct pcmcia_device *link)
+{
+ dtl1_info_t *info = link->priv;
+ int i;
+
+ /* Look for a generic full-sized window */
+ link->io.NumPorts1 = 8;
+ if (!pcmcia_loop_config(link, dtl1_confcheck, NULL))
+ goto failed;
+
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ cs_error(link, RequestIRQ, i);
+ link->irq.AssignedIRQ = 0;
+ }
+
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ cs_error(link, RequestConfiguration, i);
+ goto failed;
+ }
+
+ if (dtl1_open(info) != 0)
+ goto failed;
+
+ strcpy(info->node.dev_name, info->hdev->name);
+ link->dev_node = &info->node;
+
+ return 0;
+
+failed:
+ dtl1_release(link);
+ return -ENODEV;
+}
+
+
+static void dtl1_release(struct pcmcia_device *link)
+{
+ dtl1_info_t *info = link->priv;
+
+ dtl1_close(info);
+
+ pcmcia_disable_device(link);
+}
+
+
+static struct pcmcia_device_id dtl1_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
+ PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
+ PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
+ PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, dtl1_ids);
+
+static struct pcmcia_driver dtl1_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "dtl1_cs",
+ },
+ .probe = dtl1_probe,
+ .remove = dtl1_detach,
+ .id_table = dtl1_ids,
+};
+
+static int __init init_dtl1_cs(void)
+{
+ return pcmcia_register_driver(&dtl1_driver);
+}
+
+
+static void __exit exit_dtl1_cs(void)
+{
+ pcmcia_unregister_driver(&dtl1_driver);
+}
+
+module_init(init_dtl1_cs);
+module_exit(exit_dtl1_cs);
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
new file mode 100644
index 0000000..7938062
--- /dev/null
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -0,0 +1,767 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2002-2003 Fabrizio Gennari <fabrizio.gennari@philips.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+#include <linux/bitrev.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#ifndef CONFIG_BT_HCIUART_DEBUG
+#undef BT_DBG
+#define BT_DBG( A... )
+#endif
+
+#define VERSION "0.3"
+
+static int txcrc = 1;
+static int hciextn = 1;
+
+#define BCSP_TXWINSIZE 4
+
+#define BCSP_ACK_PKT 0x05
+#define BCSP_LE_PKT 0x06
+
+struct bcsp_struct {
+ struct sk_buff_head unack; /* Unack'ed packets queue */
+ struct sk_buff_head rel; /* Reliable packets queue */
+ struct sk_buff_head unrel; /* Unreliable packets queue */
+
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ u8 rxseq_txack; /* rxseq == txack. */
+ u8 rxack; /* Last packet sent by us that the peer ack'ed */
+ struct timer_list tbcsp;
+
+ enum {
+ BCSP_W4_PKT_DELIMITER,
+ BCSP_W4_PKT_START,
+ BCSP_W4_BCSP_HDR,
+ BCSP_W4_DATA,
+ BCSP_W4_CRC
+ } rx_state;
+
+ enum {
+ BCSP_ESCSTATE_NOESC,
+ BCSP_ESCSTATE_ESC
+ } rx_esc_state;
+
+ u8 use_crc;
+ u16 message_crc;
+ u8 txack_req; /* Do we need to send ack's to the peer? */
+
+ /* Reliable packet sequence number - used to assign seq to each rel pkt. */
+ u8 msgq_txseq;
+};
+
+/* ---- BCSP CRC calculation ---- */
+
+/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
+initial value 0xffff, bits shifted in reverse order. */
+
+static const u16 crc_table[] = {
+ 0x0000, 0x1081, 0x2102, 0x3183,
+ 0x4204, 0x5285, 0x6306, 0x7387,
+ 0x8408, 0x9489, 0xa50a, 0xb58b,
+ 0xc60c, 0xd68d, 0xe70e, 0xf78f
+};
+
+/* Initialise the crc calculator */
+#define BCSP_CRC_INIT(x) x = 0xffff
+
+/*
+ Update crc with next data byte
+
+ Implementation note
+ The data byte is treated as two nibbles. The crc is generated
+ in reverse, i.e., bits are fed into the register from the top.
+*/
+static void bcsp_crc_update(u16 *crc, u8 d)
+{
+ u16 reg = *crc;
+
+ reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
+ reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
+
+ *crc = reg;
+}
+
+/* ---- BCSP core ---- */
+
+static void bcsp_slip_msgdelim(struct sk_buff *skb)
+{
+ const char pkt_delim = 0xc0;
+
+ memcpy(skb_put(skb, 1), &pkt_delim, 1);
+}
+
+static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
+{
+ const char esc_c0[2] = { 0xdb, 0xdc };
+ const char esc_db[2] = { 0xdb, 0xdd };
+
+ switch (c) {
+ case 0xc0:
+ memcpy(skb_put(skb, 2), &esc_c0, 2);
+ break;
+ case 0xdb:
+ memcpy(skb_put(skb, 2), &esc_db, 2);
+ break;
+ default:
+ memcpy(skb_put(skb, 1), &c, 1);
+ }
+}
+
+static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+
+ if (skb->len > 0xFFF) {
+ BT_ERR("Packet too long");
+ kfree_skb(skb);
+ return 0;
+ }
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_ACLDATA_PKT:
+ case HCI_COMMAND_PKT:
+ skb_queue_tail(&bcsp->rel, skb);
+ break;
+
+ case HCI_SCODATA_PKT:
+ skb_queue_tail(&bcsp->unrel, skb);
+ break;
+
+ default:
+ BT_ERR("Unknown packet type");
+ kfree_skb(skb);
+ break;
+ }
+
+ return 0;
+}
+
+static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
+ int len, int pkt_type)
+{
+ struct sk_buff *nskb;
+ u8 hdr[4], chan;
+ u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
+ int rel, i;
+
+ switch (pkt_type) {
+ case HCI_ACLDATA_PKT:
+ chan = 6; /* BCSP ACL channel */
+ rel = 1; /* reliable channel */
+ break;
+ case HCI_COMMAND_PKT:
+ chan = 5; /* BCSP cmd/evt channel */
+ rel = 1; /* reliable channel */
+ break;
+ case HCI_SCODATA_PKT:
+ chan = 7; /* BCSP SCO channel */
+ rel = 0; /* unreliable channel */
+ break;
+ case BCSP_LE_PKT:
+ chan = 1; /* BCSP LE channel */
+ rel = 0; /* unreliable channel */
+ break;
+ case BCSP_ACK_PKT:
+ chan = 0; /* BCSP internal channel */
+ rel = 0; /* unreliable channel */
+ break;
+ default:
+ BT_ERR("Unknown packet type");
+ return NULL;
+ }
+
+ if (hciextn && chan == 5) {
+ __le16 opcode = ((struct hci_command_hdr *)data)->opcode;
+
+ /* Vendor specific commands */
+ if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) {
+ u8 desc = *(data + HCI_COMMAND_HDR_SIZE);
+ if ((desc & 0xf0) == 0xc0) {
+ data += HCI_COMMAND_HDR_SIZE + 1;
+ len -= HCI_COMMAND_HDR_SIZE + 1;
+ chan = desc & 0x0f;
+ }
+ }
+ }
+
+ /* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
+ (because bytes 0xc0 and 0xdb are escaped, worst case is
+ when the packet is all made of 0xc0 and 0xdb :) )
+ + 2 (0xc0 delimiters at start and end). */
+
+ nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
+ if (!nskb)
+ return NULL;
+
+ bt_cb(nskb)->pkt_type = pkt_type;
+
+ bcsp_slip_msgdelim(nskb);
+
+ hdr[0] = bcsp->rxseq_txack << 3;
+ bcsp->txack_req = 0;
+ BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
+
+ if (rel) {
+ hdr[0] |= 0x80 + bcsp->msgq_txseq;
+ BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
+ bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07;
+ }
+
+ if (bcsp->use_crc)
+ hdr[0] |= 0x40;
+
+ hdr[1] = ((len << 4) & 0xff) | chan;
+ hdr[2] = len >> 4;
+ hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
+
+ /* Put BCSP header */
+ for (i = 0; i < 4; i++) {
+ bcsp_slip_one_byte(nskb, hdr[i]);
+
+ if (bcsp->use_crc)
+ bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
+ }
+
+ /* Put payload */
+ for (i = 0; i < len; i++) {
+ bcsp_slip_one_byte(nskb, data[i]);
+
+ if (bcsp->use_crc)
+ bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
+ }
+
+ /* Put CRC */
+ if (bcsp->use_crc) {
+ bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc);
+ bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
+ bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
+ }
+
+ bcsp_slip_msgdelim(nskb);
+ return nskb;
+}
+
+/* This is a rewrite of pkt_avail in ABCSP */
+static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ /* First of all, check for unreliable messages in the queue,
+ since they have priority */
+
+ if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+ if (nskb) {
+ kfree_skb(skb);
+ return nskb;
+ } else {
+ skb_queue_head(&bcsp->unrel, skb);
+ BT_ERR("Could not dequeue pkt because alloc_skb failed");
+ }
+ }
+
+ /* Now, try to send a reliable pkt. We can only send a
+ reliable packet if the number of packets sent but not yet ack'ed
+ is < than the winsize */
+
+ spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+
+ if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, bt_cb(skb)->pkt_type);
+ if (nskb) {
+ __skb_queue_tail(&bcsp->unack, skb);
+ mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+ return nskb;
+ } else {
+ skb_queue_head(&bcsp->rel, skb);
+ BT_ERR("Could not dequeue pkt because alloc_skb failed");
+ }
+ }
+
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+ /* We could not send a reliable packet, either because there are
+ none or because there are too many unack'ed pkts. Did we receive
+ any packets we have not acknowledged yet ? */
+
+ if (bcsp->txack_req) {
+ /* if so, craft an empty ACK pkt and send it on BCSP unreliable
+ channel 0 */
+ struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
+ return nskb;
+ }
+
+ /* We have nothing to send */
+ return NULL;
+}
+
+static int bcsp_flush(struct hci_uart *hu)
+{
+ BT_DBG("hu %p", hu);
+ return 0;
+}
+
+/* Remove ack'ed packets */
+static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
+{
+ struct sk_buff *skb, *tmp;
+ unsigned long flags;
+ int i, pkts_to_be_removed;
+ u8 seqno;
+
+ spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+ pkts_to_be_removed = skb_queue_len(&bcsp->unack);
+ seqno = bcsp->msgq_txseq;
+
+ while (pkts_to_be_removed) {
+ if (bcsp->rxack == seqno)
+ break;
+ pkts_to_be_removed--;
+ seqno = (seqno - 1) & 0x07;
+ }
+
+ if (bcsp->rxack != seqno)
+ BT_ERR("Peer acked invalid packet");
+
+ BT_DBG("Removing %u pkts out of %u, up to seqno %u",
+ pkts_to_be_removed, skb_queue_len(&bcsp->unack),
+ (seqno - 1) & 0x07);
+
+ i = 0;
+ skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
+ if (i++ >= pkts_to_be_removed)
+ break;
+
+ __skb_unlink(skb, &bcsp->unack);
+ kfree_skb(skb);
+ }
+
+ if (skb_queue_empty(&bcsp->unack))
+ del_timer(&bcsp->tbcsp);
+
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+ if (i != pkts_to_be_removed)
+ BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
+}
+
+/* Handle BCSP link-establishment packets. When we
+ detect a "sync" packet, symptom that the BT module has reset,
+ we do nothing :) (yet) */
+static void bcsp_handle_le_pkt(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
+ u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
+ u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
+
+ /* spot "conf" pkts and reply with a "conf rsp" pkt */
+ if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+ !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+ struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
+
+ BT_DBG("Found a LE conf pkt");
+ if (!nskb)
+ return;
+ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
+ bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+
+ skb_queue_head(&bcsp->unrel, nskb);
+ hci_uart_tx_wakeup(hu);
+ }
+ /* Spot "sync" pkts. If we find one...disaster! */
+ else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+ !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+ BT_ERR("Found a LE sync pkt, card has reset");
+ }
+}
+
+static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
+{
+ const u8 c0 = 0xc0, db = 0xdb;
+
+ switch (bcsp->rx_esc_state) {
+ case BCSP_ESCSTATE_NOESC:
+ switch (byte) {
+ case 0xdb:
+ bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
+ break;
+ default:
+ memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ bcsp->rx_state != BCSP_W4_CRC)
+ bcsp_crc_update(&bcsp->message_crc, byte);
+ bcsp->rx_count--;
+ }
+ break;
+
+ case BCSP_ESCSTATE_ESC:
+ switch (byte) {
+ case 0xdc:
+ memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ bcsp->rx_state != BCSP_W4_CRC)
+ bcsp_crc_update(&bcsp-> message_crc, 0xc0);
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+ bcsp->rx_count--;
+ break;
+
+ case 0xdd:
+ memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
+ if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
+ bcsp->rx_state != BCSP_W4_CRC)
+ bcsp_crc_update(&bcsp-> message_crc, 0xdb);
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+ bcsp->rx_count--;
+ break;
+
+ default:
+ BT_ERR ("Invalid byte %02x after esc byte", byte);
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_skb = NULL;
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ }
+ }
+}
+
+static void bcsp_complete_rx_pkt(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ int pass_up;
+
+ if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */
+ BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
+ bcsp->rxseq_txack++;
+ bcsp->rxseq_txack %= 0x8;
+ bcsp->txack_req = 1;
+
+ /* If needed, transmit an ack pkt */
+ hci_uart_tx_wakeup(hu);
+ }
+
+ bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
+ BT_DBG("Request for pkt %u from card", bcsp->rxack);
+
+ bcsp_pkt_cull(bcsp);
+ if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
+ bcsp->rx_skb->data[0] & 0x80) {
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT;
+ pass_up = 1;
+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
+ bcsp->rx_skb->data[0] & 0x80) {
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+ pass_up = 1;
+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_SCODATA_PKT;
+ pass_up = 1;
+ } else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
+ !(bcsp->rx_skb->data[0] & 0x80)) {
+ bcsp_handle_le_pkt(hu);
+ pass_up = 0;
+ } else
+ pass_up = 0;
+
+ if (!pass_up) {
+ struct hci_event_hdr hdr;
+ u8 desc = (bcsp->rx_skb->data[1] & 0x0f);
+
+ if (desc != 0 && desc != 1) {
+ if (hciextn) {
+ desc |= 0xc0;
+ skb_pull(bcsp->rx_skb, 4);
+ memcpy(skb_push(bcsp->rx_skb, 1), &desc, 1);
+
+ hdr.evt = 0xff;
+ hdr.plen = bcsp->rx_skb->len;
+ memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
+ bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT;
+
+ hci_recv_frame(bcsp->rx_skb);
+ } else {
+ BT_ERR ("Packet for unknown channel (%u %s)",
+ bcsp->rx_skb->data[1] & 0x0f,
+ bcsp->rx_skb->data[0] & 0x80 ?
+ "reliable" : "unreliable");
+ kfree_skb(bcsp->rx_skb);
+ }
+ } else
+ kfree_skb(bcsp->rx_skb);
+ } else {
+ /* Pull out BCSP hdr */
+ skb_pull(bcsp->rx_skb, 4);
+
+ hci_recv_frame(bcsp->rx_skb);
+ }
+
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_skb = NULL;
+}
+
+static u16 bscp_get_crc(struct bcsp_struct *bcsp)
+{
+ return get_unaligned_be16(&bcsp->rx_skb->data[bcsp->rx_skb->len - 2]);
+}
+
+/* Recv data */
+static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ register unsigned char *ptr;
+
+ BT_DBG("hu %p count %d rx_state %d rx_count %ld",
+ hu, count, bcsp->rx_state, bcsp->rx_count);
+
+ ptr = data;
+ while (count) {
+ if (bcsp->rx_count) {
+ if (*ptr == 0xc0) {
+ BT_ERR("Short BCSP packet");
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_START;
+ bcsp->rx_count = 0;
+ } else
+ bcsp_unslip_one_byte(bcsp, *ptr);
+
+ ptr++; count--;
+ continue;
+ }
+
+ switch (bcsp->rx_state) {
+ case BCSP_W4_BCSP_HDR:
+ if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
+ bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
+ BT_ERR("Error in BCSP hdr checksum");
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ continue;
+ }
+ if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
+ && (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
+ BT_ERR ("Out-of-order packet arrived, got %u expected %u",
+ bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ continue;
+ }
+ bcsp->rx_state = BCSP_W4_DATA;
+ bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
+ (bcsp->rx_skb->data[2] << 4); /* May be 0 */
+ continue;
+
+ case BCSP_W4_DATA:
+ if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */
+ bcsp->rx_state = BCSP_W4_CRC;
+ bcsp->rx_count = 2;
+ } else
+ bcsp_complete_rx_pkt(hu);
+ continue;
+
+ case BCSP_W4_CRC:
+ if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) {
+ BT_ERR ("Checksum failed: computed %04x received %04x",
+ bitrev16(bcsp->message_crc),
+ bscp_get_crc(bcsp));
+
+ kfree_skb(bcsp->rx_skb);
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ continue;
+ }
+ skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
+ bcsp_complete_rx_pkt(hu);
+ continue;
+
+ case BCSP_W4_PKT_DELIMITER:
+ switch (*ptr) {
+ case 0xc0:
+ bcsp->rx_state = BCSP_W4_PKT_START;
+ break;
+ default:
+ /*BT_ERR("Ignoring byte %02x", *ptr);*/
+ break;
+ }
+ ptr++; count--;
+ break;
+
+ case BCSP_W4_PKT_START:
+ switch (*ptr) {
+ case 0xc0:
+ ptr++; count--;
+ break;
+
+ default:
+ bcsp->rx_state = BCSP_W4_BCSP_HDR;
+ bcsp->rx_count = 4;
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+ BCSP_CRC_INIT(bcsp->message_crc);
+
+ /* Do not increment ptr or decrement count
+ * Allocate packet. Max len of a BCSP pkt=
+ * 0xFFF (payload) +4 (header) +2 (crc) */
+
+ bcsp->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC);
+ if (!bcsp->rx_skb) {
+ BT_ERR("Can't allocate mem for new packet");
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_count = 0;
+ return 0;
+ }
+ bcsp->rx_skb->dev = (void *) hu->hdev;
+ break;
+ }
+ break;
+ }
+ }
+ return count;
+}
+
+ /* Arrange to retransmit all messages in the relq. */
+static void bcsp_timed_event(unsigned long arg)
+{
+ struct hci_uart *hu = (struct hci_uart *) arg;
+ struct bcsp_struct *bcsp = hu->priv;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
+
+ spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+
+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
+ skb_queue_head(&bcsp->rel, skb);
+ }
+
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+ hci_uart_tx_wakeup(hu);
+}
+
+static int bcsp_open(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp;
+
+ BT_DBG("hu %p", hu);
+
+ bcsp = kzalloc(sizeof(*bcsp), GFP_ATOMIC);
+ if (!bcsp)
+ return -ENOMEM;
+
+ hu->priv = bcsp;
+ skb_queue_head_init(&bcsp->unack);
+ skb_queue_head_init(&bcsp->rel);
+ skb_queue_head_init(&bcsp->unrel);
+
+ init_timer(&bcsp->tbcsp);
+ bcsp->tbcsp.function = bcsp_timed_event;
+ bcsp->tbcsp.data = (u_long) hu;
+
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+
+ if (txcrc)
+ bcsp->use_crc = 1;
+
+ return 0;
+}
+
+static int bcsp_close(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+ hu->priv = NULL;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&bcsp->unack);
+ skb_queue_purge(&bcsp->rel);
+ skb_queue_purge(&bcsp->unrel);
+ del_timer(&bcsp->tbcsp);
+
+ kfree(bcsp);
+ return 0;
+}
+
+static struct hci_uart_proto bcsp = {
+ .id = HCI_UART_BCSP,
+ .open = bcsp_open,
+ .close = bcsp_close,
+ .enqueue = bcsp_enqueue,
+ .dequeue = bcsp_dequeue,
+ .recv = bcsp_recv,
+ .flush = bcsp_flush
+};
+
+int bcsp_init(void)
+{
+ int err = hci_uart_register_proto(&bcsp);
+
+ if (!err)
+ BT_INFO("HCI BCSP protocol initialized");
+ else
+ BT_ERR("HCI BCSP protocol registration failed");
+
+ return err;
+}
+
+int bcsp_deinit(void)
+{
+ return hci_uart_unregister_proto(&bcsp);
+}
+
+module_param(txcrc, bool, 0644);
+MODULE_PARM_DESC(txcrc, "Transmit CRC with every BCSP packet");
+
+module_param(hciextn, bool, 0644);
+MODULE_PARM_DESC(hciextn, "Convert HCI Extensions into BCSP packets");
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
new file mode 100644
index 0000000..bfbae14
--- /dev/null
+++ b/drivers/bluetooth/hci_h4.c
@@ -0,0 +1,296 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#ifndef CONFIG_BT_HCIUART_DEBUG
+#undef BT_DBG
+#define BT_DBG( A... )
+#endif
+
+#define VERSION "1.2"
+
+struct h4_struct {
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ 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)
+{
+ struct h4_struct *h4;
+
+ BT_DBG("hu %p", hu);
+
+ h4 = kzalloc(sizeof(*h4), GFP_ATOMIC);
+ if (!h4)
+ return -ENOMEM;
+
+ skb_queue_head_init(&h4->txq);
+
+ hu->priv = h4;
+ return 0;
+}
+
+/* Flush protocol data */
+static int h4_flush(struct hci_uart *hu)
+{
+ struct h4_struct *h4 = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&h4->txq);
+
+ return 0;
+}
+
+/* Close protocol */
+static int h4_close(struct hci_uart *hu)
+{
+ struct h4_struct *h4 = hu->priv;
+
+ hu->priv = NULL;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&h4->txq);
+
+ if (h4->rx_skb)
+ kfree_skb(h4->rx_skb);
+
+ hu->priv = NULL;
+ kfree(h4);
+
+ return 0;
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ struct h4_struct *h4 = hu->priv;
+
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&h4->txq, skb);
+
+ return 0;
+}
+
+static inline int h4_check_data_len(struct h4_struct *h4, int len)
+{
+ register int room = skb_tailroom(h4->rx_skb);
+
+ BT_DBG("len %d room %d", len, room);
+
+ if (!len) {
+ hci_recv_frame(h4->rx_skb);
+ } else if (len > room) {
+ BT_ERR("Data length is too large");
+ kfree_skb(h4->rx_skb);
+ } else {
+ h4->rx_state = H4_W4_DATA;
+ h4->rx_count = len;
+ return len;
+ }
+
+ h4->rx_state = H4_W4_PACKET_TYPE;
+ h4->rx_skb = NULL;
+ h4->rx_count = 0;
+
+ return 0;
+}
+
+/* Recv data */
+static int h4_recv(struct hci_uart *hu, void *data, int count)
+{
+ struct h4_struct *h4 = hu->priv;
+ register char *ptr;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+ register int len, type, dlen;
+
+ BT_DBG("hu %p count %d rx_state %ld rx_count %ld",
+ hu, count, h4->rx_state, h4->rx_count);
+
+ ptr = data;
+ while (count) {
+ if (h4->rx_count) {
+ len = min_t(unsigned int, h4->rx_count, count);
+ memcpy(skb_put(h4->rx_skb, len), ptr, len);
+ h4->rx_count -= len; count -= len; ptr += len;
+
+ if (h4->rx_count)
+ continue;
+
+ switch (h4->rx_state) {
+ case H4_W4_DATA:
+ BT_DBG("Complete data");
+
+ hci_recv_frame(h4->rx_skb);
+
+ h4->rx_state = H4_W4_PACKET_TYPE;
+ h4->rx_skb = NULL;
+ continue;
+
+ case H4_W4_EVENT_HDR:
+ eh = hci_event_hdr(h4->rx_skb);
+
+ BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+
+ h4_check_data_len(h4, eh->plen);
+ continue;
+
+ case H4_W4_ACL_HDR:
+ ah = hci_acl_hdr(h4->rx_skb);
+ dlen = __le16_to_cpu(ah->dlen);
+
+ BT_DBG("ACL header: dlen %d", dlen);
+
+ h4_check_data_len(h4, dlen);
+ continue;
+
+ case H4_W4_SCO_HDR:
+ sh = hci_sco_hdr(h4->rx_skb);
+
+ BT_DBG("SCO header: dlen %d", sh->dlen);
+
+ h4_check_data_len(h4, sh->dlen);
+ continue;
+ }
+ }
+
+ /* H4_W4_PACKET_TYPE */
+ switch (*ptr) {
+ case HCI_EVENT_PKT:
+ BT_DBG("Event packet");
+ h4->rx_state = H4_W4_EVENT_HDR;
+ h4->rx_count = HCI_EVENT_HDR_SIZE;
+ type = HCI_EVENT_PKT;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ BT_DBG("ACL packet");
+ h4->rx_state = H4_W4_ACL_HDR;
+ h4->rx_count = HCI_ACL_HDR_SIZE;
+ type = HCI_ACLDATA_PKT;
+ break;
+
+ case HCI_SCODATA_PKT:
+ BT_DBG("SCO packet");
+ h4->rx_state = H4_W4_SCO_HDR;
+ h4->rx_count = HCI_SCO_HDR_SIZE;
+ type = HCI_SCODATA_PKT;
+ break;
+
+ default:
+ BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+ hu->hdev->stat.err_rx++;
+ ptr++; count--;
+ continue;
+ };
+
+ ptr++; count--;
+
+ /* Allocate packet */
+ h4->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!h4->rx_skb) {
+ BT_ERR("Can't allocate mem for new packet");
+ h4->rx_state = H4_W4_PACKET_TYPE;
+ h4->rx_count = 0;
+ return 0;
+ }
+
+ h4->rx_skb->dev = (void *) hu->hdev;
+ bt_cb(h4->rx_skb)->pkt_type = type;
+ }
+
+ return count;
+}
+
+static struct sk_buff *h4_dequeue(struct hci_uart *hu)
+{
+ struct h4_struct *h4 = hu->priv;
+ return skb_dequeue(&h4->txq);
+}
+
+static struct hci_uart_proto h4p = {
+ .id = HCI_UART_H4,
+ .open = h4_open,
+ .close = h4_close,
+ .recv = h4_recv,
+ .enqueue = h4_enqueue,
+ .dequeue = h4_dequeue,
+ .flush = h4_flush,
+};
+
+int h4_init(void)
+{
+ int err = hci_uart_register_proto(&h4p);
+
+ if (!err)
+ BT_INFO("HCI H4 protocol initialized");
+ else
+ BT_ERR("HCI H4 protocol registration failed");
+
+ return err;
+}
+
+int h4_deinit(void)
+{
+ return hci_uart_unregister_proto(&h4p);
+}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
new file mode 100644
index 0000000..4426bb5
--- /dev/null
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -0,0 +1,584 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+#ifndef CONFIG_BT_HCIUART_DEBUG
+#undef BT_DBG
+#define BT_DBG( A... )
+#endif
+
+#define VERSION "2.2"
+
+static int reset = 0;
+
+static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+
+int hci_uart_register_proto(struct hci_uart_proto *p)
+{
+ if (p->id >= HCI_UART_MAX_PROTO)
+ return -EINVAL;
+
+ if (hup[p->id])
+ return -EEXIST;
+
+ hup[p->id] = p;
+
+ return 0;
+}
+
+int hci_uart_unregister_proto(struct hci_uart_proto *p)
+{
+ if (p->id >= HCI_UART_MAX_PROTO)
+ return -EINVAL;
+
+ if (!hup[p->id])
+ return -EINVAL;
+
+ hup[p->id] = NULL;
+
+ return 0;
+}
+
+static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
+{
+ if (id >= HCI_UART_MAX_PROTO)
+ return NULL;
+
+ return hup[id];
+}
+
+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
+{
+ struct hci_dev *hdev = hu->hdev;
+
+ /* Update HCI stat counters */
+ switch (pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ }
+}
+
+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
+{
+ struct sk_buff *skb = hu->tx_skb;
+
+ if (!skb)
+ skb = hu->proto->dequeue(hu);
+ else
+ hu->tx_skb = NULL;
+
+ return skb;
+}
+
+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;
+ }
+
+ BT_DBG("");
+
+restart:
+ clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+
+ while ((skb = hci_uart_dequeue(hu))) {
+ int len;
+
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ len = tty->ops->write(tty, skb->data, skb->len);
+ hdev->stat.byte_tx += len;
+
+ skb_pull(skb, len);
+ if (skb->len) {
+ hu->tx_skb = skb;
+ break;
+ }
+
+ hci_uart_tx_complete(hu, bt_cb(skb)->pkt_type);
+ kfree_skb(skb);
+ }
+
+ if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
+ goto restart;
+
+ clear_bit(HCI_UART_SENDING, &hu->tx_state);
+ return 0;
+}
+
+/* ------- Interface to HCI layer ------ */
+/* Initialize device */
+static int hci_uart_open(struct hci_dev *hdev)
+{
+ BT_DBG("%s %p", hdev->name, hdev);
+
+ /* Nothing to do for UART driver */
+
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ return 0;
+}
+
+/* Reset device */
+static int hci_uart_flush(struct hci_dev *hdev)
+{
+ struct hci_uart *hu = (struct hci_uart *) hdev->driver_data;
+ struct tty_struct *tty = hu->tty;
+
+ BT_DBG("hdev %p tty %p", hdev, tty);
+
+ if (hu->tx_skb) {
+ kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
+ }
+
+ /* Flush any pending characters in the driver and discipline. */
+ tty_ldisc_flush(tty);
+ tty_driver_flush_buffer(tty);
+
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ hu->proto->flush(hu);
+
+ return 0;
+}
+
+/* Close device */
+static int hci_uart_close(struct hci_dev *hdev)
+{
+ BT_DBG("hdev %p", hdev);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ hci_uart_flush(hdev);
+ hdev->flush = NULL;
+ return 0;
+}
+
+/* Send frames from HCI layer */
+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) {
+ BT_ERR("Frame for uknown device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ 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);
+
+ hu->proto->enqueue(hu, skb);
+
+ hci_uart_tx_wakeup(hu);
+
+ return 0;
+}
+
+static void hci_uart_destruct(struct hci_dev *hdev)
+{
+ if (!hdev)
+ return;
+
+ BT_DBG("%s", hdev->name);
+ kfree(hdev->driver_data);
+}
+
+/* ------ LDISC part ------ */
+/* hci_uart_tty_open
+ *
+ * Called when line discipline changed to HCI_UART.
+ *
+ * Arguments:
+ * tty pointer to tty info structure
+ * Return Value:
+ * 0 if success, otherwise error code
+ */
+static int hci_uart_tty_open(struct tty_struct *tty)
+{
+ struct hci_uart *hu = (void *) tty->disc_data;
+
+ BT_DBG("tty %p", tty);
+
+ if (hu)
+ return -EEXIST;
+
+ if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
+ BT_ERR("Can't allocate control structure");
+ return -ENFILE;
+ }
+
+ tty->disc_data = hu;
+ hu->tty = tty;
+ tty->receive_room = 65536;
+
+ spin_lock_init(&hu->rx_lock);
+
+ /* Flush any pending characters in the driver and line discipline. */
+
+ /* FIXME: why is this needed. Note don't use ldisc_ref here as the
+ open path is before the ldisc is referencable */
+
+ if (tty->ldisc.ops->flush_buffer)
+ tty->ldisc.ops->flush_buffer(tty);
+ tty_driver_flush_buffer(tty);
+
+ return 0;
+}
+
+/* hci_uart_tty_close()
+ *
+ * Called when the line discipline is changed to something
+ * else, the tty is closed, or the tty detects a hangup.
+ */
+static void hci_uart_tty_close(struct tty_struct *tty)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+
+ BT_DBG("tty %p", tty);
+
+ /* Detach from the tty */
+ tty->disc_data = NULL;
+
+ if (hu) {
+ struct hci_dev *hdev = hu->hdev;
+
+ if (hdev)
+ hci_uart_close(hdev);
+
+ if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+ hu->proto->close(hu);
+ hci_unregister_dev(hdev);
+ hci_free_dev(hdev);
+ }
+ }
+}
+
+/* hci_uart_tty_wakeup()
+ *
+ * Callback for transmit wakeup. Called when low level
+ * device driver can accept more send data.
+ *
+ * Arguments: tty pointer to associated tty instance data
+ * Return Value: None
+ */
+static void hci_uart_tty_wakeup(struct tty_struct *tty)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+
+ BT_DBG("");
+
+ if (!hu)
+ return;
+
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
+ if (tty != hu->tty)
+ return;
+
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ hci_uart_tx_wakeup(hu);
+}
+
+/* hci_uart_tty_receive()
+ *
+ * Called by tty low level driver when receive data is
+ * available.
+ *
+ * Arguments: tty pointer to tty isntance data
+ * data pointer to received data
+ * flags pointer to flags for data
+ * count count of received data in bytes
+ *
+ * Return Value: None
+ */
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+
+ if (!hu || tty != hu->tty)
+ return;
+
+ if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return;
+
+ spin_lock(&hu->rx_lock);
+ hu->proto->recv(hu, (void *) data, count);
+ hu->hdev->stat.byte_rx += count;
+ spin_unlock(&hu->rx_lock);
+
+ tty_unthrottle(tty);
+}
+
+static int hci_uart_register_dev(struct hci_uart *hu)
+{
+ struct hci_dev *hdev;
+
+ BT_DBG("");
+
+ /* Initialize and register HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
+ }
+
+ hu->hdev = hdev;
+
+ hdev->type = HCI_UART;
+ hdev->driver_data = hu;
+
+ hdev->open = hci_uart_open;
+ hdev->close = hci_uart_close;
+ hdev->flush = hci_uart_flush;
+ hdev->send = hci_uart_send_frame;
+ hdev->destruct = hci_uart_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ if (reset)
+ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int hci_uart_set_proto(struct hci_uart *hu, int id)
+{
+ struct hci_uart_proto *p;
+ int err;
+
+ p = hci_uart_get_proto(id);
+ if (!p)
+ return -EPROTONOSUPPORT;
+
+ err = p->open(hu);
+ if (err)
+ return err;
+
+ hu->proto = p;
+
+ err = hci_uart_register_dev(hu);
+ if (err) {
+ p->close(hu);
+ return err;
+ }
+
+ return 0;
+}
+
+/* hci_uart_tty_ioctl()
+ *
+ * Process IOCTL system call for the tty device.
+ *
+ * Arguments:
+ *
+ * tty pointer to tty instance data
+ * file pointer to open file object for device
+ * cmd IOCTL command code
+ * arg argument for IOCTL call (cmd dependent)
+ *
+ * Return Value: Command dependent
+ */
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hci_uart *hu = (void *)tty->disc_data;
+ int err = 0;
+
+ BT_DBG("");
+
+ /* Verify the status of the device */
+ if (!hu)
+ return -EBADF;
+
+ switch (cmd) {
+ case HCIUARTSETPROTO:
+ if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+ err = hci_uart_set_proto(hu, arg);
+ if (err) {
+ clear_bit(HCI_UART_PROTO_SET, &hu->flags);
+ return err;
+ }
+ tty->low_latency = 1;
+ } else
+ return -EBUSY;
+ break;
+
+ case HCIUARTGETPROTO:
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return hu->proto->id;
+ return -EUNATCH;
+
+ case HCIUARTGETDEVICE:
+ if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+ return hu->hdev->id;
+ return -EUNATCH;
+
+ default:
+ err = n_tty_ioctl_helper(tty, file, cmd, arg);
+ break;
+ };
+
+ return err;
+}
+
+/*
+ * We don't provide read/write/poll interface for user space.
+ */
+static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
+ unsigned char __user *buf, size_t nr)
+{
+ return 0;
+}
+
+static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *data, size_t count)
+{
+ return 0;
+}
+
+static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
+ struct file *filp, poll_table *wait)
+{
+ return 0;
+}
+
+static int __init hci_uart_init(void)
+{
+ static struct tty_ldisc_ops hci_uart_ldisc;
+ int err;
+
+ BT_INFO("HCI UART driver ver %s", VERSION);
+
+ /* Register the tty discipline */
+
+ memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
+ hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
+ hci_uart_ldisc.name = "n_hci";
+ hci_uart_ldisc.open = hci_uart_tty_open;
+ hci_uart_ldisc.close = hci_uart_tty_close;
+ hci_uart_ldisc.read = hci_uart_tty_read;
+ hci_uart_ldisc.write = hci_uart_tty_write;
+ hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
+ hci_uart_ldisc.poll = hci_uart_tty_poll;
+ hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
+ hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup;
+ hci_uart_ldisc.owner = THIS_MODULE;
+
+ if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
+ BT_ERR("HCI line discipline registration failed. (%d)", err);
+ return err;
+ }
+
+#ifdef CONFIG_BT_HCIUART_H4
+ h4_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_BCSP
+ bcsp_init();
+#endif
+#ifdef CONFIG_BT_HCIUART_LL
+ ll_init();
+#endif
+
+ return 0;
+}
+
+static void __exit hci_uart_exit(void)
+{
+ int err;
+
+#ifdef CONFIG_BT_HCIUART_H4
+ h4_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_BCSP
+ bcsp_deinit();
+#endif
+#ifdef CONFIG_BT_HCIUART_LL
+ ll_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);
+}
+
+module_init(hci_uart_init);
+module_exit(hci_uart_exit);
+
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_HCI);
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
new file mode 100644
index 0000000..b91d45a
--- /dev/null
+++ b/drivers/bluetooth/hci_ll.c
@@ -0,0 +1,536 @@
+/*
+ * Texas Instruments' Bluetooth HCILL UART protocol
+ *
+ * HCILL (HCI Low Level) is a Texas Instruments' power management
+ * protocol extension to H4.
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ *
+ * Written by Ohad Ben-Cohen <ohad@bencohen.org>
+ *
+ * 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 version 2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+
+/* HCILL commands */
+#define HCILL_GO_TO_SLEEP_IND 0x30
+#define HCILL_GO_TO_SLEEP_ACK 0x31
+#define HCILL_WAKE_UP_IND 0x32
+#define HCILL_WAKE_UP_ACK 0x33
+
+/* HCILL receiver States */
+#define HCILL_W4_PACKET_TYPE 0
+#define HCILL_W4_EVENT_HDR 1
+#define HCILL_W4_ACL_HDR 2
+#define HCILL_W4_SCO_HDR 3
+#define HCILL_W4_DATA 4
+
+/* HCILL states */
+enum hcill_states_e {
+ HCILL_ASLEEP,
+ HCILL_ASLEEP_TO_AWAKE,
+ HCILL_AWAKE,
+ HCILL_AWAKE_TO_ASLEEP
+};
+
+struct hcill_cmd {
+ u8 cmd;
+} __attribute__((packed));
+
+struct ll_struct {
+ unsigned long rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head txq;
+ spinlock_t hcill_lock; /* HCILL state lock */
+ unsigned long hcill_state; /* HCILL power state */
+ struct sk_buff_head tx_wait_q; /* HCILL wait queue */
+};
+
+/*
+ * Builds and sends an HCILL command packet.
+ * These are very simple packets with only 1 cmd byte
+ */
+static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
+{
+ int err = 0;
+ struct sk_buff *skb = NULL;
+ struct ll_struct *ll = hu->priv;
+ struct hcill_cmd *hcill_packet;
+
+ BT_DBG("hu %p cmd 0x%x", hu, cmd);
+
+ /* allocate packet */
+ skb = bt_skb_alloc(1, GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("cannot allocate memory for HCILL packet");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* prepare packet */
+ hcill_packet = (struct hcill_cmd *) skb_put(skb, 1);
+ hcill_packet->cmd = cmd;
+ skb->dev = (void *) hu->hdev;
+
+ /* send packet */
+ skb_queue_tail(&ll->txq, skb);
+out:
+ return err;
+}
+
+/* Initialize protocol */
+static int ll_open(struct hci_uart *hu)
+{
+ struct ll_struct *ll;
+
+ BT_DBG("hu %p", hu);
+
+ ll = kzalloc(sizeof(*ll), GFP_ATOMIC);
+ if (!ll)
+ return -ENOMEM;
+
+ skb_queue_head_init(&ll->txq);
+ skb_queue_head_init(&ll->tx_wait_q);
+ spin_lock_init(&ll->hcill_lock);
+
+ ll->hcill_state = HCILL_AWAKE;
+
+ hu->priv = ll;
+
+ return 0;
+}
+
+/* Flush protocol data */
+static int ll_flush(struct hci_uart *hu)
+{
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ll->tx_wait_q);
+ skb_queue_purge(&ll->txq);
+
+ return 0;
+}
+
+/* Close protocol */
+static int ll_close(struct hci_uart *hu)
+{
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ skb_queue_purge(&ll->tx_wait_q);
+ skb_queue_purge(&ll->txq);
+
+ if (ll->rx_skb)
+ kfree_skb(ll->rx_skb);
+
+ hu->priv = NULL;
+
+ kfree(ll);
+
+ return 0;
+}
+
+/*
+ * internal function, which does common work of the device wake up process:
+ * 1. places all pending packets (waiting in tx_wait_q list) in txq list.
+ * 2. changes internal state to HCILL_AWAKE.
+ * Note: assumes that hcill_lock spinlock is taken,
+ * shouldn't be called otherwise!
+ */
+static void __ll_do_awake(struct ll_struct *ll)
+{
+ struct sk_buff *skb = NULL;
+
+ while ((skb = skb_dequeue(&ll->tx_wait_q)))
+ skb_queue_tail(&ll->txq, skb);
+
+ ll->hcill_state = HCILL_AWAKE;
+}
+
+/*
+ * Called upon a wake-up-indication from the device
+ */
+static void ll_device_want_to_wakeup(struct hci_uart *hu)
+{
+ unsigned long flags;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ switch (ll->hcill_state) {
+ case HCILL_ASLEEP_TO_AWAKE:
+ /*
+ * This state means that both the host and the BRF chip
+ * have simultaneously sent a wake-up-indication packet.
+ * Traditionaly, in this case, receiving a wake-up-indication
+ * was enough and an additional wake-up-ack wasn't needed.
+ * This has changed with the BRF6350, which does require an
+ * explicit wake-up-ack. Other BRF versions, which do not
+ * require an explicit ack here, do accept it, thus it is
+ * perfectly safe to always send one.
+ */
+ BT_DBG("dual wake-up-indication");
+ /* deliberate fall-through - do not add break */
+ case HCILL_ASLEEP:
+ /* acknowledge device wake up */
+ if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
+ BT_ERR("cannot acknowledge device wake up");
+ goto out;
+ }
+ break;
+ default:
+ /* any other state is illegal */
+ BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state);
+ break;
+ }
+
+ /* send pending packets and change state to HCILL_AWAKE */
+ __ll_do_awake(ll);
+
+out:
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ /* actually send the packets */
+ hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon a sleep-indication from the device
+ */
+static void ll_device_want_to_sleep(struct hci_uart *hu)
+{
+ unsigned long flags;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ /* sanity check */
+ if (ll->hcill_state != HCILL_AWAKE)
+ BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state);
+
+ /* acknowledge device sleep */
+ if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) {
+ BT_ERR("cannot acknowledge device sleep");
+ goto out;
+ }
+
+ /* update state */
+ ll->hcill_state = HCILL_ASLEEP;
+
+out:
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ /* actually send the sleep ack packet */
+ hci_uart_tx_wakeup(hu);
+}
+
+/*
+ * Called upon wake-up-acknowledgement from the device
+ */
+static void ll_device_woke_up(struct hci_uart *hu)
+{
+ unsigned long flags;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p", hu);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ /* sanity check */
+ if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE)
+ BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state);
+
+ /* send pending packets and change state to HCILL_AWAKE */
+ __ll_do_awake(ll);
+
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ /* actually send the packets */
+ hci_uart_tx_wakeup(hu);
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+/* may be called from two simultaneous tasklets */
+static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+ unsigned long flags = 0;
+ struct ll_struct *ll = hu->priv;
+
+ BT_DBG("hu %p skb %p", hu, skb);
+
+ /* Prepend skb with frame type */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+ /* lock hcill state */
+ spin_lock_irqsave(&ll->hcill_lock, flags);
+
+ /* act according to current state */
+ switch (ll->hcill_state) {
+ case HCILL_AWAKE:
+ BT_DBG("device awake, sending normally");
+ skb_queue_tail(&ll->txq, skb);
+ break;
+ case HCILL_ASLEEP:
+ BT_DBG("device asleep, waking up and queueing packet");
+ /* save packet for later */
+ skb_queue_tail(&ll->tx_wait_q, skb);
+ /* awake device */
+ if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) {
+ BT_ERR("cannot wake up device");
+ break;
+ }
+ ll->hcill_state = HCILL_ASLEEP_TO_AWAKE;
+ break;
+ case HCILL_ASLEEP_TO_AWAKE:
+ BT_DBG("device waking up, queueing packet");
+ /* transient state; just keep packet for later */
+ skb_queue_tail(&ll->tx_wait_q, skb);
+ break;
+ default:
+ BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state);
+ kfree_skb(skb);
+ break;
+ }
+
+ spin_unlock_irqrestore(&ll->hcill_lock, flags);
+
+ return 0;
+}
+
+static inline int ll_check_data_len(struct ll_struct *ll, int len)
+{
+ register int room = skb_tailroom(ll->rx_skb);
+
+ BT_DBG("len %d room %d", len, room);
+
+ if (!len) {
+ hci_recv_frame(ll->rx_skb);
+ } else if (len > room) {
+ BT_ERR("Data length is too large");
+ kfree_skb(ll->rx_skb);
+ } else {
+ ll->rx_state = HCILL_W4_DATA;
+ ll->rx_count = len;
+ return len;
+ }
+
+ ll->rx_state = HCILL_W4_PACKET_TYPE;
+ ll->rx_skb = NULL;
+ ll->rx_count = 0;
+
+ return 0;
+}
+
+/* Recv data */
+static int ll_recv(struct hci_uart *hu, void *data, int count)
+{
+ struct ll_struct *ll = hu->priv;
+ register char *ptr;
+ struct hci_event_hdr *eh;
+ struct hci_acl_hdr *ah;
+ struct hci_sco_hdr *sh;
+ register int len, type, dlen;
+
+ BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
+
+ ptr = data;
+ while (count) {
+ if (ll->rx_count) {
+ len = min_t(unsigned int, ll->rx_count, count);
+ memcpy(skb_put(ll->rx_skb, len), ptr, len);
+ ll->rx_count -= len; count -= len; ptr += len;
+
+ if (ll->rx_count)
+ continue;
+
+ switch (ll->rx_state) {
+ case HCILL_W4_DATA:
+ BT_DBG("Complete data");
+ hci_recv_frame(ll->rx_skb);
+
+ ll->rx_state = HCILL_W4_PACKET_TYPE;
+ ll->rx_skb = NULL;
+ continue;
+
+ case HCILL_W4_EVENT_HDR:
+ eh = (struct hci_event_hdr *) ll->rx_skb->data;
+
+ BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+
+ ll_check_data_len(ll, eh->plen);
+ continue;
+
+ case HCILL_W4_ACL_HDR:
+ ah = (struct hci_acl_hdr *) ll->rx_skb->data;
+ dlen = __le16_to_cpu(ah->dlen);
+
+ BT_DBG("ACL header: dlen %d", dlen);
+
+ ll_check_data_len(ll, dlen);
+ continue;
+
+ case HCILL_W4_SCO_HDR:
+ sh = (struct hci_sco_hdr *) ll->rx_skb->data;
+
+ BT_DBG("SCO header: dlen %d", sh->dlen);
+
+ ll_check_data_len(ll, sh->dlen);
+ continue;
+ }
+ }
+
+ /* HCILL_W4_PACKET_TYPE */
+ switch (*ptr) {
+ case HCI_EVENT_PKT:
+ BT_DBG("Event packet");
+ ll->rx_state = HCILL_W4_EVENT_HDR;
+ ll->rx_count = HCI_EVENT_HDR_SIZE;
+ type = HCI_EVENT_PKT;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ BT_DBG("ACL packet");
+ ll->rx_state = HCILL_W4_ACL_HDR;
+ ll->rx_count = HCI_ACL_HDR_SIZE;
+ type = HCI_ACLDATA_PKT;
+ break;
+
+ case HCI_SCODATA_PKT:
+ BT_DBG("SCO packet");
+ ll->rx_state = HCILL_W4_SCO_HDR;
+ ll->rx_count = HCI_SCO_HDR_SIZE;
+ type = HCI_SCODATA_PKT;
+ break;
+
+ /* HCILL signals */
+ case HCILL_GO_TO_SLEEP_IND:
+ BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
+ ll_device_want_to_sleep(hu);
+ ptr++; count--;
+ continue;
+
+ case HCILL_GO_TO_SLEEP_ACK:
+ /* shouldn't happen */
+ BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state);
+ ptr++; count--;
+ continue;
+
+ case HCILL_WAKE_UP_IND:
+ BT_DBG("HCILL_WAKE_UP_IND packet");
+ ll_device_want_to_wakeup(hu);
+ ptr++; count--;
+ continue;
+
+ case HCILL_WAKE_UP_ACK:
+ BT_DBG("HCILL_WAKE_UP_ACK packet");
+ ll_device_woke_up(hu);
+ ptr++; count--;
+ continue;
+
+ default:
+ BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+ hu->hdev->stat.err_rx++;
+ ptr++; count--;
+ continue;
+ };
+
+ ptr++; count--;
+
+ /* Allocate packet */
+ ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+ if (!ll->rx_skb) {
+ BT_ERR("Can't allocate mem for new packet");
+ ll->rx_state = HCILL_W4_PACKET_TYPE;
+ ll->rx_count = 0;
+ return 0;
+ }
+
+ ll->rx_skb->dev = (void *) hu->hdev;
+ bt_cb(ll->rx_skb)->pkt_type = type;
+ }
+
+ return count;
+}
+
+static struct sk_buff *ll_dequeue(struct hci_uart *hu)
+{
+ struct ll_struct *ll = hu->priv;
+ return skb_dequeue(&ll->txq);
+}
+
+static struct hci_uart_proto llp = {
+ .id = HCI_UART_LL,
+ .open = ll_open,
+ .close = ll_close,
+ .recv = ll_recv,
+ .enqueue = ll_enqueue,
+ .dequeue = ll_dequeue,
+ .flush = ll_flush,
+};
+
+int ll_init(void)
+{
+ int err = hci_uart_register_proto(&llp);
+
+ if (!err)
+ BT_INFO("HCILL protocol initialized");
+ else
+ BT_ERR("HCILL protocol registration failed");
+
+ return err;
+}
+
+int ll_deinit(void)
+{
+ return hci_uart_unregister_proto(&llp);
+}
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
new file mode 100644
index 0000000..50113db
--- /dev/null
+++ b/drivers/bluetooth/hci_uart.h
@@ -0,0 +1,93 @@
+/*
+ *
+ * Bluetooth HCI UART driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef N_HCI
+#define N_HCI 15
+#endif
+
+/* Ioctls */
+#define HCIUARTSETPROTO _IOW('U', 200, int)
+#define HCIUARTGETPROTO _IOR('U', 201, int)
+#define HCIUARTGETDEVICE _IOR('U', 202, int)
+
+/* UART protocols */
+#define HCI_UART_MAX_PROTO 5
+
+#define HCI_UART_H4 0
+#define HCI_UART_BCSP 1
+#define HCI_UART_3WIRE 2
+#define HCI_UART_H4DS 3
+#define HCI_UART_LL 4
+
+struct hci_uart;
+
+struct hci_uart_proto {
+ unsigned int id;
+ int (*open)(struct hci_uart *hu);
+ int (*close)(struct hci_uart *hu);
+ int (*flush)(struct hci_uart *hu);
+ int (*recv)(struct hci_uart *hu, void *data, int len);
+ int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
+ struct sk_buff *(*dequeue)(struct hci_uart *hu);
+};
+
+struct hci_uart {
+ struct tty_struct *tty;
+ struct hci_dev *hdev;
+ unsigned long flags;
+
+ struct hci_uart_proto *proto;
+ void *priv;
+
+ struct sk_buff *tx_skb;
+ unsigned long tx_state;
+ spinlock_t rx_lock;
+};
+
+/* HCI_UART flag bits */
+#define HCI_UART_PROTO_SET 0
+
+/* TX states */
+#define HCI_UART_SENDING 1
+#define HCI_UART_TX_WAKEUP 2
+
+int hci_uart_register_proto(struct hci_uart_proto *p);
+int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_tx_wakeup(struct hci_uart *hu);
+
+#ifdef CONFIG_BT_HCIUART_H4
+int h4_init(void);
+int h4_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_BCSP
+int bcsp_init(void);
+int bcsp_deinit(void);
+#endif
+
+#ifdef CONFIG_BT_HCIUART_LL
+int ll_init(void);
+int ll_deinit(void);
+#endif
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
new file mode 100644
index 0000000..3c45392
--- /dev/null
+++ b/drivers/bluetooth/hci_usb.c
@@ -0,0 +1,1136 @@
+/*
+ HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * Bluetooth HCI USB driver.
+ * Based on original USB Bluetooth driver for Linux kernel
+ * Copyright (c) 2000 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (c) 2000 Mark Douglas Corner <mcorner@umich.edu>
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/unistd.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_usb.h"
+
+#ifndef CONFIG_BT_HCIUSB_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#ifndef CONFIG_BT_HCIUSB_ZERO_PACKET
+#undef URB_ZERO_PACKET
+#define URB_ZERO_PACKET 0
+#endif
+
+static int ignore_dga;
+static int ignore_csr;
+static int ignore_sniffer;
+static int disable_scofix;
+static int force_scofix;
+static int reset;
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+static int isoc = 2;
+#endif
+
+#define VERSION "2.10"
+
+static struct usb_driver hci_usb_driver;
+
+static struct usb_device_id bluetooth_ids[] = {
+ /* Generic Bluetooth USB device */
+ { USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
+
+ /* AVM BlueFRITZ! USB v2.0 */
+ { USB_DEVICE(0x057c, 0x3800) },
+
+ /* Bluetooth Ultraport Module from IBM */
+ { USB_DEVICE(0x04bf, 0x030a) },
+
+ /* ALPS Modules with non-standard id */
+ { USB_DEVICE(0x044e, 0x3001) },
+ { USB_DEVICE(0x044e, 0x3002) },
+
+ /* Ericsson with non-standard id */
+ { USB_DEVICE(0x0bdb, 0x1002) },
+
+ /* Canyon CN-BTU1 with HID interfaces */
+ { USB_DEVICE(0x0c10, 0x0000), .driver_info = HCI_RESET },
+
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+static struct usb_device_id blacklist_ids[] = {
+ /* CSR BlueCore devices */
+ { USB_DEVICE(0x0a12, 0x0001), .driver_info = HCI_CSR },
+
+ /* Broadcom BCM2033 without firmware */
+ { USB_DEVICE(0x0a5c, 0x2033), .driver_info = HCI_IGNORE },
+
+ /* Broadcom BCM2035 */
+ { USB_DEVICE(0x0a5c, 0x2035), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x200a), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2009), .driver_info = HCI_BCM92035 },
+
+ /* Broadcom BCM2045 */
+ { USB_DEVICE(0x0a5c, 0x2039), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2101), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+
+ /* IBM/Lenovo ThinkPad with Broadcom chip */
+ { USB_DEVICE(0x0a5c, 0x201e), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ { USB_DEVICE(0x0a5c, 0x2110), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+
+ /* Targus ACB10US */
+ { USB_DEVICE(0x0a5c, 0x2100), .driver_info = HCI_RESET },
+
+ /* ANYCOM Bluetooth USB-200 and USB-250 */
+ { USB_DEVICE(0x0a5c, 0x2111), .driver_info = HCI_RESET },
+
+ /* HP laptop with Broadcom chip */
+ { USB_DEVICE(0x03f0, 0x171d), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+
+ /* Dell laptop with Broadcom chip */
+ { USB_DEVICE(0x413c, 0x8126), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ /* Dell Wireless 370 */
+ { USB_DEVICE(0x413c, 0x8156), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ /* Dell Wireless 410 */
+ { USB_DEVICE(0x413c, 0x8152), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+
+ /* Broadcom 2046 */
+ { USB_DEVICE(0x0a5c, 0x2151), .driver_info = HCI_RESET },
+
+ /* Microsoft Wireless Transceiver for Bluetooth 2.0 */
+ { USB_DEVICE(0x045e, 0x009c), .driver_info = HCI_RESET },
+
+ /* Kensington Bluetooth USB adapter */
+ { USB_DEVICE(0x047d, 0x105d), .driver_info = HCI_RESET },
+ { USB_DEVICE(0x047d, 0x105e), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+
+ /* ISSC Bluetooth Adapter v3.1 */
+ { USB_DEVICE(0x1131, 0x1001), .driver_info = HCI_RESET },
+
+ /* RTX Telecom based adapters with buggy SCO support */
+ { USB_DEVICE(0x0400, 0x0807), .driver_info = HCI_BROKEN_ISOC },
+ { USB_DEVICE(0x0400, 0x080a), .driver_info = HCI_BROKEN_ISOC },
+
+ /* CONWISE Technology based adapters with buggy SCO support */
+ { USB_DEVICE(0x0e5e, 0x6622), .driver_info = HCI_BROKEN_ISOC },
+
+ /* Belkin F8T012 and F8T013 devices */
+ { USB_DEVICE(0x050d, 0x0012), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+ { USB_DEVICE(0x050d, 0x0013), .driver_info = HCI_RESET | HCI_WRONG_SCO_MTU },
+
+ /* Digianswer devices */
+ { USB_DEVICE(0x08fd, 0x0001), .driver_info = HCI_DIGIANSWER },
+ { USB_DEVICE(0x08fd, 0x0002), .driver_info = HCI_IGNORE },
+
+ /* CSR BlueCore Bluetooth Sniffer */
+ { USB_DEVICE(0x0a12, 0x0002), .driver_info = HCI_SNIFFER },
+
+ /* Frontline ComProbe Bluetooth Sniffer */
+ { USB_DEVICE(0x16d3, 0x0002), .driver_info = HCI_SNIFFER },
+
+ { } /* Terminating entry */
+};
+
+static struct _urb *_urb_alloc(int isoc, gfp_t gfp)
+{
+ struct _urb *_urb = kmalloc(sizeof(struct _urb) +
+ sizeof(struct usb_iso_packet_descriptor) * isoc, gfp);
+ if (_urb) {
+ memset(_urb, 0, sizeof(*_urb));
+ usb_init_urb(&_urb->urb);
+ }
+ return _urb;
+}
+
+static struct _urb *_urb_dequeue(struct _urb_queue *q)
+{
+ struct _urb *_urb = NULL;
+ unsigned long flags;
+ spin_lock_irqsave(&q->lock, flags);
+ {
+ struct list_head *head = &q->head;
+ struct list_head *next = head->next;
+ if (next != head) {
+ _urb = list_entry(next, struct _urb, list);
+ list_del(next); _urb->queue = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+ return _urb;
+}
+
+static void hci_usb_rx_complete(struct urb *urb);
+static void hci_usb_tx_complete(struct urb *urb);
+
+#define __pending_tx(husb, type) (&husb->pending_tx[type-1])
+#define __pending_q(husb, type) (&husb->pending_q[type-1])
+#define __completed_q(husb, type) (&husb->completed_q[type-1])
+#define __transmit_q(husb, type) (&husb->transmit_q[type-1])
+
+static inline struct _urb *__get_completed(struct hci_usb *husb, int type)
+{
+ return _urb_dequeue(__completed_q(husb, type));
+}
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+static void __fill_isoc_desc(struct urb *urb, int len, int mtu)
+{
+ int offset = 0, i;
+
+ BT_DBG("len %d mtu %d", len, mtu);
+
+ for (i=0; i < HCI_MAX_ISOC_FRAMES && len >= mtu; i++, offset += mtu, len -= mtu) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = mtu;
+ BT_DBG("desc %d offset %d len %d", i, offset, mtu);
+ }
+ if (len && i < HCI_MAX_ISOC_FRAMES) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = len;
+ BT_DBG("desc %d offset %d len %d", i, offset, len);
+ i++;
+ }
+ urb->number_of_packets = i;
+}
+#endif
+
+static int hci_usb_intr_rx_submit(struct hci_usb *husb)
+{
+ struct _urb *_urb;
+ struct urb *urb;
+ int err, pipe, interval, size;
+ void *buf;
+
+ BT_DBG("%s", husb->hdev->name);
+
+ size = le16_to_cpu(husb->intr_in_ep->desc.wMaxPacketSize);
+
+ buf = kmalloc(size, GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ _urb = _urb_alloc(0, GFP_ATOMIC);
+ if (!_urb) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+ _urb->type = HCI_EVENT_PKT;
+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+
+ urb = &_urb->urb;
+ pipe = usb_rcvintpipe(husb->udev, husb->intr_in_ep->desc.bEndpointAddress);
+ interval = husb->intr_in_ep->desc.bInterval;
+ usb_fill_int_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb, interval);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s intr rx submit failed urb %p err %d",
+ husb->hdev->name, urb, err);
+ _urb_unlink(_urb);
+ kfree(_urb);
+ kfree(buf);
+ }
+ return err;
+}
+
+static int hci_usb_bulk_rx_submit(struct hci_usb *husb)
+{
+ struct _urb *_urb;
+ struct urb *urb;
+ int err, pipe, size = HCI_MAX_FRAME_SIZE;
+ void *buf;
+
+ buf = kmalloc(size, GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ _urb = _urb_alloc(0, GFP_ATOMIC);
+ if (!_urb) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+ _urb->type = HCI_ACLDATA_PKT;
+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+
+ urb = &_urb->urb;
+ pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep->desc.bEndpointAddress);
+ usb_fill_bulk_urb(urb, husb->udev, pipe, buf, size, hci_usb_rx_complete, husb);
+ urb->transfer_flags = 0;
+
+ BT_DBG("%s urb %p", husb->hdev->name, urb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s bulk rx submit failed urb %p err %d",
+ husb->hdev->name, urb, err);
+ _urb_unlink(_urb);
+ kfree(_urb);
+ kfree(buf);
+ }
+ return err;
+}
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+static int hci_usb_isoc_rx_submit(struct hci_usb *husb)
+{
+ struct _urb *_urb;
+ struct urb *urb;
+ int err, mtu, size;
+ void *buf;
+
+ mtu = le16_to_cpu(husb->isoc_in_ep->desc.wMaxPacketSize);
+ size = mtu * HCI_MAX_ISOC_FRAMES;
+
+ buf = kmalloc(size, GFP_ATOMIC);
+ if (!buf)
+ return -ENOMEM;
+
+ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
+ if (!_urb) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+ _urb->type = HCI_SCODATA_PKT;
+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+
+ urb = &_urb->urb;
+
+ urb->context = husb;
+ urb->dev = husb->udev;
+ urb->pipe = usb_rcvisocpipe(husb->udev, husb->isoc_in_ep->desc.bEndpointAddress);
+ urb->complete = hci_usb_rx_complete;
+
+ urb->interval = husb->isoc_in_ep->desc.bInterval;
+
+ urb->transfer_buffer_length = size;
+ urb->transfer_buffer = buf;
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ __fill_isoc_desc(urb, size, mtu);
+
+ BT_DBG("%s urb %p", husb->hdev->name, urb);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s isoc rx submit failed urb %p err %d",
+ husb->hdev->name, urb, err);
+ _urb_unlink(_urb);
+ kfree(_urb);
+ kfree(buf);
+ }
+ return err;
+}
+#endif
+
+/* Initialize device */
+static int hci_usb_open(struct hci_dev *hdev)
+{
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+ int i, err;
+ unsigned long flags;
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ write_lock_irqsave(&husb->completion_lock, flags);
+
+ err = hci_usb_intr_rx_submit(husb);
+ if (!err) {
+ for (i = 0; i < HCI_MAX_BULK_RX; i++)
+ hci_usb_bulk_rx_submit(husb);
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+ if (husb->isoc_iface)
+ for (i = 0; i < HCI_MAX_ISOC_RX; i++)
+ hci_usb_isoc_rx_submit(husb);
+#endif
+ } else {
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ }
+
+ write_unlock_irqrestore(&husb->completion_lock, flags);
+ return err;
+}
+
+/* Reset device */
+static int hci_usb_flush(struct hci_dev *hdev)
+{
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+ int i;
+
+ BT_DBG("%s", hdev->name);
+
+ for (i = 0; i < 4; i++)
+ skb_queue_purge(&husb->transmit_q[i]);
+ return 0;
+}
+
+static void hci_usb_unlink_urbs(struct hci_usb *husb)
+{
+ int i;
+
+ BT_DBG("%s", husb->hdev->name);
+
+ for (i = 0; i < 4; i++) {
+ struct _urb *_urb;
+ struct urb *urb;
+
+ /* Kill pending requests */
+ while ((_urb = _urb_dequeue(&husb->pending_q[i]))) {
+ urb = &_urb->urb;
+ BT_DBG("%s unlinking _urb %p type %d urb %p",
+ husb->hdev->name, _urb, _urb->type, urb);
+ usb_kill_urb(urb);
+ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+ }
+
+ /* Release completed requests */
+ while ((_urb = _urb_dequeue(&husb->completed_q[i]))) {
+ urb = &_urb->urb;
+ BT_DBG("%s freeing _urb %p type %d urb %p",
+ husb->hdev->name, _urb, _urb->type, urb);
+ kfree(urb->setup_packet);
+ kfree(urb->transfer_buffer);
+ kfree(_urb);
+ }
+ }
+}
+
+/* Close device */
+static int hci_usb_close(struct hci_dev *hdev)
+{
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+ unsigned long flags;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ BT_DBG("%s", hdev->name);
+
+ /* Synchronize with completion handlers */
+ write_lock_irqsave(&husb->completion_lock, flags);
+ write_unlock_irqrestore(&husb->completion_lock, flags);
+
+ hci_usb_unlink_urbs(husb);
+ hci_usb_flush(hdev);
+ return 0;
+}
+
+static int __tx_submit(struct hci_usb *husb, struct _urb *_urb)
+{
+ struct urb *urb = &_urb->urb;
+ int err;
+
+ BT_DBG("%s urb %p type %d", husb->hdev->name, urb, _urb->type);
+
+ _urb_queue_tail(__pending_q(husb, _urb->type), _urb);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ BT_ERR("%s tx submit failed urb %p type %d err %d",
+ husb->hdev->name, urb, _urb->type, err);
+ _urb_unlink(_urb);
+ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+ } else
+ atomic_inc(__pending_tx(husb, _urb->type));
+
+ return err;
+}
+
+static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
+{
+ struct _urb *_urb = __get_completed(husb, bt_cb(skb)->pkt_type);
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+
+ if (!_urb) {
+ _urb = _urb_alloc(0, GFP_ATOMIC);
+ if (!_urb)
+ return -ENOMEM;
+ _urb->type = bt_cb(skb)->pkt_type;
+
+ dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
+ if (!dr) {
+ kfree(_urb);
+ return -ENOMEM;
+ }
+ } else
+ dr = (void *) _urb->urb.setup_packet;
+
+ dr->bRequestType = husb->ctrl_req;
+ dr->bRequest = 0;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ urb = &_urb->urb;
+ usb_fill_control_urb(urb, husb->udev, usb_sndctrlpipe(husb->udev, 0),
+ (void *) dr, skb->data, skb->len, hci_usb_tx_complete, husb);
+
+ BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);
+
+ _urb->priv = skb;
+ return __tx_submit(husb, _urb);
+}
+
+static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
+{
+ struct _urb *_urb = __get_completed(husb, bt_cb(skb)->pkt_type);
+ struct urb *urb;
+ int pipe;
+
+ if (!_urb) {
+ _urb = _urb_alloc(0, GFP_ATOMIC);
+ if (!_urb)
+ return -ENOMEM;
+ _urb->type = bt_cb(skb)->pkt_type;
+ }
+
+ urb = &_urb->urb;
+ pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep->desc.bEndpointAddress);
+ usb_fill_bulk_urb(urb, husb->udev, pipe, skb->data, skb->len,
+ hci_usb_tx_complete, husb);
+ urb->transfer_flags = URB_ZERO_PACKET;
+
+ BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);
+
+ _urb->priv = skb;
+ return __tx_submit(husb, _urb);
+}
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+static inline int hci_usb_send_isoc(struct hci_usb *husb, struct sk_buff *skb)
+{
+ struct _urb *_urb = __get_completed(husb, bt_cb(skb)->pkt_type);
+ struct urb *urb;
+
+ if (!_urb) {
+ _urb = _urb_alloc(HCI_MAX_ISOC_FRAMES, GFP_ATOMIC);
+ if (!_urb)
+ return -ENOMEM;
+ _urb->type = bt_cb(skb)->pkt_type;
+ }
+
+ BT_DBG("%s skb %p len %d", husb->hdev->name, skb, skb->len);
+
+ urb = &_urb->urb;
+
+ urb->context = husb;
+ urb->dev = husb->udev;
+ urb->pipe = usb_sndisocpipe(husb->udev, husb->isoc_out_ep->desc.bEndpointAddress);
+ urb->complete = hci_usb_tx_complete;
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ urb->interval = husb->isoc_out_ep->desc.bInterval;
+
+ urb->transfer_buffer = skb->data;
+ urb->transfer_buffer_length = skb->len;
+
+ __fill_isoc_desc(urb, skb->len, le16_to_cpu(husb->isoc_out_ep->desc.wMaxPacketSize));
+
+ _urb->priv = skb;
+ return __tx_submit(husb, _urb);
+}
+#endif
+
+static void hci_usb_tx_process(struct hci_usb *husb)
+{
+ struct sk_buff_head *q;
+ struct sk_buff *skb;
+
+ BT_DBG("%s", husb->hdev->name);
+
+ do {
+ clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
+
+ /* Process command queue */
+ q = __transmit_q(husb, HCI_COMMAND_PKT);
+ if (!atomic_read(__pending_tx(husb, HCI_COMMAND_PKT)) &&
+ (skb = skb_dequeue(q))) {
+ if (hci_usb_send_ctrl(husb, skb) < 0)
+ skb_queue_head(q, skb);
+ }
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+ /* Process SCO queue */
+ q = __transmit_q(husb, HCI_SCODATA_PKT);
+ if (atomic_read(__pending_tx(husb, HCI_SCODATA_PKT)) < HCI_MAX_ISOC_TX &&
+ (skb = skb_dequeue(q))) {
+ if (hci_usb_send_isoc(husb, skb) < 0)
+ skb_queue_head(q, skb);
+ }
+#endif
+
+ /* Process ACL queue */
+ q = __transmit_q(husb, HCI_ACLDATA_PKT);
+ while (atomic_read(__pending_tx(husb, HCI_ACLDATA_PKT)) < HCI_MAX_BULK_TX &&
+ (skb = skb_dequeue(q))) {
+ if (hci_usb_send_bulk(husb, skb) < 0) {
+ skb_queue_head(q, skb);
+ break;
+ }
+ }
+ } while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
+}
+
+static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
+{
+ /* Serialize TX queue processing to avoid data reordering */
+ if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {
+ hci_usb_tx_process(husb);
+ clear_bit(HCI_USB_TX_PROCESS, &husb->state);
+ } else
+ set_bit(HCI_USB_TX_WAKEUP, &husb->state);
+}
+
+/* Send frames from HCI layer */
+static int hci_usb_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+ struct hci_usb *husb;
+
+ if (!hdev) {
+ BT_ERR("frame for uknown device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
+
+ husb = (struct hci_usb *) hdev->driver_data;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+#endif
+
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+
+ read_lock(&husb->completion_lock);
+
+ skb_queue_tail(__transmit_q(husb, bt_cb(skb)->pkt_type), skb);
+ hci_usb_tx_wakeup(husb);
+
+ read_unlock(&husb->completion_lock);
+ return 0;
+}
+
+static void hci_usb_rx_complete(struct urb *urb)
+{
+ struct _urb *_urb = container_of(urb, struct _urb, urb);
+ struct hci_usb *husb = (void *) urb->context;
+ struct hci_dev *hdev = husb->hdev;
+ int err, count = urb->actual_length;
+
+ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb,
+ _urb->type, urb->status, count, urb->transfer_flags);
+
+ read_lock(&husb->completion_lock);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto unlock;
+
+ if (urb->status || !count)
+ goto resubmit;
+
+ if (_urb->type == HCI_SCODATA_PKT) {
+#ifdef CONFIG_BT_HCIUSB_SCO
+ int i;
+ for (i=0; i < urb->number_of_packets; i++) {
+ BT_DBG("desc %d status %d offset %d len %d", i,
+ urb->iso_frame_desc[i].status,
+ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+
+ if (!urb->iso_frame_desc[i].status) {
+ husb->hdev->stat.byte_rx += urb->iso_frame_desc[i].actual_length;
+ hci_recv_fragment(husb->hdev, _urb->type,
+ urb->transfer_buffer + urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+ }
+ }
+#else
+ ;
+#endif
+ } else {
+ husb->hdev->stat.byte_rx += count;
+ err = hci_recv_fragment(husb->hdev, _urb->type, urb->transfer_buffer, count);
+ if (err < 0) {
+ BT_ERR("%s corrupted packet: type %d count %d",
+ husb->hdev->name, _urb->type, count);
+ hdev->stat.err_rx++;
+ }
+ }
+
+resubmit:
+ urb->dev = husb->udev;
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb,
+ _urb->type, err);
+
+unlock:
+ read_unlock(&husb->completion_lock);
+}
+
+static void hci_usb_tx_complete(struct urb *urb)
+{
+ struct _urb *_urb = container_of(urb, struct _urb, urb);
+ struct hci_usb *husb = (void *) urb->context;
+ struct hci_dev *hdev = husb->hdev;
+
+ BT_DBG("%s urb %p status %d flags %x", hdev->name, urb,
+ urb->status, urb->transfer_flags);
+
+ atomic_dec(__pending_tx(husb, _urb->type));
+
+ urb->transfer_buffer = NULL;
+ kfree_skb((struct sk_buff *) _urb->priv);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+ read_lock(&husb->completion_lock);
+
+ _urb_unlink(_urb);
+ _urb_queue_tail(__completed_q(husb, _urb->type), _urb);
+
+ hci_usb_tx_wakeup(husb);
+
+ read_unlock(&husb->completion_lock);
+}
+
+static void hci_usb_destruct(struct hci_dev *hdev)
+{
+ struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+
+ BT_DBG("%s", hdev->name);
+
+ kfree(husb);
+}
+
+static void hci_usb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+ BT_DBG("%s evt %d", hdev->name, evt);
+}
+
+static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_host_endpoint *bulk_out_ep = NULL;
+ struct usb_host_endpoint *bulk_in_ep = NULL;
+ struct usb_host_endpoint *intr_in_ep = NULL;
+ struct usb_host_endpoint *ep;
+ struct usb_host_interface *uif;
+ struct usb_interface *isoc_iface;
+ struct hci_usb *husb;
+ struct hci_dev *hdev;
+ int i, e, size, isoc_ifnum, isoc_alts;
+
+ BT_DBG("udev %p intf %p", udev, intf);
+
+ if (!id->driver_info) {
+ const struct usb_device_id *match;
+ match = usb_match_id(intf, blacklist_ids);
+ if (match)
+ id = match;
+ }
+
+ if (id->driver_info & HCI_IGNORE)
+ return -ENODEV;
+
+ if (ignore_dga && id->driver_info & HCI_DIGIANSWER)
+ return -ENODEV;
+
+ if (ignore_csr && id->driver_info & HCI_CSR)
+ return -ENODEV;
+
+ if (ignore_sniffer && id->driver_info & HCI_SNIFFER)
+ return -ENODEV;
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber > 0)
+ return -ENODEV;
+
+ /* Find endpoints that we need */
+ uif = intf->cur_altsetting;
+ for (e = 0; e < uif->desc.bNumEndpoints; e++) {
+ ep = &uif->endpoint[e];
+
+ switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_INT:
+ if (ep->desc.bEndpointAddress & USB_DIR_IN)
+ intr_in_ep = ep;
+ break;
+
+ case USB_ENDPOINT_XFER_BULK:
+ if (ep->desc.bEndpointAddress & USB_DIR_IN)
+ bulk_in_ep = ep;
+ else
+ bulk_out_ep = ep;
+ break;
+ }
+ }
+
+ if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {
+ BT_DBG("Bulk endpoints not found");
+ goto done;
+ }
+
+ if (!(husb = kzalloc(sizeof(struct hci_usb), GFP_KERNEL))) {
+ BT_ERR("Can't allocate: control structure");
+ goto done;
+ }
+
+ husb->udev = udev;
+ husb->bulk_out_ep = bulk_out_ep;
+ husb->bulk_in_ep = bulk_in_ep;
+ husb->intr_in_ep = intr_in_ep;
+
+ if (id->driver_info & HCI_DIGIANSWER)
+ husb->ctrl_req = USB_TYPE_VENDOR;
+ else
+ husb->ctrl_req = USB_TYPE_CLASS;
+
+ /* Find isochronous endpoints that we can use */
+ size = 0;
+ isoc_iface = NULL;
+ isoc_alts = 0;
+ isoc_ifnum = 1;
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+ if (isoc && !(id->driver_info & (HCI_BROKEN_ISOC | HCI_SNIFFER)))
+ isoc_iface = usb_ifnum_to_if(udev, isoc_ifnum);
+
+ if (isoc_iface) {
+ int a;
+ struct usb_host_endpoint *isoc_out_ep = NULL;
+ struct usb_host_endpoint *isoc_in_ep = NULL;
+
+ for (a = 0; a < isoc_iface->num_altsetting; a++) {
+ uif = &isoc_iface->altsetting[a];
+ for (e = 0; e < uif->desc.bNumEndpoints; e++) {
+ ep = &uif->endpoint[e];
+
+ switch (ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_ISOC:
+ if (le16_to_cpu(ep->desc.wMaxPacketSize) < size ||
+ uif->desc.bAlternateSetting != isoc)
+ break;
+ size = le16_to_cpu(ep->desc.wMaxPacketSize);
+
+ isoc_alts = uif->desc.bAlternateSetting;
+
+ if (ep->desc.bEndpointAddress & USB_DIR_IN)
+ isoc_in_ep = ep;
+ else
+ isoc_out_ep = ep;
+ break;
+ }
+ }
+ }
+
+ if (!isoc_in_ep || !isoc_out_ep)
+ BT_DBG("Isoc endpoints not found");
+ else {
+ BT_DBG("isoc ifnum %d alts %d", isoc_ifnum, isoc_alts);
+ if (usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb) != 0)
+ BT_ERR("Can't claim isoc interface");
+ else if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
+ BT_ERR("Can't set isoc interface settings");
+ husb->isoc_iface = isoc_iface;
+ usb_driver_release_interface(&hci_usb_driver, isoc_iface);
+ husb->isoc_iface = NULL;
+ } else {
+ husb->isoc_iface = isoc_iface;
+ husb->isoc_in_ep = isoc_in_ep;
+ husb->isoc_out_ep = isoc_out_ep;
+ }
+ }
+ }
+#endif
+
+ rwlock_init(&husb->completion_lock);
+
+ for (i = 0; i < 4; i++) {
+ skb_queue_head_init(&husb->transmit_q[i]);
+ _urb_queue_init(&husb->pending_q[i]);
+ _urb_queue_init(&husb->completed_q[i]);
+ }
+
+ /* Initialize and register HCI device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ goto probe_error;
+ }
+
+ husb->hdev = hdev;
+
+ hdev->type = HCI_USB;
+ hdev->driver_data = husb;
+ SET_HCIDEV_DEV(hdev, &intf->dev);
+
+ hdev->open = hci_usb_open;
+ hdev->close = hci_usb_close;
+ hdev->flush = hci_usb_flush;
+ hdev->send = hci_usb_send_frame;
+ hdev->destruct = hci_usb_destruct;
+ hdev->notify = hci_usb_notify;
+
+ hdev->owner = THIS_MODULE;
+
+ if (reset || id->driver_info & HCI_RESET)
+ set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks);
+
+ if (force_scofix || id->driver_info & HCI_WRONG_SCO_MTU) {
+ if (!disable_scofix)
+ set_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks);
+ }
+
+ if (id->driver_info & HCI_SNIFFER) {
+ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997)
+ set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
+ }
+
+ if (id->driver_info & HCI_BCM92035) {
+ unsigned char cmd[] = { 0x3b, 0xfc, 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
+ if (skb) {
+ memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
+ skb_queue_tail(&hdev->driver_init, skb);
+ }
+ }
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ goto probe_error;
+ }
+
+ usb_set_intfdata(intf, husb);
+ return 0;
+
+probe_error:
+ if (husb->isoc_iface)
+ usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
+ kfree(husb);
+
+done:
+ return -EIO;
+}
+
+static void hci_usb_disconnect(struct usb_interface *intf)
+{
+ struct hci_usb *husb = usb_get_intfdata(intf);
+ struct hci_dev *hdev;
+
+ if (!husb || intf == husb->isoc_iface)
+ return;
+
+ usb_set_intfdata(intf, NULL);
+ hdev = husb->hdev;
+
+ BT_DBG("%s", hdev->name);
+
+ hci_usb_close(hdev);
+
+ if (husb->isoc_iface)
+ usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
+
+ if (hci_unregister_dev(hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+
+ hci_free_dev(hdev);
+}
+
+static int hci_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct hci_usb *husb = usb_get_intfdata(intf);
+ struct list_head killed;
+ unsigned long flags;
+ int i;
+
+ if (!husb || intf == husb->isoc_iface)
+ return 0;
+
+ hci_suspend_dev(husb->hdev);
+
+ INIT_LIST_HEAD(&killed);
+
+ for (i = 0; i < 4; i++) {
+ struct _urb_queue *q = &husb->pending_q[i];
+ struct _urb *_urb, *_tmp;
+
+ while ((_urb = _urb_dequeue(q))) {
+ /* reset queue since _urb_dequeue sets it to NULL */
+ _urb->queue = q;
+ usb_kill_urb(&_urb->urb);
+ list_add(&_urb->list, &killed);
+ }
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ list_for_each_entry_safe(_urb, _tmp, &killed, list) {
+ list_move_tail(&_urb->list, &q->head);
+ }
+
+ spin_unlock_irqrestore(&q->lock, flags);
+ }
+
+ return 0;
+}
+
+static int hci_usb_resume(struct usb_interface *intf)
+{
+ struct hci_usb *husb = usb_get_intfdata(intf);
+ unsigned long flags;
+ int i, err = 0;
+
+ if (!husb || intf == husb->isoc_iface)
+ return 0;
+
+ for (i = 0; i < 4; i++) {
+ struct _urb_queue *q = &husb->pending_q[i];
+ struct _urb *_urb;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ list_for_each_entry(_urb, &q->head, list) {
+ err = usb_submit_urb(&_urb->urb, GFP_ATOMIC);
+ if (err)
+ break;
+ }
+
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ if (err)
+ return -EIO;
+ }
+
+ hci_resume_dev(husb->hdev);
+
+ return 0;
+}
+
+static struct usb_driver hci_usb_driver = {
+ .name = "hci_usb",
+ .probe = hci_usb_probe,
+ .disconnect = hci_usb_disconnect,
+ .suspend = hci_usb_suspend,
+ .resume = hci_usb_resume,
+ .id_table = bluetooth_ids,
+};
+
+static int __init hci_usb_init(void)
+{
+ int err;
+
+ BT_INFO("HCI USB driver ver %s", VERSION);
+
+ if ((err = usb_register(&hci_usb_driver)) < 0)
+ BT_ERR("Failed to register HCI USB driver");
+
+ return err;
+}
+
+static void __exit hci_usb_exit(void)
+{
+ usb_deregister(&hci_usb_driver);
+}
+
+module_init(hci_usb_init);
+module_exit(hci_usb_exit);
+
+module_param(ignore_dga, bool, 0644);
+MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001");
+
+module_param(ignore_csr, bool, 0644);
+MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001");
+
+module_param(ignore_sniffer, bool, 0644);
+MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002");
+
+module_param(disable_scofix, bool, 0644);
+MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size");
+
+module_param(force_scofix, bool, 0644);
+MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
+
+module_param(reset, bool, 0644);
+MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
+
+#ifdef CONFIG_BT_HCIUSB_SCO
+module_param(isoc, int, 0644);
+MODULE_PARM_DESC(isoc, "Set isochronous transfers for SCO over HCI support");
+#endif
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth HCI USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h
new file mode 100644
index 0000000..8e65991
--- /dev/null
+++ b/drivers/bluetooth/hci_usb.h
@@ -0,0 +1,129 @@
+/*
+ HCI USB driver for Linux Bluetooth protocol stack (BlueZ)
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+ CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
+ COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
+ SOFTWARE IS DISCLAIMED.
+*/
+
+/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
+#define HCI_DEV_CLASS 0xe0 /* Wireless class */
+#define HCI_DEV_SUBCLASS 0x01 /* RF subclass */
+#define HCI_DEV_PROTOCOL 0x01 /* Bluetooth programming protocol */
+
+#define HCI_IGNORE 0x01
+#define HCI_RESET 0x02
+#define HCI_DIGIANSWER 0x04
+#define HCI_CSR 0x08
+#define HCI_SNIFFER 0x10
+#define HCI_BCM92035 0x20
+#define HCI_BROKEN_ISOC 0x40
+#define HCI_WRONG_SCO_MTU 0x80
+
+#define HCI_MAX_IFACE_NUM 3
+
+#define HCI_MAX_BULK_TX 4
+#define HCI_MAX_BULK_RX 1
+
+#define HCI_MAX_ISOC_RX 2
+#define HCI_MAX_ISOC_TX 2
+
+#define HCI_MAX_ISOC_FRAMES 10
+
+struct _urb_queue {
+ struct list_head head;
+ spinlock_t lock;
+};
+
+struct _urb {
+ struct list_head list;
+ struct _urb_queue *queue;
+ int type;
+ void *priv;
+ struct urb urb;
+};
+
+static inline void _urb_queue_init(struct _urb_queue *q)
+{
+ INIT_LIST_HEAD(&q->head);
+ spin_lock_init(&q->lock);
+}
+
+static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&q->lock, flags);
+ /* _urb_unlink needs to know which spinlock to use, thus smp_mb(). */
+ _urb->queue = q; smp_mb(); list_add(&_urb->list, &q->head);
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&q->lock, flags);
+ /* _urb_unlink needs to know which spinlock to use, thus smp_mb(). */
+ _urb->queue = q; smp_mb(); list_add_tail(&_urb->list, &q->head);
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static inline void _urb_unlink(struct _urb *_urb)
+{
+ struct _urb_queue *q;
+ unsigned long flags;
+
+ smp_mb();
+ q = _urb->queue;
+ /* If q is NULL, it will die at easy-to-debug NULL pointer dereference.
+ No need to BUG(). */
+ spin_lock_irqsave(&q->lock, flags);
+ list_del(&_urb->list); _urb->queue = NULL;
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+struct hci_usb {
+ struct hci_dev *hdev;
+
+ unsigned long state;
+
+ struct usb_device *udev;
+
+ struct usb_host_endpoint *bulk_in_ep;
+ struct usb_host_endpoint *bulk_out_ep;
+ struct usb_host_endpoint *intr_in_ep;
+
+ struct usb_interface *isoc_iface;
+ struct usb_host_endpoint *isoc_out_ep;
+ struct usb_host_endpoint *isoc_in_ep;
+
+ __u8 ctrl_req;
+
+ struct sk_buff_head transmit_q[4];
+
+ rwlock_t completion_lock;
+
+ atomic_t pending_tx[4]; /* Number of pending requests */
+ struct _urb_queue pending_q[4]; /* Pending requests */
+ struct _urb_queue completed_q[4]; /* Completed requests */
+};
+
+/* States */
+#define HCI_USB_TX_PROCESS 1
+#define HCI_USB_TX_WAKEUP 2
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
new file mode 100644
index 0000000..7320a71
--- /dev/null
+++ b/drivers/bluetooth/hci_vhci.c
@@ -0,0 +1,383 @@
+/*
+ *
+ * Bluetooth virtual HCI driver
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+
+#include <linux/skbuff.h>
+#include <linux/miscdevice.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef CONFIG_BT_HCIVHCI_DEBUG
+#undef BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.2"
+
+static int minor = MISC_DYNAMIC_MINOR;
+
+struct vhci_data {
+ struct hci_dev *hdev;
+
+ unsigned long flags;
+
+ wait_queue_head_t read_wait;
+ struct sk_buff_head readq;
+
+ struct fasync_struct *fasync;
+};
+
+#define VHCI_FASYNC 0x0010
+
+static struct miscdevice vhci_miscdev;
+
+static int vhci_open_dev(struct hci_dev *hdev)
+{
+ set_bit(HCI_RUNNING, &hdev->flags);
+
+ return 0;
+}
+
+static int vhci_close_dev(struct hci_dev *hdev)
+{
+ struct vhci_data *data = hdev->driver_data;
+
+ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+ return 0;
+
+ skb_queue_purge(&data->readq);
+
+ return 0;
+}
+
+static int vhci_flush(struct hci_dev *hdev)
+{
+ struct vhci_data *data = hdev->driver_data;
+
+ skb_queue_purge(&data->readq);
+
+ return 0;
+}
+
+static int vhci_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+ struct vhci_data *data;
+
+ if (!hdev) {
+ BT_ERR("Frame for unknown HCI device (hdev=NULL)");
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return -EBUSY;
+
+ data = hdev->driver_data;
+
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ skb_queue_tail(&data->readq, skb);
+
+ if (data->flags & VHCI_FASYNC)
+ kill_fasync(&data->fasync, SIGIO, POLL_IN);
+
+ wake_up_interruptible(&data->read_wait);
+
+ return 0;
+}
+
+static void vhci_destruct(struct hci_dev *hdev)
+{
+ kfree(hdev->driver_data);
+}
+
+static inline ssize_t vhci_get_user(struct vhci_data *data,
+ const char __user *buf, size_t count)
+{
+ struct sk_buff *skb;
+
+ if (count > HCI_MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ skb = bt_skb_alloc(count, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ if (copy_from_user(skb_put(skb, count), buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+
+ skb->dev = (void *) data->hdev;
+ bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
+ skb_pull(skb, 1);
+
+ hci_recv_frame(skb);
+
+ return count;
+}
+
+static inline ssize_t vhci_put_user(struct vhci_data *data,
+ struct sk_buff *skb, char __user *buf, int count)
+{
+ char __user *ptr = buf;
+ int len, total = 0;
+
+ len = min_t(unsigned int, skb->len, count);
+
+ if (copy_to_user(ptr, skb->data, len))
+ return -EFAULT;
+
+ total += len;
+
+ data->hdev->stat.byte_tx += len;
+
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ data->hdev->stat.cmd_tx++;
+ break;
+
+ case HCI_ACLDATA_PKT:
+ data->hdev->stat.acl_tx++;
+ break;
+
+ case HCI_SCODATA_PKT:
+ data->hdev->stat.cmd_tx++;
+ break;
+ };
+
+ return total;
+}
+
+static ssize_t vhci_read(struct file *file,
+ char __user *buf, size_t count, loff_t *pos)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct vhci_data *data = file->private_data;
+ struct sk_buff *skb;
+ ssize_t ret = 0;
+
+ add_wait_queue(&data->read_wait, &wait);
+ while (count) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ skb = skb_dequeue(&data->readq);
+ if (!skb) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ schedule();
+ continue;
+ }
+
+ if (access_ok(VERIFY_WRITE, buf, count))
+ ret = vhci_put_user(data, skb, buf, count);
+ else
+ ret = -EFAULT;
+
+ kfree_skb(skb);
+ break;
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&data->read_wait, &wait);
+
+ return ret;
+}
+
+static ssize_t vhci_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *pos)
+{
+ struct vhci_data *data = file->private_data;
+
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ return vhci_get_user(data, buf, count);
+}
+
+static unsigned int vhci_poll(struct file *file, poll_table *wait)
+{
+ struct vhci_data *data = file->private_data;
+
+ poll_wait(file, &data->read_wait, wait);
+
+ if (!skb_queue_empty(&data->readq))
+ return POLLIN | POLLRDNORM;
+
+ return POLLOUT | POLLWRNORM;
+}
+
+static int vhci_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return -EINVAL;
+}
+
+static int vhci_open(struct inode *inode, struct file *file)
+{
+ struct vhci_data *data;
+ struct hci_dev *hdev;
+
+ data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ skb_queue_head_init(&data->readq);
+ init_waitqueue_head(&data->read_wait);
+
+ lock_kernel();
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ kfree(data);
+ unlock_kernel();
+ return -ENOMEM;
+ }
+
+ data->hdev = hdev;
+
+ hdev->type = HCI_VIRTUAL;
+ hdev->driver_data = data;
+
+ hdev->open = vhci_open_dev;
+ hdev->close = vhci_close_dev;
+ hdev->flush = vhci_flush;
+ hdev->send = vhci_send_frame;
+ hdev->destruct = vhci_destruct;
+
+ hdev->owner = THIS_MODULE;
+
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ kfree(data);
+ hci_free_dev(hdev);
+ unlock_kernel();
+ return -EBUSY;
+ }
+
+ file->private_data = data;
+ unlock_kernel();
+
+ return nonseekable_open(inode, file);
+}
+
+static int vhci_release(struct inode *inode, struct file *file)
+{
+ struct vhci_data *data = file->private_data;
+ struct hci_dev *hdev = data->hdev;
+
+ if (hci_unregister_dev(hdev) < 0) {
+ BT_ERR("Can't unregister HCI device %s", hdev->name);
+ }
+
+ hci_free_dev(hdev);
+
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static int vhci_fasync(int fd, struct file *file, int on)
+{
+ struct vhci_data *data = file->private_data;
+ int err = 0;
+
+ lock_kernel();
+ err = fasync_helper(fd, file, on, &data->fasync);
+ if (err < 0)
+ goto out;
+
+ if (on)
+ data->flags |= VHCI_FASYNC;
+ else
+ data->flags &= ~VHCI_FASYNC;
+
+out:
+ unlock_kernel();
+ return err;
+}
+
+static const struct file_operations vhci_fops = {
+ .owner = THIS_MODULE,
+ .read = vhci_read,
+ .write = vhci_write,
+ .poll = vhci_poll,
+ .ioctl = vhci_ioctl,
+ .open = vhci_open,
+ .release = vhci_release,
+ .fasync = vhci_fasync,
+};
+
+static struct miscdevice vhci_miscdev= {
+ .name = "vhci",
+ .fops = &vhci_fops,
+};
+
+static int __init vhci_init(void)
+{
+ BT_INFO("Virtual HCI driver ver %s", VERSION);
+
+ vhci_miscdev.minor = minor;
+
+ if (misc_register(&vhci_miscdev) < 0) {
+ BT_ERR("Can't register misc device with minor %d", minor);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void __exit vhci_exit(void)
+{
+ if (misc_deregister(&vhci_miscdev) < 0)
+ BT_ERR("Can't unregister misc device with minor %d", minor);
+}
+
+module_init(vhci_init);
+module_exit(vhci_exit);
+
+module_param(minor, int, 0444);
+MODULE_PARM_DESC(minor, "Miscellaneous minor device number");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
OpenPOWER on IntegriCloud