linux/drivers/bluetooth/bcm203x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *
   4 *  Broadcom Blutonium firmware driver
   5 *
   6 *  Copyright (C) 2003  Maxim Krasnyansky <maxk@qualcomm.com>
   7 *  Copyright (C) 2003  Marcel Holtmann <marcel@holtmann.org>
   8 */
   9
  10#include <linux/module.h>
  11
  12#include <linux/atomic.h>
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/slab.h>
  16#include <linux/types.h>
  17#include <linux/errno.h>
  18
  19#include <linux/device.h>
  20#include <linux/firmware.h>
  21
  22#include <linux/usb.h>
  23
  24#include <net/bluetooth/bluetooth.h>
  25
  26#define VERSION "1.2"
  27
  28static const struct usb_device_id bcm203x_table[] = {
  29        /* Broadcom Blutonium (BCM2033) */
  30        { USB_DEVICE(0x0a5c, 0x2033) },
  31
  32        { }     /* Terminating entry */
  33};
  34
  35MODULE_DEVICE_TABLE(usb, bcm203x_table);
  36
  37#define BCM203X_ERROR           0
  38#define BCM203X_RESET           1
  39#define BCM203X_LOAD_MINIDRV    2
  40#define BCM203X_SELECT_MEMORY   3
  41#define BCM203X_CHECK_MEMORY    4
  42#define BCM203X_LOAD_FIRMWARE   5
  43#define BCM203X_CHECK_FIRMWARE  6
  44
  45#define BCM203X_IN_EP           0x81
  46#define BCM203X_OUT_EP          0x02
  47
  48struct bcm203x_data {
  49        struct usb_device       *udev;
  50
  51        unsigned long           state;
  52
  53        struct work_struct      work;
  54        atomic_t                shutdown;
  55
  56        struct urb              *urb;
  57        unsigned char           *buffer;
  58
  59        unsigned char           *fw_data;
  60        unsigned int            fw_size;
  61        unsigned int            fw_sent;
  62};
  63
  64static void bcm203x_complete(struct urb *urb)
  65{
  66        struct bcm203x_data *data = urb->context;
  67        struct usb_device *udev = urb->dev;
  68        int len;
  69
  70        BT_DBG("udev %p urb %p", udev, urb);
  71
  72        if (urb->status) {
  73                BT_ERR("URB failed with status %d", urb->status);
  74                data->state = BCM203X_ERROR;
  75                return;
  76        }
  77
  78        switch (data->state) {
  79        case BCM203X_LOAD_MINIDRV:
  80                memcpy(data->buffer, "#", 1);
  81
  82                usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
  83                                data->buffer, 1, bcm203x_complete, data);
  84
  85                data->state = BCM203X_SELECT_MEMORY;
  86
  87                /* use workqueue to have a small delay */
  88                schedule_work(&data->work);
  89                break;
  90
  91        case BCM203X_SELECT_MEMORY:
  92                usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
  93                                data->buffer, 32, bcm203x_complete, data, 1);
  94
  95                data->state = BCM203X_CHECK_MEMORY;
  96
  97                if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
  98                        BT_ERR("Can't submit URB");
  99                break;
 100
 101        case BCM203X_CHECK_MEMORY:
 102                if (data->buffer[0] != '#') {
 103                        BT_ERR("Memory select failed");
 104                        data->state = BCM203X_ERROR;
 105                        break;
 106                }
 107
 108                data->state = BCM203X_LOAD_FIRMWARE;
 109                fallthrough;
 110        case BCM203X_LOAD_FIRMWARE:
 111                if (data->fw_sent == data->fw_size) {
 112                        usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
 113                                data->buffer, 32, bcm203x_complete, data, 1);
 114
 115                        data->state = BCM203X_CHECK_FIRMWARE;
 116                } else {
 117                        len = min_t(uint, data->fw_size - data->fw_sent, 4096);
 118
 119                        usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
 120                                data->fw_data + data->fw_sent, len, bcm203x_complete, data);
 121
 122                        data->fw_sent += len;
 123                }
 124
 125                if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
 126                        BT_ERR("Can't submit URB");
 127                break;
 128
 129        case BCM203X_CHECK_FIRMWARE:
 130                if (data->buffer[0] != '.') {
 131                        BT_ERR("Firmware loading failed");
 132                        data->state = BCM203X_ERROR;
 133                        break;
 134                }
 135
 136                data->state = BCM203X_RESET;
 137                break;
 138        }
 139}
 140
 141static void bcm203x_work(struct work_struct *work)
 142{
 143        struct bcm203x_data *data =
 144                container_of(work, struct bcm203x_data, work);
 145
 146        if (atomic_read(&data->shutdown))
 147                return;
 148
 149        if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
 150                BT_ERR("Can't submit URB");
 151}
 152
 153static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
 154{
 155        const struct firmware *firmware;
 156        struct usb_device *udev = interface_to_usbdev(intf);
 157        struct bcm203x_data *data;
 158        int size;
 159
 160        BT_DBG("intf %p id %p", intf, id);
 161
 162        if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
 163                return -ENODEV;
 164
 165        data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
 166        if (!data)
 167                return -ENOMEM;
 168
 169        data->udev  = udev;
 170        data->state = BCM203X_LOAD_MINIDRV;
 171
 172        data->urb = usb_alloc_urb(0, GFP_KERNEL);
 173        if (!data->urb)
 174                return -ENOMEM;
 175
 176        if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
 177                BT_ERR("Mini driver request failed");
 178                usb_free_urb(data->urb);
 179                return -EIO;
 180        }
 181
 182        BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
 183
 184        size = max_t(uint, firmware->size, 4096);
 185
 186        data->buffer = kmalloc(size, GFP_KERNEL);
 187        if (!data->buffer) {
 188                BT_ERR("Can't allocate memory for mini driver");
 189                release_firmware(firmware);
 190                usb_free_urb(data->urb);
 191                return -ENOMEM;
 192        }
 193
 194        memcpy(data->buffer, firmware->data, firmware->size);
 195
 196        usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
 197                        data->buffer, firmware->size, bcm203x_complete, data);
 198
 199        release_firmware(firmware);
 200
 201        if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
 202                BT_ERR("Firmware request failed");
 203                usb_free_urb(data->urb);
 204                kfree(data->buffer);
 205                return -EIO;
 206        }
 207
 208        BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
 209
 210        data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
 211        if (!data->fw_data) {
 212                BT_ERR("Can't allocate memory for firmware image");
 213                release_firmware(firmware);
 214                usb_free_urb(data->urb);
 215                kfree(data->buffer);
 216                return -ENOMEM;
 217        }
 218
 219        data->fw_size = firmware->size;
 220        data->fw_sent = 0;
 221
 222        release_firmware(firmware);
 223
 224        INIT_WORK(&data->work, bcm203x_work);
 225
 226        usb_set_intfdata(intf, data);
 227
 228        /* use workqueue to have a small delay */
 229        schedule_work(&data->work);
 230
 231        return 0;
 232}
 233
 234static void bcm203x_disconnect(struct usb_interface *intf)
 235{
 236        struct bcm203x_data *data = usb_get_intfdata(intf);
 237
 238        BT_DBG("intf %p", intf);
 239
 240        atomic_inc(&data->shutdown);
 241        cancel_work_sync(&data->work);
 242
 243        usb_kill_urb(data->urb);
 244
 245        usb_set_intfdata(intf, NULL);
 246
 247        usb_free_urb(data->urb);
 248        kfree(data->fw_data);
 249        kfree(data->buffer);
 250}
 251
 252static struct usb_driver bcm203x_driver = {
 253        .name           = "bcm203x",
 254        .probe          = bcm203x_probe,
 255        .disconnect     = bcm203x_disconnect,
 256        .id_table       = bcm203x_table,
 257        .disable_hub_initiated_lpm = 1,
 258};
 259
 260module_usb_driver(bcm203x_driver);
 261
 262MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 263MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
 264MODULE_VERSION(VERSION);
 265MODULE_LICENSE("GPL");
 266MODULE_FIRMWARE("BCM2033-MD.hex");
 267MODULE_FIRMWARE("BCM2033-FW.bin");
 268