xref: /openbmc/linux/drivers/bluetooth/bcm203x.c (revision be58f710)
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 
28 static const struct usb_device_id bcm203x_table[] = {
29 	/* Broadcom Blutonium (BCM2033) */
30 	{ USB_DEVICE(0x0a5c, 0x2033) },
31 
32 	{ }	/* Terminating entry */
33 };
34 
35 MODULE_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 
48 struct 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 
64 static 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 
141 static 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 
153 static 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 
234 static 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 
252 static 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 
260 module_usb_driver(bcm203x_driver);
261 
262 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
263 MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
264 MODULE_VERSION(VERSION);
265 MODULE_LICENSE("GPL");
266 MODULE_FIRMWARE("BCM2033-MD.hex");
267 MODULE_FIRMWARE("BCM2033-FW.bin");
268