ubtbcmfw.c revision 187994
1/*
2 * ubtbcmfw.c
3 */
4
5/*-
6 * Copyright (c) 2003-2009 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 187994 2009-02-02 00:49:39Z 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
58#define	UBTBCMFW_BSIZE		1024
59#define	UBTBCMFW_IFQ_MAXLEN	2
60
61enum {
62	UBTBCMFW_BULK_DT_WR = 0,
63	UBTBCMFW_INTR_DT_RD,
64	UBTBCMFW_N_TRANSFER,
65};
66
67struct ubtbcmfw_softc {
68	struct usb2_device	*sc_udev;
69	struct mtx		sc_mtx;
70	struct usb2_xfer	*sc_xfer[UBTBCMFW_N_TRANSFER];
71	struct usb2_fifo_sc	sc_fifo;
72};
73
74/*
75 * Prototypes
76 */
77
78static device_probe_t		ubtbcmfw_probe;
79static device_attach_t		ubtbcmfw_attach;
80static device_detach_t		ubtbcmfw_detach;
81
82static usb2_callback_t		ubtbcmfw_write_callback;
83static usb2_callback_t		ubtbcmfw_read_callback;
84
85static usb2_fifo_close_t	ubtbcmfw_close;
86static usb2_fifo_cmd_t		ubtbcmfw_start_read;
87static usb2_fifo_cmd_t		ubtbcmfw_start_write;
88static usb2_fifo_cmd_t		ubtbcmfw_stop_read;
89static usb2_fifo_cmd_t		ubtbcmfw_stop_write;
90static usb2_fifo_ioctl_t	ubtbcmfw_ioctl;
91static usb2_fifo_open_t		ubtbcmfw_open;
92
93static struct usb2_fifo_methods	ubtbcmfw_fifo_methods =
94{
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
110/*
111 * Device's config structure
112 */
113
114static const struct usb2_config	ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
115{
116	[UBTBCMFW_BULK_DT_WR] = {
117		.type =		UE_BULK,
118		.endpoint =	0x02,	/* fixed */
119		.direction =	UE_DIR_OUT,
120		.if_index =	UBTBCMFW_IFACE_IDX,
121		.mh.bufsize =	UBTBCMFW_BSIZE,
122		.mh.flags =	{ .pipe_bof = 1, .force_short_xfer = 1,
123				  .proxy_buffer = 1, },
124		.mh.callback =	&ubtbcmfw_write_callback,
125	},
126
127	[UBTBCMFW_INTR_DT_RD] = {
128		.type =		UE_INTERRUPT,
129		.endpoint =	0x01,	/* fixed */
130		.direction =	UE_DIR_IN,
131		.if_index =	UBTBCMFW_IFACE_IDX,
132		.mh.bufsize =	UBTBCMFW_BSIZE,
133		.mh.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1,
134				  .proxy_buffer = 1, },
135		.mh.callback =	&ubtbcmfw_read_callback,
136	},
137};
138
139/*
140 * Module
141 */
142
143static devclass_t	ubtbcmfw_devclass;
144
145static device_method_t	ubtbcmfw_methods[] =
146{
147	DEVMETHOD(device_probe, ubtbcmfw_probe),
148	DEVMETHOD(device_attach, ubtbcmfw_attach),
149	DEVMETHOD(device_detach, ubtbcmfw_detach),
150	{0, 0}
151};
152
153static driver_t		ubtbcmfw_driver =
154{
155	.name =		"ubtbcmfw",
156	.methods =	ubtbcmfw_methods,
157	.size =		sizeof(struct ubtbcmfw_softc),
158};
159
160DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
161MODULE_DEPEND(ubtbcmfw, usb2_bluetooth, 1, 1, 1);
162MODULE_DEPEND(ubtbcmfw, usb2_core, 1, 1, 1);
163
164/*
165 * Probe for a USB Bluetooth device
166 */
167
168static int
169ubtbcmfw_probe(device_t dev)
170{
171	const struct usb2_device_id	devs[] = {
172	/* Broadcom BCM2033 devices only */
173	{ USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
174	};
175
176	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
177
178	if (uaa->usb2_mode != USB_MODE_HOST)
179		return (ENXIO);
180
181	if (uaa->info.bIfaceIndex != 0)
182		return (ENXIO);
183
184	return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa));
185} /* ubtbcmfw_probe */
186
187/*
188 * Attach the device
189 */
190
191static int
192ubtbcmfw_attach(device_t dev)
193{
194	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
195	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
196	uint8_t			iface_index;
197	int			error;
198
199	sc->sc_udev = uaa->device;
200
201	device_set_usb2_desc(dev);
202
203	mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
204
205	iface_index = UBTBCMFW_IFACE_IDX;
206	error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
207				ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
208				sc, &sc->sc_mtx);
209	if (error != 0) {
210		device_printf(dev, "allocating USB transfers failed. %s\n",
211			usb2_errstr(error));
212		goto detach;
213	}
214
215	/* Set interface permissions */
216	usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
217		UID_ROOT, GID_OPERATOR, 0644);
218
219	error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
220			&ubtbcmfw_fifo_methods, &sc->sc_fifo,
221			device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex);
222	if (error != 0) {
223		device_printf(dev, "could not attach fifo. %s\n",
224			usb2_errstr(error));
225		goto detach;
226	}
227
228	return (0);	/* success */
229
230detach:
231	ubtbcmfw_detach(dev);
232
233	return (ENXIO);	/* failure */
234} /* ubtbcmfw_attach */
235
236/*
237 * Detach the device
238 */
239
240static int
241ubtbcmfw_detach(device_t dev)
242{
243	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
244
245	usb2_fifo_detach(&sc->sc_fifo);
246
247	usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
248
249	mtx_destroy(&sc->sc_mtx);
250
251	return (0);
252} /* ubtbcmfw_detach */
253
254/*
255 * USB write callback
256 */
257
258static void
259ubtbcmfw_write_callback(struct usb2_xfer *xfer)
260{
261	struct ubtbcmfw_softc	*sc = xfer->priv_sc;
262	struct usb2_fifo	*f = sc->sc_fifo.fp[USB_FIFO_TX];
263	uint32_t		actlen;
264
265	switch (USB_GET_STATE(xfer)) {
266	case USB_ST_SETUP:
267	case USB_ST_TRANSFERRED:
268setup_next:
269		if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
270				xfer->max_data_length, &actlen, 0)) {
271			xfer->frlengths[0] = actlen;
272			usb2_start_hardware(xfer);
273		}
274		break;
275
276	default: /* Error */
277		if (xfer->error != USB_ERR_CANCELLED) {
278			/* try to clear stall first */
279			xfer->flags.stall_pipe = 1;
280			goto setup_next;
281		}
282		break;
283	}
284} /* ubtbcmfw_write_callback */
285
286/*
287 * USB read callback
288 */
289
290static void
291ubtbcmfw_read_callback(struct usb2_xfer *xfer)
292{
293	struct ubtbcmfw_softc	*sc = xfer->priv_sc;
294	struct usb2_fifo	*fifo = sc->sc_fifo.fp[USB_FIFO_RX];
295
296	switch (USB_GET_STATE(xfer)) {
297	case USB_ST_TRANSFERRED:
298		usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1);
299		/* FALLTHROUGH */
300
301	case USB_ST_SETUP:
302setup_next:
303		if (usb2_fifo_put_bytes_max(fifo) > 0) {
304			xfer->frlengths[0] = xfer->max_data_length;
305			usb2_start_hardware(xfer);
306		}
307		break;
308
309	default: /* Error */
310		if (xfer->error != USB_ERR_CANCELLED) {
311			/* try to clear stall first */
312			xfer->flags.stall_pipe = 1;
313			goto setup_next;
314		}
315		break;
316	}
317} /* ubtbcmfw_read_callback */
318
319/*
320 * Called when we about to start read()ing from the device
321 */
322
323static void
324ubtbcmfw_start_read(struct usb2_fifo *fifo)
325{
326	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
327
328	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
329} /* ubtbcmfw_start_read */
330
331/*
332 * Called when we about to stop reading (i.e. closing fifo)
333 */
334
335static void
336ubtbcmfw_stop_read(struct usb2_fifo *fifo)
337{
338	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
339
340	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
341} /* ubtbcmfw_stop_read */
342
343/*
344 * Called when we about to start write()ing to the device, poll()ing
345 * for write or flushing fifo
346 */
347
348static void
349ubtbcmfw_start_write(struct usb2_fifo *fifo)
350{
351	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
352
353	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
354} /* ubtbcmfw_start_write */
355
356/*
357 * Called when we about to stop writing (i.e. closing fifo)
358 */
359
360static void
361ubtbcmfw_stop_write(struct usb2_fifo *fifo)
362{
363	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
364
365	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
366} /* ubtbcmfw_stop_write */
367
368/*
369 * Called when fifo is open
370 */
371
372static int
373ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
374{
375	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
376	struct usb2_xfer	*xfer;
377
378	/*
379	 * f_open fifo method can only be called with either FREAD
380	 * or FWRITE flag set at one time.
381	 */
382
383	if (fflags & FREAD)
384		xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
385	else if (fflags & FWRITE)
386		xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
387	else
388		return (EINVAL);	/* should not happen */
389
390	if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length,
391			UBTBCMFW_IFQ_MAXLEN) != 0)
392		return (ENOMEM);
393
394	return (0);
395} /* ubtbcmfw_open */
396
397/*
398 * Called when fifo is closed
399 */
400
401static void
402ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
403{
404	if (fflags & (FREAD | FWRITE))
405		usb2_fifo_free_buffer(fifo);
406} /* ubtbcmfw_close */
407
408/*
409 * Process ioctl() on USB device
410 */
411
412static int
413ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
414    int fflags, struct thread *td)
415{
416	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
417	int			error = 0;
418
419	switch (cmd) {
420	case USB_GET_DEVICE_DESC:
421		memcpy(data, usb2_get_device_descriptor(sc->sc_udev),
422			sizeof(struct usb2_device_descriptor));
423		break;
424
425	default:
426		error = EINVAL;
427		break;
428	}
429
430	return (error);
431} /* ubtbcmfw_ioctl */
432