From e89502bf9d0ac11d3b1fe982375316e63740f022 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 28 Dec 2003 17:08:07 +0100 Subject: [PATCH] [Bluetooth] Support for Broadcom Blutonium This adds a driver for the the Broadcom Blutonium USB devices. The devices with Broadcom chips need a firmware loading sequence before they can work like any other Bluetooth H:2 USB device. --- MAINTAINERS | 6 + drivers/bluetooth/Kconfig | 14 +- drivers/bluetooth/Makefile | 1 + drivers/bluetooth/bcm203x.c | 309 ++++++++++++++++++++++++++++++++++++ 4 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 drivers/bluetooth/bcm203x.c diff --git a/MAINTAINERS b/MAINTAINERS index 9cd2cf4e19264..f0d74166942cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -373,6 +373,12 @@ M: maxk@qualcomm.com W: http://bluez.sf.net S: Maintained +BLUETOOTH HCI BCM203X DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org +W: http://www.holtmann.org/linux/bluetooth/ +S: Maintained + BLUETOOTH HCI BFUSB DRIVER P: Marcel Holtmann M: marcel@holtmann.org diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index a4ad9599aa054..5ad7ea2b6bc6f 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -64,6 +64,18 @@ config BT_HCIUART_BCSP_TXCRC every BCSP (BlueCore Serial Protocol) packet sent to the Bluetooth chip. This increases reliability, but slightly reduces efficiency. +config BT_HCIBCM203X + tristate "HCI BCM203x USB driver" + depends on USB && BT + 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_HCIBFUSB tristate "HCI BlueFRITZ! USB driver" depends on USB && BT @@ -75,7 +87,7 @@ config BT_HCIBFUSB 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.o). + kernel or say M to compile it as module (bfusb). config BT_HCIDTL1 tristate "HCI DTL1 (PC Card) driver" diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index f6d3d4a9afe36..ec5345fac3a3d 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -5,6 +5,7 @@ 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_HCIBFUSB) += bfusb.o obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c new file mode 100644 index 0000000000000..12e44029a38a9 --- /dev/null +++ b/drivers/bluetooth/bcm203x.c @@ -0,0 +1,309 @@ +/* + * + * Broadcom Blutonium firmware driver + * + * Copyright (C) 2003 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#ifndef CONFIG_BT_HCIBCM203X_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "1.0" + +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 timer_list timer; + + struct urb *urb; + unsigned char buffer[4096]; + + unsigned char *fw_data; + unsigned int fw_size; + unsigned int fw_sent; +}; + +static void bcm203x_complete(struct urb *urb, struct pt_regs *regs) +{ + 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; + + mod_timer(&data->timer, jiffies + (HZ / 10)); + 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, + sizeof(data->buffer)); + + 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_timer(unsigned long user_data) +{ + struct bcm203x_data *data = (struct bcm203x_data *) user_data; + + 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; + + BT_DBG("intf %p id %p", intf, id); + + if (intf->altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + BT_ERR("Can't allocate memory for data structure"); + return -ENOMEM; + } + + memset(data, 0, sizeof(*data)); + + 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); + + if (firmware->size > sizeof(data->buffer)) { + BT_ERR("Mini driver exceeds size of buffer"); + release_firmware(firmware); + usb_free_urb(data->urb); + kfree(data); + return -EIO; + } + + 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); + 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"); + usb_free_urb(data->urb); + 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_timer(&data->timer); + data->timer.function = bcm203x_timer; + data->timer.data = (unsigned long) data; + + usb_set_intfdata(intf, data); + + mod_timer(&data->timer, jiffies + HZ); + + return 0; +} + +static void bcm203x_disconnect(struct usb_interface *intf) +{ + struct bcm203x_data *data = usb_get_intfdata(intf); + + BT_DBG("intf %p", intf); + + usb_unlink_urb(data->urb); + + usb_set_intfdata(intf, NULL); + + usb_free_urb(data->urb); + kfree(data->fw_data); + kfree(data); +} + +static struct usb_driver bcm203x_driver = { + .owner = THIS_MODULE, + .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_cleanup(void) +{ + usb_deregister(&bcm203x_driver); +} + +module_init(bcm203x_init); +module_exit(bcm203x_cleanup); + +MODULE_AUTHOR("Marcel Holtmann "); +MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION); +MODULE_LICENSE("GPL"); -- 2.53.0