ubtbcmfw.c revision 187259
1/*
2 * ubtbcmfw.c
3 */
4
5/*-
6 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
31 * $FreeBSD: head/sys/dev/usb2/bluetooth/ubtbcmfw2.c 187259 2009-01-15 02:35:40Z thompsa $
32 */
33
34#include <dev/usb2/include/usb2_devid.h>
35#include <dev/usb2/include/usb2_standard.h>
36#include <dev/usb2/include/usb2_mfunc.h>
37#include <dev/usb2/include/usb2_error.h>
38#include <dev/usb2/include/usb2_ioctl.h>
39
40#define	USB_DEBUG_VAR usb2_debug
41
42#include <dev/usb2/core/usb2_core.h>
43#include <dev/usb2/core/usb2_debug.h>
44#include <dev/usb2/core/usb2_parse.h>
45#include <dev/usb2/core/usb2_lookup.h>
46#include <dev/usb2/core/usb2_util.h>
47#include <dev/usb2/core/usb2_busdma.h>
48#include <dev/usb2/core/usb2_mbuf.h>
49#include <dev/usb2/core/usb2_dev.h>
50
51/*
52 * Download firmware to BCM2033.
53 */
54
55#define	UBTBCMFW_CONFIG_NO	1	/* Config number */
56#define	UBTBCMFW_IFACE_IDX	0	/* Control interface */
57#define	UBTBCMFW_T_MAX		4	/* units */
58
59enum {
60	UBTBCMFW_BULK_DT_WR,
61	UBTBCMFW_BULK_DT_RD,
62	UBTBCMFW_BULK_CS_WR,
63	UBTBCMFW_BULK_CS_RD,
64	UBTBCMFW_N_TRANSFER = 4,
65};
66
67struct ubtbcmfw_softc {
68	struct usb2_fifo_sc sc_fifo;
69	struct mtx sc_mtx;
70
71	device_t sc_dev;
72	struct usb2_device *sc_udev;
73	struct usb2_xfer *sc_xfer[UBTBCMFW_N_TRANSFER];
74
75	uint8_t	sc_flags;
76#define	UBTBCMFW_FLAG_WRITE_STALL 0x01
77#define	UBTBCMFW_FLAG_READ_STALL  0x02
78};
79
80#define	UBTBCMFW_BSIZE		1024
81#define	UBTBCMFW_IFQ_MAXLEN	2
82
83/* prototypes */
84
85static device_probe_t ubtbcmfw_probe;
86static device_attach_t ubtbcmfw_attach;
87static device_detach_t ubtbcmfw_detach;
88
89static usb2_callback_t ubtbcmfw_write_callback;
90static usb2_callback_t ubtbcmfw_write_clear_stall_callback;
91static usb2_callback_t ubtbcmfw_read_callback;
92static usb2_callback_t ubtbcmfw_read_clear_stall_callback;
93
94static usb2_fifo_close_t ubtbcmfw_close;
95static usb2_fifo_cmd_t ubtbcmfw_start_read;
96static usb2_fifo_cmd_t ubtbcmfw_start_write;
97static usb2_fifo_cmd_t ubtbcmfw_stop_read;
98static usb2_fifo_cmd_t ubtbcmfw_stop_write;
99static usb2_fifo_ioctl_t ubtbcmfw_ioctl;
100static usb2_fifo_open_t ubtbcmfw_open;
101
102static struct usb2_fifo_methods ubtbcmfw_fifo_methods = {
103	.f_close = &ubtbcmfw_close,
104	.f_ioctl = &ubtbcmfw_ioctl,
105	.f_open = &ubtbcmfw_open,
106	.f_start_read = &ubtbcmfw_start_read,
107	.f_start_write = &ubtbcmfw_start_write,
108	.f_stop_read = &ubtbcmfw_stop_read,
109	.f_stop_write = &ubtbcmfw_stop_write,
110	.basename[0] = "ubtbcmfw",
111	.basename[1] = "ubtbcmfw",
112	.basename[2] = "ubtbcmfw",
113	.postfix[0] = "",
114	.postfix[1] = ".1",
115	.postfix[2] = ".2",
116};
117
118static const struct usb2_config ubtbcmfw_config[UBTBCMFW_T_MAX] = {
119
120	[UBTBCMFW_BULK_DT_WR] = {
121		.type = UE_BULK,
122		.endpoint = 0x02,	/* fixed */
123		.direction = UE_DIR_OUT,
124		.mh.bufsize = UBTBCMFW_BSIZE,
125		.mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,},
126		.mh.callback = &ubtbcmfw_write_callback,
127	},
128
129	[UBTBCMFW_BULK_DT_RD] = {
130		.type = UE_INTERRUPT,
131		.endpoint = 0x01,	/* fixed */
132		.direction = UE_DIR_IN,
133		.mh.bufsize = UBTBCMFW_BSIZE,
134		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,},
135		.mh.callback = &ubtbcmfw_read_callback,
136	},
137
138	[UBTBCMFW_BULK_CS_WR] = {
139		.type = UE_CONTROL,
140		.endpoint = 0x00,	/* Control pipe */
141		.direction = UE_DIR_ANY,
142		.mh.bufsize = sizeof(struct usb2_device_request),
143		.mh.flags = {},
144		.mh.callback = &ubtbcmfw_write_clear_stall_callback,
145		.mh.timeout = 1000,	/* 1 second */
146		.mh.interval = 50,	/* 50ms */
147	},
148
149	[UBTBCMFW_BULK_CS_RD] = {
150		.type = UE_CONTROL,
151		.endpoint = 0x00,	/* Control pipe */
152		.direction = UE_DIR_ANY,
153		.mh.bufsize = sizeof(struct usb2_device_request),
154		.mh.flags = {},
155		.mh.callback = &ubtbcmfw_read_clear_stall_callback,
156		.mh.timeout = 1000,	/* 1 second */
157		.mh.interval = 50,	/* 50ms */
158	},
159};
160
161/*
162 * Module
163 */
164
165static devclass_t ubtbcmfw_devclass;
166
167static device_method_t ubtbcmfw_methods[] = {
168	DEVMETHOD(device_probe, ubtbcmfw_probe),
169	DEVMETHOD(device_attach, ubtbcmfw_attach),
170	DEVMETHOD(device_detach, ubtbcmfw_detach),
171	{0, 0}
172};
173
174static driver_t ubtbcmfw_driver = {
175	.name = "ubtbcmfw",
176	.methods = ubtbcmfw_methods,
177	.size = sizeof(struct ubtbcmfw_softc),
178};
179
180DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
181MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1);
182MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1);
183
184/*
185 * Probe for a USB Bluetooth device
186 */
187
188static int
189ubtbcmfw_probe(device_t dev)
190{
191	struct usb2_attach_arg *uaa = device_get_ivars(dev);
192
193	if (uaa->usb2_mode != USB_MODE_HOST) {
194		return (ENXIO);
195	}
196	if (uaa->info.bIfaceIndex != 0)
197		return (ENXIO);
198
199	/* Match the boot device. */
200	if (uaa->info.idVendor == USB_VENDOR_BROADCOM &&
201	    uaa->info.idProduct == USB_PRODUCT_BROADCOM_BCM2033)
202		return (0);
203
204	return (ENXIO);
205}
206
207/*
208 * Attach the device
209 */
210
211static int
212ubtbcmfw_attach(device_t dev)
213{
214	struct usb2_attach_arg *uaa = device_get_ivars(dev);
215	struct ubtbcmfw_softc *sc = device_get_softc(dev);
216	int32_t err;
217	uint8_t iface_index;
218
219	if (sc == NULL) {
220		return (ENOMEM);
221	}
222	sc->sc_dev = dev;
223	sc->sc_udev = uaa->device;
224
225	device_set_usb2_desc(dev);
226
227	mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
228
229	iface_index = UBTBCMFW_IFACE_IDX;
230	err = usb2_transfer_setup(uaa->device,
231	    &iface_index, sc->sc_xfer, ubtbcmfw_config,
232	    UBTBCMFW_T_MAX, sc, &sc->sc_mtx);
233	if (err) {
234		device_printf(dev, "allocating USB transfers "
235		    "failed, err=%s\n", usb2_errstr(err));
236		goto detach;
237	}
238	/* set interface permissions */
239	usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
240	    UID_ROOT, GID_OPERATOR, 0644);
241
242	err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
243	    &ubtbcmfw_fifo_methods, &sc->sc_fifo,
244	    device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex);
245	if (err) {
246		goto detach;
247	}
248	return (0);			/* success */
249
250detach:
251	ubtbcmfw_detach(dev);
252	return (ENOMEM);		/* failure */
253}
254
255/*
256 * Detach the device
257 */
258
259static int
260ubtbcmfw_detach(device_t dev)
261{
262	struct ubtbcmfw_softc *sc = device_get_softc(dev);
263
264	usb2_fifo_detach(&sc->sc_fifo);
265
266	usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_T_MAX);
267
268	mtx_destroy(&sc->sc_mtx);
269
270	return (0);
271}
272
273static void
274ubtbcmfw_write_callback(struct usb2_xfer *xfer)
275{
276	struct ubtbcmfw_softc *sc = xfer->priv_sc;
277	struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX];
278	uint32_t actlen;
279
280	switch (USB_GET_STATE(xfer)) {
281	case USB_ST_TRANSFERRED:
282	case USB_ST_SETUP:
283		if (sc->sc_flags & UBTBCMFW_FLAG_WRITE_STALL) {
284			usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_WR]);
285			return;
286		}
287		if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
288		    UBTBCMFW_BSIZE, &actlen, 0)) {
289
290			xfer->frlengths[0] = actlen;
291			usb2_start_hardware(xfer);
292		}
293		return;
294
295	default:			/* Error */
296		if (xfer->error != USB_ERR_CANCELLED) {
297			/* try to clear stall first */
298			sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL;
299			usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_WR]);
300		}
301		return;
302	}
303}
304
305static void
306ubtbcmfw_write_clear_stall_callback(struct usb2_xfer *xfer)
307{
308	struct ubtbcmfw_softc *sc = xfer->priv_sc;
309	struct usb2_xfer *xfer_other = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
310
311	if (usb2_clear_stall_callback(xfer, xfer_other)) {
312		DPRINTF("stall cleared\n");
313		sc->sc_flags &= ~UBTBCMFW_FLAG_WRITE_STALL;
314		usb2_transfer_start(xfer_other);
315	}
316}
317
318static void
319ubtbcmfw_read_callback(struct usb2_xfer *xfer)
320{
321	struct ubtbcmfw_softc *sc = xfer->priv_sc;
322	struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX];
323
324	switch (USB_GET_STATE(xfer)) {
325	case USB_ST_TRANSFERRED:
326		usb2_fifo_put_data(f, xfer->frbuffers,
327		    0, xfer->actlen, 1);
328
329	case USB_ST_SETUP:
330		if (sc->sc_flags & UBTBCMFW_FLAG_READ_STALL) {
331			usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_RD]);
332			return;
333		}
334		if (usb2_fifo_put_bytes_max(f) != 0) {
335			xfer->frlengths[0] = xfer->max_data_length;
336			usb2_start_hardware(xfer);
337		}
338		return;
339
340	default:			/* Error */
341		if (xfer->error != USB_ERR_CANCELLED) {
342			/* try to clear stall first */
343			sc->sc_flags |= UBTBCMFW_FLAG_READ_STALL;
344			usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_CS_RD]);
345		}
346		return;
347	}
348}
349
350static void
351ubtbcmfw_read_clear_stall_callback(struct usb2_xfer *xfer)
352{
353	struct ubtbcmfw_softc *sc = xfer->priv_sc;
354	struct usb2_xfer *xfer_other = sc->sc_xfer[UBTBCMFW_BULK_DT_RD];
355
356	if (usb2_clear_stall_callback(xfer, xfer_other)) {
357		DPRINTF("stall cleared\n");
358		sc->sc_flags &= ~UBTBCMFW_FLAG_READ_STALL;
359		usb2_transfer_start(xfer_other);
360	}
361}
362
363static void
364ubtbcmfw_start_read(struct usb2_fifo *fifo)
365{
366	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
367
368	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_RD]);
369}
370
371static void
372ubtbcmfw_stop_read(struct usb2_fifo *fifo)
373{
374	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
375
376	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_CS_RD]);
377	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_RD]);
378}
379
380static void
381ubtbcmfw_start_write(struct usb2_fifo *fifo)
382{
383	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
384
385	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
386}
387
388static void
389ubtbcmfw_stop_write(struct usb2_fifo *fifo)
390{
391	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
392
393	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_CS_WR]);
394	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
395}
396
397static int
398ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
399{
400	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
401
402	if (fflags & FREAD) {
403		if (usb2_fifo_alloc_buffer(fifo,
404		    sc->sc_xfer[UBTBCMFW_BULK_DT_RD]->max_data_length,
405		    UBTBCMFW_IFQ_MAXLEN)) {
406			return (ENOMEM);
407		}
408	}
409	if (fflags & FWRITE) {
410		/* clear stall first */
411		mtx_lock(&sc->sc_mtx);
412		sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL;
413		mtx_unlock(&sc->sc_mtx);
414		if (usb2_fifo_alloc_buffer(fifo,
415		    sc->sc_xfer[UBTBCMFW_BULK_DT_WR]->max_data_length,
416		    UBTBCMFW_IFQ_MAXLEN)) {
417			return (ENOMEM);
418		}
419	}
420	return (0);
421}
422
423static void
424ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
425{
426	if (fflags & (FREAD | FWRITE)) {
427		usb2_fifo_free_buffer(fifo);
428	}
429}
430
431static int
432ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
433    int fflags, struct thread *td)
434{
435	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
436	int error = 0;
437
438	switch (cmd) {
439	case USB_GET_DEVICE_DESC:
440		*(struct usb2_device_descriptor *)data =
441		    *usb2_get_device_descriptor(sc->sc_udev);
442		break;
443
444	default:
445		error = EINVAL;
446		break;
447	}
448	return (error);
449}
450