ubtbcmfw.c revision 190734
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/usb/bluetooth/ubtbcmfw.c 190734 2009-04-05 18:20:38Z thompsa $
32 */
33
34#include "usbdevs.h"
35#include <dev/usb/usb.h>
36#include <dev/usb/usb_mfunc.h>
37#include <dev/usb/usb_error.h>
38#include <dev/usb/usb_ioctl.h>
39
40#define	USB_DEBUG_VAR usb2_debug
41
42#include <dev/usb/usb_core.h>
43#include <dev/usb/usb_debug.h>
44#include <dev/usb/usb_parse.h>
45#include <dev/usb/usb_lookup.h>
46#include <dev/usb/usb_util.h>
47#include <dev/usb/usb_busdma.h>
48#include <dev/usb/usb_mbuf.h>
49#include <dev/usb/usb_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		.bufsize =	UBTBCMFW_BSIZE,
122		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1,
123				  .proxy_buffer = 1, },
124		.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		.bufsize =	UBTBCMFW_BSIZE,
133		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1,
134				  .proxy_buffer = 1, },
135		.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, uhub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
161MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
162
163/*
164 * Probe for a USB Bluetooth device
165 */
166
167static int
168ubtbcmfw_probe(device_t dev)
169{
170	const struct usb2_device_id	devs[] = {
171	/* Broadcom BCM2033 devices only */
172	{ USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
173	};
174
175	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
176
177	if (uaa->usb2_mode != USB_MODE_HOST)
178		return (ENXIO);
179
180	if (uaa->info.bIfaceIndex != 0)
181		return (ENXIO);
182
183	return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa));
184} /* ubtbcmfw_probe */
185
186/*
187 * Attach the device
188 */
189
190static int
191ubtbcmfw_attach(device_t dev)
192{
193	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
194	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
195	uint8_t			iface_index;
196	int			error;
197
198	sc->sc_udev = uaa->device;
199
200	device_set_usb2_desc(dev);
201
202	mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
203
204	iface_index = UBTBCMFW_IFACE_IDX;
205	error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
206				ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
207				sc, &sc->sc_mtx);
208	if (error != 0) {
209		device_printf(dev, "allocating USB transfers failed. %s\n",
210			usb2_errstr(error));
211		goto detach;
212	}
213
214	error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
215			&ubtbcmfw_fifo_methods, &sc->sc_fifo,
216			device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
217			UID_ROOT, GID_OPERATOR, 0644);
218	if (error != 0) {
219		device_printf(dev, "could not attach fifo. %s\n",
220			usb2_errstr(error));
221		goto detach;
222	}
223
224	return (0);	/* success */
225
226detach:
227	ubtbcmfw_detach(dev);
228
229	return (ENXIO);	/* failure */
230} /* ubtbcmfw_attach */
231
232/*
233 * Detach the device
234 */
235
236static int
237ubtbcmfw_detach(device_t dev)
238{
239	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
240
241	usb2_fifo_detach(&sc->sc_fifo);
242
243	usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
244
245	mtx_destroy(&sc->sc_mtx);
246
247	return (0);
248} /* ubtbcmfw_detach */
249
250/*
251 * USB write callback
252 */
253
254static void
255ubtbcmfw_write_callback(struct usb2_xfer *xfer)
256{
257	struct ubtbcmfw_softc	*sc = xfer->priv_sc;
258	struct usb2_fifo	*f = sc->sc_fifo.fp[USB_FIFO_TX];
259	uint32_t		actlen;
260
261	switch (USB_GET_STATE(xfer)) {
262	case USB_ST_SETUP:
263	case USB_ST_TRANSFERRED:
264setup_next:
265		if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
266				xfer->max_data_length, &actlen, 0)) {
267			xfer->frlengths[0] = actlen;
268			usb2_start_hardware(xfer);
269		}
270		break;
271
272	default: /* Error */
273		if (xfer->error != USB_ERR_CANCELLED) {
274			/* try to clear stall first */
275			xfer->flags.stall_pipe = 1;
276			goto setup_next;
277		}
278		break;
279	}
280} /* ubtbcmfw_write_callback */
281
282/*
283 * USB read callback
284 */
285
286static void
287ubtbcmfw_read_callback(struct usb2_xfer *xfer)
288{
289	struct ubtbcmfw_softc	*sc = xfer->priv_sc;
290	struct usb2_fifo	*fifo = sc->sc_fifo.fp[USB_FIFO_RX];
291
292	switch (USB_GET_STATE(xfer)) {
293	case USB_ST_TRANSFERRED:
294		usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1);
295		/* FALLTHROUGH */
296
297	case USB_ST_SETUP:
298setup_next:
299		if (usb2_fifo_put_bytes_max(fifo) > 0) {
300			xfer->frlengths[0] = xfer->max_data_length;
301			usb2_start_hardware(xfer);
302		}
303		break;
304
305	default: /* Error */
306		if (xfer->error != USB_ERR_CANCELLED) {
307			/* try to clear stall first */
308			xfer->flags.stall_pipe = 1;
309			goto setup_next;
310		}
311		break;
312	}
313} /* ubtbcmfw_read_callback */
314
315/*
316 * Called when we about to start read()ing from the device
317 */
318
319static void
320ubtbcmfw_start_read(struct usb2_fifo *fifo)
321{
322	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
323
324	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
325} /* ubtbcmfw_start_read */
326
327/*
328 * Called when we about to stop reading (i.e. closing fifo)
329 */
330
331static void
332ubtbcmfw_stop_read(struct usb2_fifo *fifo)
333{
334	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
335
336	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
337} /* ubtbcmfw_stop_read */
338
339/*
340 * Called when we about to start write()ing to the device, poll()ing
341 * for write or flushing fifo
342 */
343
344static void
345ubtbcmfw_start_write(struct usb2_fifo *fifo)
346{
347	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
348
349	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
350} /* ubtbcmfw_start_write */
351
352/*
353 * Called when we about to stop writing (i.e. closing fifo)
354 */
355
356static void
357ubtbcmfw_stop_write(struct usb2_fifo *fifo)
358{
359	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
360
361	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
362} /* ubtbcmfw_stop_write */
363
364/*
365 * Called when fifo is open
366 */
367
368static int
369ubtbcmfw_open(struct usb2_fifo *fifo, int fflags)
370{
371	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
372	struct usb2_xfer	*xfer;
373
374	/*
375	 * f_open fifo method can only be called with either FREAD
376	 * or FWRITE flag set at one time.
377	 */
378
379	if (fflags & FREAD)
380		xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
381	else if (fflags & FWRITE)
382		xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
383	else
384		return (EINVAL);	/* should not happen */
385
386	if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length,
387			UBTBCMFW_IFQ_MAXLEN) != 0)
388		return (ENOMEM);
389
390	return (0);
391} /* ubtbcmfw_open */
392
393/*
394 * Called when fifo is closed
395 */
396
397static void
398ubtbcmfw_close(struct usb2_fifo *fifo, int fflags)
399{
400	if (fflags & (FREAD | FWRITE))
401		usb2_fifo_free_buffer(fifo);
402} /* ubtbcmfw_close */
403
404/*
405 * Process ioctl() on USB device
406 */
407
408static int
409ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
410    int fflags)
411{
412	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
413	int			error = 0;
414
415	switch (cmd) {
416	case USB_GET_DEVICE_DESC:
417		memcpy(data, usb2_get_device_descriptor(sc->sc_udev),
418			sizeof(struct usb2_device_descriptor));
419		break;
420
421	default:
422		error = EINVAL;
423		break;
424	}
425
426	return (error);
427} /* ubtbcmfw_ioctl */
428