ubtbcmfw.c revision 190734
1169689Skan/*
2169689Skan * ubtbcmfw.c
3169689Skan */
4169689Skan
5169689Skan/*-
6169689Skan * Copyright (c) 2003-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
7169689Skan * All rights reserved.
8169689Skan *
9169689Skan * Redistribution and use in source and binary forms, with or without
10169689Skan * modification, are permitted provided that the following conditions
11169689Skan * are met:
12169689Skan * 1. Redistributions of source code must retain the above copyright
13169689Skan *    notice, this list of conditions and the following disclaimer.
14169689Skan * 2. Redistributions in binary form must reproduce the above copyright
15169689Skan *    notice, this list of conditions and the following disclaimer in the
16169689Skan *    documentation and/or other materials provided with the distribution.
17169689Skan *
18169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21169689Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28169689Skan * SUCH DAMAGE.
29169689Skan *
30169689Skan * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
31169689Skan * $FreeBSD: head/sys/dev/usb/bluetooth/ubtbcmfw.c 190734 2009-04-05 18:20:38Z thompsa $
32169689Skan */
33169689Skan
34169689Skan#include "usbdevs.h"
35169689Skan#include <dev/usb/usb.h>
36169689Skan#include <dev/usb/usb_mfunc.h>
37169689Skan#include <dev/usb/usb_error.h>
38169689Skan#include <dev/usb/usb_ioctl.h>
39169689Skan
40169689Skan#define	USB_DEBUG_VAR usb2_debug
41169689Skan
42169689Skan#include <dev/usb/usb_core.h>
43169689Skan#include <dev/usb/usb_debug.h>
44169689Skan#include <dev/usb/usb_parse.h>
45169689Skan#include <dev/usb/usb_lookup.h>
46169689Skan#include <dev/usb/usb_util.h>
47169689Skan#include <dev/usb/usb_busdma.h>
48169689Skan#include <dev/usb/usb_mbuf.h>
49169689Skan#include <dev/usb/usb_dev.h>
50169689Skan
51169689Skan/*
52169689Skan * Download firmware to BCM2033.
53169689Skan */
54169689Skan
55169689Skan#define	UBTBCMFW_CONFIG_NO	1	/* Config number */
56169689Skan#define	UBTBCMFW_IFACE_IDX	0	/* Control interface */
57169689Skan
58169689Skan#define	UBTBCMFW_BSIZE		1024
59169689Skan#define	UBTBCMFW_IFQ_MAXLEN	2
60169689Skan
61169689Skanenum {
62169689Skan	UBTBCMFW_BULK_DT_WR = 0,
63169689Skan	UBTBCMFW_INTR_DT_RD,
64169689Skan	UBTBCMFW_N_TRANSFER,
65169689Skan};
66169689Skan
67169689Skanstruct ubtbcmfw_softc {
68169689Skan	struct usb2_device	*sc_udev;
69169689Skan	struct mtx		sc_mtx;
70169689Skan	struct usb2_xfer	*sc_xfer[UBTBCMFW_N_TRANSFER];
71169689Skan	struct usb2_fifo_sc	sc_fifo;
72169689Skan};
73169689Skan
74169689Skan/*
75169689Skan * Prototypes
76169689Skan */
77169689Skan
78169689Skanstatic device_probe_t		ubtbcmfw_probe;
79169689Skanstatic device_attach_t		ubtbcmfw_attach;
80169689Skanstatic device_detach_t		ubtbcmfw_detach;
81169689Skan
82169689Skanstatic usb2_callback_t		ubtbcmfw_write_callback;
83169689Skanstatic usb2_callback_t		ubtbcmfw_read_callback;
84169689Skan
85169689Skanstatic usb2_fifo_close_t	ubtbcmfw_close;
86169689Skanstatic usb2_fifo_cmd_t		ubtbcmfw_start_read;
87169689Skanstatic usb2_fifo_cmd_t		ubtbcmfw_start_write;
88169689Skanstatic usb2_fifo_cmd_t		ubtbcmfw_stop_read;
89169689Skanstatic usb2_fifo_cmd_t		ubtbcmfw_stop_write;
90169689Skanstatic usb2_fifo_ioctl_t	ubtbcmfw_ioctl;
91169689Skanstatic usb2_fifo_open_t		ubtbcmfw_open;
92169689Skan
93169689Skanstatic struct usb2_fifo_methods	ubtbcmfw_fifo_methods =
94169689Skan{
95169689Skan	.f_close =		&ubtbcmfw_close,
96169689Skan	.f_ioctl =		&ubtbcmfw_ioctl,
97169689Skan	.f_open =		&ubtbcmfw_open,
98169689Skan	.f_start_read =		&ubtbcmfw_start_read,
99169689Skan	.f_start_write =	&ubtbcmfw_start_write,
100169689Skan	.f_stop_read =		&ubtbcmfw_stop_read,
101169689Skan	.f_stop_write =		&ubtbcmfw_stop_write,
102169689Skan	.basename[0] =		"ubtbcmfw",
103169689Skan	.basename[1] =		"ubtbcmfw",
104169689Skan	.basename[2] =		"ubtbcmfw",
105169689Skan	.postfix[0] =		"",
106169689Skan	.postfix[1] =		".1",
107169689Skan	.postfix[2] =		".2",
108169689Skan};
109169689Skan
110169689Skan/*
111169689Skan * Device's config structure
112169689Skan */
113169689Skan
114169689Skanstatic const struct usb2_config	ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
115169689Skan{
116169689Skan	[UBTBCMFW_BULK_DT_WR] = {
117169689Skan		.type =		UE_BULK,
118169689Skan		.endpoint =	0x02,	/* fixed */
119169689Skan		.direction =	UE_DIR_OUT,
120169689Skan		.if_index =	UBTBCMFW_IFACE_IDX,
121169689Skan		.bufsize =	UBTBCMFW_BSIZE,
122169689Skan		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1,
123169689Skan				  .proxy_buffer = 1, },
124169689Skan		.callback =	&ubtbcmfw_write_callback,
125169689Skan	},
126169689Skan
127169689Skan	[UBTBCMFW_INTR_DT_RD] = {
128169689Skan		.type =		UE_INTERRUPT,
129169689Skan		.endpoint =	0x01,	/* fixed */
130169689Skan		.direction =	UE_DIR_IN,
131169689Skan		.if_index =	UBTBCMFW_IFACE_IDX,
132169689Skan		.bufsize =	UBTBCMFW_BSIZE,
133169689Skan		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1,
134169689Skan				  .proxy_buffer = 1, },
135169689Skan		.callback =	&ubtbcmfw_read_callback,
136169689Skan	},
137169689Skan};
138169689Skan
139169689Skan/*
140169689Skan * Module
141169689Skan */
142169689Skan
143169689Skanstatic devclass_t	ubtbcmfw_devclass;
144169689Skan
145169689Skanstatic device_method_t	ubtbcmfw_methods[] =
146169689Skan{
147169689Skan	DEVMETHOD(device_probe, ubtbcmfw_probe),
148169689Skan	DEVMETHOD(device_attach, ubtbcmfw_attach),
149169689Skan	DEVMETHOD(device_detach, ubtbcmfw_detach),
150169689Skan	{0, 0}
151169689Skan};
152169689Skan
153169689Skanstatic driver_t		ubtbcmfw_driver =
154169689Skan{
155169689Skan	.name =		"ubtbcmfw",
156169689Skan	.methods =	ubtbcmfw_methods,
157169689Skan	.size =		sizeof(struct ubtbcmfw_softc),
158169689Skan};
159169689Skan
160169689SkanDRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
161169689SkanMODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
162169689Skan
163169689Skan/*
164169689Skan * Probe for a USB Bluetooth device
165169689Skan */
166169689Skan
167169689Skanstatic int
168169689Skanubtbcmfw_probe(device_t dev)
169169689Skan{
170169689Skan	const struct usb2_device_id	devs[] = {
171169689Skan	/* Broadcom BCM2033 devices only */
172169689Skan	{ USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
173169689Skan	};
174169689Skan
175169689Skan	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
176169689Skan
177169689Skan	if (uaa->usb2_mode != USB_MODE_HOST)
178169689Skan		return (ENXIO);
179169689Skan
180169689Skan	if (uaa->info.bIfaceIndex != 0)
181169689Skan		return (ENXIO);
182169689Skan
183169689Skan	return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa));
184169689Skan} /* ubtbcmfw_probe */
185169689Skan
186169689Skan/*
187169689Skan * Attach the device
188169689Skan */
189169689Skan
190169689Skanstatic int
191169689Skanubtbcmfw_attach(device_t dev)
192169689Skan{
193169689Skan	struct usb2_attach_arg	*uaa = device_get_ivars(dev);
194169689Skan	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
195169689Skan	uint8_t			iface_index;
196169689Skan	int			error;
197169689Skan
198169689Skan	sc->sc_udev = uaa->device;
199169689Skan
200169689Skan	device_set_usb2_desc(dev);
201169689Skan
202169689Skan	mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
203169689Skan
204169689Skan	iface_index = UBTBCMFW_IFACE_IDX;
205169689Skan	error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
206169689Skan				ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
207169689Skan				sc, &sc->sc_mtx);
208169689Skan	if (error != 0) {
209169689Skan		device_printf(dev, "allocating USB transfers failed. %s\n",
210169689Skan			usb2_errstr(error));
211169689Skan		goto detach;
212169689Skan	}
213169689Skan
214169689Skan	error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
215169689Skan			&ubtbcmfw_fifo_methods, &sc->sc_fifo,
216169689Skan			device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
217169689Skan			UID_ROOT, GID_OPERATOR, 0644);
218169689Skan	if (error != 0) {
219169689Skan		device_printf(dev, "could not attach fifo. %s\n",
220169689Skan			usb2_errstr(error));
221169689Skan		goto detach;
222169689Skan	}
223169689Skan
224169689Skan	return (0);	/* success */
225169689Skan
226169689Skandetach:
227169689Skan	ubtbcmfw_detach(dev);
228169689Skan
229169689Skan	return (ENXIO);	/* failure */
230169689Skan} /* ubtbcmfw_attach */
231169689Skan
232169689Skan/*
233169689Skan * Detach the device
234169689Skan */
235169689Skan
236169689Skanstatic int
237169689Skanubtbcmfw_detach(device_t dev)
238169689Skan{
239169689Skan	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
240169689Skan
241169689Skan	usb2_fifo_detach(&sc->sc_fifo);
242169689Skan
243169689Skan	usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
244169689Skan
245169689Skan	mtx_destroy(&sc->sc_mtx);
246169689Skan
247169689Skan	return (0);
248169689Skan} /* ubtbcmfw_detach */
249169689Skan
250169689Skan/*
251169689Skan * USB write callback
252169689Skan */
253169689Skan
254169689Skanstatic void
255169689Skanubtbcmfw_write_callback(struct usb2_xfer *xfer)
256169689Skan{
257169689Skan	struct ubtbcmfw_softc	*sc = xfer->priv_sc;
258169689Skan	struct usb2_fifo	*f = sc->sc_fifo.fp[USB_FIFO_TX];
259169689Skan	uint32_t		actlen;
260169689Skan
261169689Skan	switch (USB_GET_STATE(xfer)) {
262169689Skan	case USB_ST_SETUP:
263169689Skan	case USB_ST_TRANSFERRED:
264169689Skansetup_next:
265169689Skan		if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
266169689Skan				xfer->max_data_length, &actlen, 0)) {
267169689Skan			xfer->frlengths[0] = actlen;
268169689Skan			usb2_start_hardware(xfer);
269169689Skan		}
270169689Skan		break;
271169689Skan
272169689Skan	default: /* Error */
273169689Skan		if (xfer->error != USB_ERR_CANCELLED) {
274169689Skan			/* try to clear stall first */
275169689Skan			xfer->flags.stall_pipe = 1;
276169689Skan			goto setup_next;
277169689Skan		}
278169689Skan		break;
279169689Skan	}
280169689Skan} /* ubtbcmfw_write_callback */
281169689Skan
282169689Skan/*
283169689Skan * USB read callback
284169689Skan */
285169689Skan
286169689Skanstatic void
287169689Skanubtbcmfw_read_callback(struct usb2_xfer *xfer)
288169689Skan{
289169689Skan	struct ubtbcmfw_softc	*sc = xfer->priv_sc;
290169689Skan	struct usb2_fifo	*fifo = sc->sc_fifo.fp[USB_FIFO_RX];
291169689Skan
292169689Skan	switch (USB_GET_STATE(xfer)) {
293169689Skan	case USB_ST_TRANSFERRED:
294169689Skan		usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1);
295169689Skan		/* FALLTHROUGH */
296169689Skan
297169689Skan	case USB_ST_SETUP:
298169689Skansetup_next:
299169689Skan		if (usb2_fifo_put_bytes_max(fifo) > 0) {
300169689Skan			xfer->frlengths[0] = xfer->max_data_length;
301169689Skan			usb2_start_hardware(xfer);
302169689Skan		}
303169689Skan		break;
304169689Skan
305169689Skan	default: /* Error */
306169689Skan		if (xfer->error != USB_ERR_CANCELLED) {
307169689Skan			/* try to clear stall first */
308169689Skan			xfer->flags.stall_pipe = 1;
309169689Skan			goto setup_next;
310169689Skan		}
311169689Skan		break;
312169689Skan	}
313169689Skan} /* ubtbcmfw_read_callback */
314169689Skan
315169689Skan/*
316169689Skan * Called when we about to start read()ing from the device
317169689Skan */
318169689Skan
319169689Skanstatic void
320169689Skanubtbcmfw_start_read(struct usb2_fifo *fifo)
321169689Skan{
322169689Skan	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
323169689Skan
324169689Skan	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
325169689Skan} /* ubtbcmfw_start_read */
326169689Skan
327169689Skan/*
328169689Skan * Called when we about to stop reading (i.e. closing fifo)
329169689Skan */
330169689Skan
331169689Skanstatic void
332169689Skanubtbcmfw_stop_read(struct usb2_fifo *fifo)
333169689Skan{
334169689Skan	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
335169689Skan
336169689Skan	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
337169689Skan} /* ubtbcmfw_stop_read */
338169689Skan
339169689Skan/*
340169689Skan * Called when we about to start write()ing to the device, poll()ing
341169689Skan * for write or flushing fifo
342169689Skan */
343169689Skan
344169689Skanstatic void
345169689Skanubtbcmfw_start_write(struct usb2_fifo *fifo)
346169689Skan{
347169689Skan	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
348169689Skan
349169689Skan	usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
350169689Skan} /* ubtbcmfw_start_write */
351169689Skan
352169689Skan/*
353169689Skan * Called when we about to stop writing (i.e. closing fifo)
354169689Skan */
355169689Skan
356169689Skanstatic void
357169689Skanubtbcmfw_stop_write(struct usb2_fifo *fifo)
358169689Skan{
359169689Skan	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
360169689Skan
361169689Skan	usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
362169689Skan} /* ubtbcmfw_stop_write */
363169689Skan
364169689Skan/*
365169689Skan * Called when fifo is open
366169689Skan */
367169689Skan
368169689Skanstatic int
369169689Skanubtbcmfw_open(struct usb2_fifo *fifo, int fflags)
370169689Skan{
371169689Skan	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
372169689Skan	struct usb2_xfer	*xfer;
373169689Skan
374169689Skan	/*
375169689Skan	 * f_open fifo method can only be called with either FREAD
376169689Skan	 * or FWRITE flag set at one time.
377169689Skan	 */
378169689Skan
379169689Skan	if (fflags & FREAD)
380169689Skan		xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
381169689Skan	else if (fflags & FWRITE)
382169689Skan		xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
383169689Skan	else
384169689Skan		return (EINVAL);	/* should not happen */
385169689Skan
386169689Skan	if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length,
387169689Skan			UBTBCMFW_IFQ_MAXLEN) != 0)
388169689Skan		return (ENOMEM);
389169689Skan
390169689Skan	return (0);
391169689Skan} /* ubtbcmfw_open */
392169689Skan
393169689Skan/*
394169689Skan * Called when fifo is closed
395169689Skan */
396169689Skan
397169689Skanstatic void
398169689Skanubtbcmfw_close(struct usb2_fifo *fifo, int fflags)
399169689Skan{
400169689Skan	if (fflags & (FREAD | FWRITE))
401169689Skan		usb2_fifo_free_buffer(fifo);
402169689Skan} /* ubtbcmfw_close */
403169689Skan
404169689Skan/*
405169689Skan * Process ioctl() on USB device
406169689Skan */
407169689Skan
408169689Skanstatic int
409169689Skanubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
410169689Skan    int fflags)
411169689Skan{
412169689Skan	struct ubtbcmfw_softc	*sc = fifo->priv_sc0;
413169689Skan	int			error = 0;
414169689Skan
415169689Skan	switch (cmd) {
416169689Skan	case USB_GET_DEVICE_DESC:
417169689Skan		memcpy(data, usb2_get_device_descriptor(sc->sc_udev),
418169689Skan			sizeof(struct usb2_device_descriptor));
419169689Skan		break;
420169689Skan
421169689Skan	default:
422169689Skan		error = EINVAL;
423169689Skan		break;
424169689Skan	}
425169689Skan
426169689Skan	return (error);
427169689Skan} /* ubtbcmfw_ioctl */
428169689Skan