brintos

brintos / linux-shallow public Read only

0
0
Text · 6.0 KiB · c738ad0 Raw
267 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *4 *  Broadcom Blutonium firmware driver5 *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		037#define BCM203X_RESET		138#define BCM203X_LOAD_MINIDRV	239#define BCM203X_SELECT_MEMORY	340#define BCM203X_CHECK_MEMORY	441#define BCM203X_LOAD_FIRMWARE	542#define BCM203X_CHECK_FIRMWARE	643 44#define BCM203X_IN_EP		0x8145#define BCM203X_OUT_EP		0x0246 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");267