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