Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *
0004  *  Broadcom Blutonium firmware driver
0005  *
0006  *  Copyright (C) 2003  Maxim Krasnyansky <maxk@qualcomm.com>
0007  *  Copyright (C) 2003  Marcel Holtmann <marcel@holtmann.org>
0008  */
0009 
0010 #include <linux/module.h>
0011 
0012 #include <linux/kernel.h>
0013 #include <linux/init.h>
0014 #include <linux/slab.h>
0015 #include <linux/types.h>
0016 #include <linux/errno.h>
0017 
0018 #include <linux/device.h>
0019 #include <linux/firmware.h>
0020 
0021 #include <linux/usb.h>
0022 
0023 #include <net/bluetooth/bluetooth.h>
0024 
0025 #define VERSION "1.2"
0026 
0027 static const struct usb_device_id bcm203x_table[] = {
0028     /* Broadcom Blutonium (BCM2033) */
0029     { USB_DEVICE(0x0a5c, 0x2033) },
0030 
0031     { } /* Terminating entry */
0032 };
0033 
0034 MODULE_DEVICE_TABLE(usb, bcm203x_table);
0035 
0036 #define BCM203X_ERROR       0
0037 #define BCM203X_RESET       1
0038 #define BCM203X_LOAD_MINIDRV    2
0039 #define BCM203X_SELECT_MEMORY   3
0040 #define BCM203X_CHECK_MEMORY    4
0041 #define BCM203X_LOAD_FIRMWARE   5
0042 #define BCM203X_CHECK_FIRMWARE  6
0043 
0044 #define BCM203X_IN_EP       0x81
0045 #define BCM203X_OUT_EP      0x02
0046 
0047 struct bcm203x_data {
0048     struct usb_device   *udev;
0049 
0050     unsigned long       state;
0051 
0052     struct work_struct  work;
0053     atomic_t        shutdown;
0054 
0055     struct urb      *urb;
0056     unsigned char       *buffer;
0057 
0058     unsigned char       *fw_data;
0059     unsigned int        fw_size;
0060     unsigned int        fw_sent;
0061 };
0062 
0063 static void bcm203x_complete(struct urb *urb)
0064 {
0065     struct bcm203x_data *data = urb->context;
0066     struct usb_device *udev = urb->dev;
0067     int len;
0068 
0069     BT_DBG("udev %p urb %p", udev, urb);
0070 
0071     if (urb->status) {
0072         BT_ERR("URB failed with status %d", urb->status);
0073         data->state = BCM203X_ERROR;
0074         return;
0075     }
0076 
0077     switch (data->state) {
0078     case BCM203X_LOAD_MINIDRV:
0079         memcpy(data->buffer, "#", 1);
0080 
0081         usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
0082                 data->buffer, 1, bcm203x_complete, data);
0083 
0084         data->state = BCM203X_SELECT_MEMORY;
0085 
0086         /* use workqueue to have a small delay */
0087         schedule_work(&data->work);
0088         break;
0089 
0090     case BCM203X_SELECT_MEMORY:
0091         usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
0092                 data->buffer, 32, bcm203x_complete, data, 1);
0093 
0094         data->state = BCM203X_CHECK_MEMORY;
0095 
0096         if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
0097             BT_ERR("Can't submit URB");
0098         break;
0099 
0100     case BCM203X_CHECK_MEMORY:
0101         if (data->buffer[0] != '#') {
0102             BT_ERR("Memory select failed");
0103             data->state = BCM203X_ERROR;
0104             break;
0105         }
0106 
0107         data->state = BCM203X_LOAD_FIRMWARE;
0108         fallthrough;
0109     case BCM203X_LOAD_FIRMWARE:
0110         if (data->fw_sent == data->fw_size) {
0111             usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
0112                 data->buffer, 32, bcm203x_complete, data, 1);
0113 
0114             data->state = BCM203X_CHECK_FIRMWARE;
0115         } else {
0116             len = min_t(uint, data->fw_size - data->fw_sent, 4096);
0117 
0118             usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
0119                 data->fw_data + data->fw_sent, len, bcm203x_complete, data);
0120 
0121             data->fw_sent += len;
0122         }
0123 
0124         if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
0125             BT_ERR("Can't submit URB");
0126         break;
0127 
0128     case BCM203X_CHECK_FIRMWARE:
0129         if (data->buffer[0] != '.') {
0130             BT_ERR("Firmware loading failed");
0131             data->state = BCM203X_ERROR;
0132             break;
0133         }
0134 
0135         data->state = BCM203X_RESET;
0136         break;
0137     }
0138 }
0139 
0140 static void bcm203x_work(struct work_struct *work)
0141 {
0142     struct bcm203x_data *data =
0143         container_of(work, struct bcm203x_data, work);
0144 
0145     if (atomic_read(&data->shutdown))
0146         return;
0147 
0148     if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
0149         BT_ERR("Can't submit URB");
0150 }
0151 
0152 static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
0153 {
0154     const struct firmware *firmware;
0155     struct usb_device *udev = interface_to_usbdev(intf);
0156     struct bcm203x_data *data;
0157     int size;
0158 
0159     BT_DBG("intf %p id %p", intf, id);
0160 
0161     if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
0162         return -ENODEV;
0163 
0164     data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
0165     if (!data)
0166         return -ENOMEM;
0167 
0168     data->udev  = udev;
0169     data->state = BCM203X_LOAD_MINIDRV;
0170 
0171     data->urb = usb_alloc_urb(0, GFP_KERNEL);
0172     if (!data->urb)
0173         return -ENOMEM;
0174 
0175     if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
0176         BT_ERR("Mini driver request failed");
0177         usb_free_urb(data->urb);
0178         return -EIO;
0179     }
0180 
0181     BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
0182 
0183     size = max_t(uint, firmware->size, 4096);
0184 
0185     data->buffer = kmalloc(size, GFP_KERNEL);
0186     if (!data->buffer) {
0187         BT_ERR("Can't allocate memory for mini driver");
0188         release_firmware(firmware);
0189         usb_free_urb(data->urb);
0190         return -ENOMEM;
0191     }
0192 
0193     memcpy(data->buffer, firmware->data, firmware->size);
0194 
0195     usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
0196             data->buffer, firmware->size, bcm203x_complete, data);
0197 
0198     release_firmware(firmware);
0199 
0200     if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
0201         BT_ERR("Firmware request failed");
0202         usb_free_urb(data->urb);
0203         kfree(data->buffer);
0204         return -EIO;
0205     }
0206 
0207     BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
0208 
0209     data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
0210     if (!data->fw_data) {
0211         BT_ERR("Can't allocate memory for firmware image");
0212         release_firmware(firmware);
0213         usb_free_urb(data->urb);
0214         kfree(data->buffer);
0215         return -ENOMEM;
0216     }
0217 
0218     data->fw_size = firmware->size;
0219     data->fw_sent = 0;
0220 
0221     release_firmware(firmware);
0222 
0223     INIT_WORK(&data->work, bcm203x_work);
0224 
0225     usb_set_intfdata(intf, data);
0226 
0227     /* use workqueue to have a small delay */
0228     schedule_work(&data->work);
0229 
0230     return 0;
0231 }
0232 
0233 static void bcm203x_disconnect(struct usb_interface *intf)
0234 {
0235     struct bcm203x_data *data = usb_get_intfdata(intf);
0236 
0237     BT_DBG("intf %p", intf);
0238 
0239     atomic_inc(&data->shutdown);
0240     cancel_work_sync(&data->work);
0241 
0242     usb_kill_urb(data->urb);
0243 
0244     usb_set_intfdata(intf, NULL);
0245 
0246     usb_free_urb(data->urb);
0247     kfree(data->fw_data);
0248     kfree(data->buffer);
0249 }
0250 
0251 static struct usb_driver bcm203x_driver = {
0252     .name       = "bcm203x",
0253     .probe      = bcm203x_probe,
0254     .disconnect = bcm203x_disconnect,
0255     .id_table   = bcm203x_table,
0256     .disable_hub_initiated_lpm = 1,
0257 };
0258 
0259 module_usb_driver(bcm203x_driver);
0260 
0261 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
0262 MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
0263 MODULE_VERSION(VERSION);
0264 MODULE_LICENSE("GPL");
0265 MODULE_FIRMWARE("BCM2033-MD.hex");
0266 MODULE_FIRMWARE("BCM2033-FW.bin");