ubtbcmfw.c revision 184610
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 184610 2008-11-04 02:31:03Z alfred $
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
59struct ubtbcmfw_softc {
60	struct usb2_fifo_sc sc_fifo;
61	struct mtx sc_mtx;
62
63	device_t sc_dev;
64	struct usb2_device *sc_udev;
65	struct usb2_xfer *sc_xfer[UBTBCMFW_T_MAX];
66
67	uint8_t	sc_flags;
68#define	UBTBCMFW_FLAG_WRITE_STALL 0x01
69#define	UBTBCMFW_FLAG_READ_STALL  0x02
70};
71
72#define	UBTBCMFW_BSIZE		1024
73#define	UBTBCMFW_IFQ_MAXLEN	2
74
75/* prototypes */
76
77static device_probe_t ubtbcmfw_probe;
78static device_attach_t ubtbcmfw_attach;
79static device_detach_t ubtbcmfw_detach;
80
81static usb2_callback_t ubtbcmfw_write_callback;
82static usb2_callback_t ubtbcmfw_write_clear_stall_callback;
83static usb2_callback_t ubtbcmfw_read_callback;
84static usb2_callback_t ubtbcmfw_read_clear_stall_callback;
85
86static usb2_fifo_close_t ubtbcmfw_close;
87static usb2_fifo_cmd_t ubtbcmfw_start_read;
88static usb2_fifo_cmd_t ubtbcmfw_start_write;
89static usb2_fifo_cmd_t ubtbcmfw_stop_read;
90static usb2_fifo_cmd_t ubtbcmfw_stop_write;
91static usb2_fifo_ioctl_t ubtbcmfw_ioctl;
92static usb2_fifo_open_t ubtbcmfw_open;
93
94static struct usb2_fifo_methods ubtbcmfw_fifo_methods = {
95	.f_close = &ubtbcmfw_close,
96	.f_ioctl = &ubtbcmfw_ioctl,
97	.f_open = &ubtbcmfw_open,
98	.f_start_read = &ubtbcmfw_start_read,
99	.f_start_write = &ubtbcmfw_start_write,
100	.f_stop_read = &ubtbcmfw_stop_read,
101	.f_stop_write = &ubtbcmfw_stop_write,
102	.basename[0] = "ubtbcmfw",
103	.basename[1] = "ubtbcmfw",
104	.basename[2] = "ubtbcmfw",
105	.postfix[0] = "",
106	.postfix[1] = ".1",
107	.postfix[2] = ".2",
108};
109
110static const struct usb2_config ubtbcmfw_config[UBTBCMFW_T_MAX] = {
111
112	[0] = {
113		.type = UE_BULK,
114		.endpoint = 0x02,	/* fixed */
115		.direction = UE_DIR_OUT,
116		.mh.bufsize = UBTBCMFW_BSIZE,
117		.mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,},
118		.mh.callback = &ubtbcmfw_write_callback,
119	},
120
121	[1] = {
122		.type = UE_INTERRUPT,
123		.endpoint = 0x01,	/* fixed */
124		.direction = UE_DIR_IN,
125		.mh.bufsize = UBTBCMFW_BSIZE,
126		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,},
127		.mh.callback = &ubtbcmfw_read_callback,
128	},
129
130	[2] = {
131		.type = UE_CONTROL,
132		.endpoint = 0x00,	/* Control pipe */
133		.direction = UE_DIR_ANY,
134		.mh.bufsize = sizeof(struct usb2_device_request),
135		.mh.flags = {},
136		.mh.callback = &ubtbcmfw_write_clear_stall_callback,
137		.mh.timeout = 1000,	/* 1 second */
138		.mh.interval = 50,	/* 50ms */
139	},
140
141	[3] = {
142		.type = UE_CONTROL,
143		.endpoint = 0x00,	/* Control pipe */
144		.direction = UE_DIR_ANY,
145		.mh.bufsize = sizeof(struct usb2_device_request),
146		.mh.flags = {},
147		.mh.callback = &ubtbcmfw_read_clear_stall_callback,
148		.mh.timeout = 1000,	/* 1 second */
149		.mh.interval = 50,	/* 50ms */
150	},
151};
152
153/*
154 * Module
155 */
156
157static devclass_t ubtbcmfw_devclass;
158
159static device_method_t ubtbcmfw_methods[] = {
160	DEVMETHOD(device_probe, ubtbcmfw_probe),
161	DEVMETHOD(device_attach, ubtbcmfw_attach),
162	DEVMETHOD(device_detach, ubtbcmfw_detach),
163	{0, 0}
164};
165
166static driver_t ubtbcmfw_driver = {
167	.name = "ubtbcmfw",
168	.methods = ubtbcmfw_methods,
169	.size = sizeof(struct ubtbcmfw_softc),
170};
171
172DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
173MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1);
174MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1);
175
176/*
177 * Probe for a USB Bluetooth device
178 */
179
180static int
181ubtbcmfw_probe(device_t dev)
182{
183	struct usb2_attach_arg *uaa = device_get_ivars(dev);
184
185	if (uaa->usb2_mode != USB_MODE_HOST) {
186		return (ENXIO);
187	}
188	if (uaa->info.bIfaceIndex != 0)
189		return (ENXIO);
190
191	/* Match the boot device. */
192	if (uaa->info.idVendor == USB_VENDOR_BROADCOM &&
193	    uaa->info.idProduct == USB_PRODUCT_BROADCOM_BCM2033)
194		return (0);
195
196	return (ENXIO);
197}
198
199/*
200 * Attach the device
201 */
202
203static int
204ubtbcmfw_attach(device_t dev)
205{
206	struct usb2_attach_arg *uaa = device_get_ivars(dev);
207	struct ubtbcmfw_softc *sc = device_get_softc(dev);
208	int32_t err;
209	uint8_t iface_index;
210
211	if (sc == NULL) {
212		return (ENOMEM);
213	}
214	sc->sc_dev = dev;
215	sc->sc_udev = uaa->device;
216
217	device_set_usb2_desc(dev);
218
219	mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
220
221	iface_index = UBTBCMFW_IFACE_IDX;
222	err = usb2_transfer_setup(uaa->device,
223	    &iface_index, sc->sc_xfer, ubtbcmfw_config,
224	    UBTBCMFW_T_MAX, sc, &sc->sc_mtx);
225	if (err) {
226		device_printf(dev, "allocating USB transfers "
227		    "failed, err=%s\n", usb2_errstr(err));
228		goto detach;
229	}
230	/* set interface permissions */
231	usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
232	    UID_ROOT, GID_OPERATOR, 0644);
233
234	err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
235	    &ubtbcmfw_fifo_methods, &sc->sc_fifo,
236	    device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex);
237	if (err) {
238		goto detach;
239	}
240	return (0);			/* success */
241
242detach:
243	ubtbcmfw_detach(dev);
244	return (ENOMEM);		/* failure */
245}
246
247/*
248 * Detach the device
249 */
250
251static int
252ubtbcmfw_detach(device_t dev)
253{
254	struct ubtbcmfw_softc *sc = device_get_softc(dev);
255
256	usb2_fifo_detach(&sc->sc_fifo);
257
258	usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_T_MAX);
259
260	mtx_destroy(&sc->sc_mtx);
261
262	return (0);
263}
264
265static void
266ubtbcmfw_write_callback(struct usb2_xfer *xfer)
267{
268	struct ubtbcmfw_softc *sc = xfer->priv_sc;
269	struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX];
270	uint32_t actlen;
271
272	switch (USB_GET_STATE(xfer)) {
273	case USB_ST_TRANSFERRED:
274	case USB_ST_SETUP:
275		if (sc->sc_flags & UBTBCMFW_FLAG_WRITE_STALL) {
276			usb2_transfer_start(sc->sc_xfer[2]);
277			return;
278		}
279		if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
280		    UBTBCMFW_BSIZE, &actlen, 0)) {
281
282			xfer->frlengths[0] = actlen;
283			usb2_start_hardware(xfer);
284		}
285		return;
286
287	default:			/* Error */
288		if (xfer->error != USB_ERR_CANCELLED) {
289			/* try to clear stall first */
290			sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL;
291			usb2_transfer_start(sc->sc_xfer[2]);
292		}
293		return;
294	}
295}
296
297static void
298ubtbcmfw_write_clear_stall_callback(struct usb2_xfer *xfer)
299{
300	struct ubtbcmfw_softc *sc = xfer->priv_sc;
301	struct usb2_xfer *xfer_other = sc->sc_xfer[0];
302
303	if (usb2_clear_stall_callback(xfer, xfer_other)) {
304		DPRINTF("stall cleared\n");
305		sc->sc_flags &= ~UBTBCMFW_FLAG_WRITE_STALL;
306		usb2_transfer_start(xfer_other);
307	}
308	return;
309}
310
311static void
312ubtbcmfw_read_callback(struct usb2_xfer *xfer)
313{
314	struct ubtbcmfw_softc *sc = xfer->priv_sc;
315	struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX];
316
317	switch (USB_GET_STATE(xfer)) {
318	case USB_ST_TRANSFERRED:
319		usb2_fifo_put_data(f, xfer->frbuffers,
320		    0, xfer->actlen, 1);
321
322	case USB_ST_SETUP:
323		if (sc->sc_flags & UBTBCMFW_FLAG_READ_STALL) {
324			usb2_transfer_start(sc->sc_xfer[3]);
325			return;
326		}
327		if (usb2_fifo_put_bytes_max(f) != 0) {
328			xfer->frlengths[0] = xfer->max_data_length;
329			usb2_start_hardware(xfer);
330		}
331		return;
332
333	default:			/* Error */
334		if (xfer->error != USB_ERR_CANCELLED) {
335			/* try to clear stall first */
336			sc->sc_flags |= UBTBCMFW_FLAG_READ_STALL;
337			usb2_transfer_start(sc->sc_xfer[3]);
338		}
339		return;
340	}
341}
342
343static void
344ubtbcmfw_read_clear_stall_callback(struct usb2_xfer *xfer)
345{
346	struct ubtbcmfw_softc *sc = xfer->priv_sc;
347	struct usb2_xfer *xfer_other = sc->sc_xfer[1];
348
349	if (usb2_clear_stall_callback(xfer, xfer_other)) {
350		DPRINTF("stall cleared\n");
351		sc->sc_flags &= ~UBTBCMFW_FLAG_READ_STALL;
352		usb2_transfer_start(xfer_other);
353	}
354	return;
355}
356
357static void
358ubtbcmfw_start_read(struct usb2_fifo *fifo)
359{
360	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
361
362	usb2_transfer_start(sc->sc_xfer[1]);
363	return;
364}
365
366static void
367ubtbcmfw_stop_read(struct usb2_fifo *fifo)
368{
369	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
370
371	usb2_transfer_stop(sc->sc_xfer[3]);
372	usb2_transfer_stop(sc->sc_xfer[1]);
373	return;
374}
375
376static void
377ubtbcmfw_start_write(struct usb2_fifo *fifo)
378{
379	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
380
381	usb2_transfer_start(sc->sc_xfer[0]);
382	return;
383}
384
385static void
386ubtbcmfw_stop_write(struct usb2_fifo *fifo)
387{
388	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
389
390	usb2_transfer_stop(sc->sc_xfer[2]);
391	usb2_transfer_stop(sc->sc_xfer[0]);
392	return;
393}
394
395static int
396ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
397{
398	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
399
400	if (fflags & FREAD) {
401		if (usb2_fifo_alloc_buffer(fifo,
402		    sc->sc_xfer[1]->max_data_length,
403		    UBTBCMFW_IFQ_MAXLEN)) {
404			return (ENOMEM);
405		}
406	}
407	if (fflags & FWRITE) {
408		/* clear stall first */
409		mtx_lock(&sc->sc_mtx);
410		sc->sc_flags |= UBTBCMFW_FLAG_WRITE_STALL;
411		mtx_unlock(&sc->sc_mtx);
412		if (usb2_fifo_alloc_buffer(fifo,
413		    sc->sc_xfer[0]->max_data_length,
414		    UBTBCMFW_IFQ_MAXLEN)) {
415			return (ENOMEM);
416		}
417	}
418	return (0);
419}
420
421static void
422ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
423{
424	if (fflags & (FREAD | FWRITE)) {
425		usb2_fifo_free_buffer(fifo);
426	}
427	return;
428}
429
430static int
431ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
432    int fflags, struct thread *td)
433{
434	struct ubtbcmfw_softc *sc = fifo->priv_sc0;
435	int error = 0;
436
437	switch (cmd) {
438	case USB_GET_DEVICE_DESC:
439		*(struct usb2_device_descriptor *)data =
440		    *usb2_get_device_descriptor(sc->sc_udev);
441		break;
442
443	default:
444		error = EINVAL;
445		break;
446	}
447	return (error);
448}
449