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/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c 223486 2011-06-24 02:30:02Z hselasky $
32  */
33 
34 #include <sys/stdint.h>
35 #include <sys/param.h>
36 #include <sys/queue.h>
37 #include <sys/types.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/bus.h>
41 #include <sys/module.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/condvar.h>
45 #include <sys/sysctl.h>
46 #include <sys/unistd.h>
47 #include <sys/callout.h>
48 #include <sys/malloc.h>
49 #include <sys/priv.h>
50 #include <sys/conf.h>
51 #include <sys/fcntl.h>
52 
53 #include "usbdevs.h"
54 #include <bus/u4b/usb.h>
55 #include <bus/u4b/usbdi.h>
56 #include <bus/u4b/usb_ioctl.h>
57 
58 #define	USB_DEBUG_VAR usb_debug
59 #include <bus/u4b/usb_debug.h>
60 #include <bus/u4b/usb_dev.h>
61 
62 /*
63  * Download firmware to BCM2033.
64  */
65 
66 #define	UBTBCMFW_CONFIG_NO	1	/* Config number */
67 #define	UBTBCMFW_IFACE_IDX	0	/* Control interface */
68 
69 #define	UBTBCMFW_BSIZE		1024
70 #define	UBTBCMFW_IFQ_MAXLEN	2
71 
72 enum {
73 	UBTBCMFW_BULK_DT_WR = 0,
74 	UBTBCMFW_INTR_DT_RD,
75 	UBTBCMFW_N_TRANSFER,
76 };
77 
78 struct ubtbcmfw_softc {
79 	struct usb_device	*sc_udev;
80 	struct lock		sc_lock;
81 	struct usb_xfer	*sc_xfer[UBTBCMFW_N_TRANSFER];
82 	struct usb_fifo_sc	sc_fifo;
83 };
84 
85 /*
86  * Prototypes
87  */
88 
89 static device_probe_t		ubtbcmfw_probe;
90 static device_attach_t		ubtbcmfw_attach;
91 static device_detach_t		ubtbcmfw_detach;
92 
93 static usb_callback_t		ubtbcmfw_write_callback;
94 static usb_callback_t		ubtbcmfw_read_callback;
95 
96 static usb_fifo_close_t	ubtbcmfw_close;
97 static usb_fifo_cmd_t		ubtbcmfw_start_read;
98 static usb_fifo_cmd_t		ubtbcmfw_start_write;
99 static usb_fifo_cmd_t		ubtbcmfw_stop_read;
100 static usb_fifo_cmd_t		ubtbcmfw_stop_write;
101 static usb_fifo_ioctl_t	ubtbcmfw_ioctl;
102 static usb_fifo_open_t		ubtbcmfw_open;
103 
104 static struct usb_fifo_methods	ubtbcmfw_fifo_methods =
105 {
106 	.f_close =		&ubtbcmfw_close,
107 	.f_ioctl =		&ubtbcmfw_ioctl,
108 	.f_open =		&ubtbcmfw_open,
109 	.f_start_read =		&ubtbcmfw_start_read,
110 	.f_start_write =	&ubtbcmfw_start_write,
111 	.f_stop_read =		&ubtbcmfw_stop_read,
112 	.f_stop_write =		&ubtbcmfw_stop_write,
113 	.basename[0] =		"ubtbcmfw",
114 	.basename[1] =		"ubtbcmfw",
115 	.basename[2] =		"ubtbcmfw",
116 	.postfix[0] =		"",
117 	.postfix[1] =		".1",
118 	.postfix[2] =		".2",
119 };
120 
121 /*
122  * Device's config structure
123  */
124 
125 static const struct usb_config	ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
126 {
127 	[UBTBCMFW_BULK_DT_WR] = {
128 		.type =		UE_BULK,
129 		.endpoint =	0x02,	/* fixed */
130 		.direction =	UE_DIR_OUT,
131 		.if_index =	UBTBCMFW_IFACE_IDX,
132 		.bufsize =	UBTBCMFW_BSIZE,
133 		.flags =	{ .pipe_bof = 1, .force_short_xfer = 1,
134 				  .proxy_buffer = 1, },
135 		.callback =	&ubtbcmfw_write_callback,
136 	},
137 
138 	[UBTBCMFW_INTR_DT_RD] = {
139 		.type =		UE_INTERRUPT,
140 		.endpoint =	0x01,	/* fixed */
141 		.direction =	UE_DIR_IN,
142 		.if_index =	UBTBCMFW_IFACE_IDX,
143 		.bufsize =	UBTBCMFW_BSIZE,
144 		.flags =	{ .pipe_bof = 1, .short_xfer_ok = 1,
145 				  .proxy_buffer = 1, },
146 		.callback =	&ubtbcmfw_read_callback,
147 	},
148 };
149 
150 /*
151  * Module
152  */
153 
154 static devclass_t	ubtbcmfw_devclass;
155 
156 static device_method_t	ubtbcmfw_methods[] =
157 {
158 	DEVMETHOD(device_probe, ubtbcmfw_probe),
159 	DEVMETHOD(device_attach, ubtbcmfw_attach),
160 	DEVMETHOD(device_detach, ubtbcmfw_detach),
161 
162 	DEVMETHOD_END
163 };
164 
165 static driver_t		ubtbcmfw_driver =
166 {
167 	.name =		"ubtbcmfw",
168 	.methods =	ubtbcmfw_methods,
169 	.size =		sizeof(struct ubtbcmfw_softc),
170 };
171 
172 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, NULL);
173 MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
174 
175 /*
176  * Probe for a USB Bluetooth device
177  */
178 
179 static int
180 ubtbcmfw_probe(device_t dev)
181 {
182 	static const STRUCT_USB_HOST_ID devs[] = {
183 	/* Broadcom BCM2033 devices only */
184 	{ USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
185 	};
186 
187 	struct usb_attach_arg	*uaa = device_get_ivars(dev);
188 
189 	if (uaa->usb_mode != USB_MODE_HOST)
190 		return (ENXIO);
191 
192 	if (uaa->info.bIfaceIndex != 0)
193 		return (ENXIO);
194 
195 	return (usbd_lookup_id_by_uaa(devs, sizeof(devs), uaa));
196 } /* ubtbcmfw_probe */
197 
198 /*
199  * Attach the device
200  */
201 
202 static int
203 ubtbcmfw_attach(device_t dev)
204 {
205 	struct usb_attach_arg	*uaa = device_get_ivars(dev);
206 	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
207 	uint8_t			iface_index;
208 	int			error;
209 
210 	sc->sc_udev = uaa->device;
211 
212 	device_set_usb_desc(dev);
213 
214 	lockinit(&sc->sc_lock, "ubtbcmfw lock", 0, LK_CANRECURSE);
215 
216 	iface_index = UBTBCMFW_IFACE_IDX;
217 	error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
218 				ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
219 				sc, &sc->sc_lock);
220 	if (error != 0) {
221 		device_printf(dev, "allocating USB transfers failed. %s\n",
222 			usbd_errstr(error));
223 		goto detach;
224 	}
225 
226 	error = usb_fifo_attach(uaa->device, sc, &sc->sc_lock,
227 			&ubtbcmfw_fifo_methods, &sc->sc_fifo,
228 			device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
229 			UID_ROOT, GID_OPERATOR, 0644);
230 	if (error != 0) {
231 		device_printf(dev, "could not attach fifo. %s\n",
232 			usbd_errstr(error));
233 		goto detach;
234 	}
235 
236 	return (0);	/* success */
237 
238 detach:
239 	ubtbcmfw_detach(dev);
240 
241 	return (ENXIO);	/* failure */
242 } /* ubtbcmfw_attach */
243 
244 /*
245  * Detach the device
246  */
247 
248 static int
249 ubtbcmfw_detach(device_t dev)
250 {
251 	struct ubtbcmfw_softc	*sc = device_get_softc(dev);
252 
253 	usb_fifo_detach(&sc->sc_fifo);
254 
255 	usbd_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
256 
257 	lockuninit(&sc->sc_lock);
258 
259 	return (0);
260 } /* ubtbcmfw_detach */
261 
262 /*
263  * USB write callback
264  */
265 
266 static void
267 ubtbcmfw_write_callback(struct usb_xfer *xfer, usb_error_t error)
268 {
269 	struct ubtbcmfw_softc	*sc = usbd_xfer_softc(xfer);
270 	struct usb_fifo	*f = sc->sc_fifo.fp[USB_FIFO_TX];
271 	struct usb_page_cache	*pc;
272 	uint32_t		actlen;
273 
274 	switch (USB_GET_STATE(xfer)) {
275 	case USB_ST_SETUP:
276 	case USB_ST_TRANSFERRED:
277 setup_next:
278 		pc = usbd_xfer_get_frame(xfer, 0);
279 		if (usb_fifo_get_data(f, pc, 0, usbd_xfer_max_len(xfer),
280 			    &actlen, 0)) {
281 			usbd_xfer_set_frame_len(xfer, 0, actlen);
282 			usbd_transfer_submit(xfer);
283 		}
284 		break;
285 
286 	default: /* Error */
287 		if (error != USB_ERR_CANCELLED) {
288 			/* try to clear stall first */
289 			usbd_xfer_set_stall(xfer);
290 			goto setup_next;
291 		}
292 		break;
293 	}
294 } /* ubtbcmfw_write_callback */
295 
296 /*
297  * USB read callback
298  */
299 
300 static void
301 ubtbcmfw_read_callback(struct usb_xfer *xfer, usb_error_t error)
302 {
303 	struct ubtbcmfw_softc	*sc = usbd_xfer_softc(xfer);
304 	struct usb_fifo	*fifo = sc->sc_fifo.fp[USB_FIFO_RX];
305 	struct usb_page_cache	*pc;
306 	int			actlen;
307 
308 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
309 
310 	switch (USB_GET_STATE(xfer)) {
311 	case USB_ST_TRANSFERRED:
312 		pc = usbd_xfer_get_frame(xfer, 0);
313 		usb_fifo_put_data(fifo, pc, 0, actlen, 1);
314 		/* FALLTHROUGH */
315 
316 	case USB_ST_SETUP:
317 setup_next:
318 		if (usb_fifo_put_bytes_max(fifo) > 0) {
319 			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
320 			usbd_transfer_submit(xfer);
321 		}
322 		break;
323 
324 	default: /* Error */
325 		if (error != USB_ERR_CANCELLED) {
326 			/* try to clear stall first */
327 			usbd_xfer_set_stall(xfer);
328 			goto setup_next;
329 		}
330 		break;
331 	}
332 } /* ubtbcmfw_read_callback */
333 
334 /*
335  * Called when we about to start read()ing from the device
336  */
337 
338 static void
339 ubtbcmfw_start_read(struct usb_fifo *fifo)
340 {
341 	struct ubtbcmfw_softc	*sc = usb_fifo_softc(fifo);
342 
343 	usbd_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
344 } /* ubtbcmfw_start_read */
345 
346 /*
347  * Called when we about to stop reading (i.e. closing fifo)
348  */
349 
350 static void
351 ubtbcmfw_stop_read(struct usb_fifo *fifo)
352 {
353 	struct ubtbcmfw_softc	*sc = usb_fifo_softc(fifo);
354 
355 	usbd_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
356 } /* ubtbcmfw_stop_read */
357 
358 /*
359  * Called when we about to start write()ing to the device, poll()ing
360  * for write or flushing fifo
361  */
362 
363 static void
364 ubtbcmfw_start_write(struct usb_fifo *fifo)
365 {
366 	struct ubtbcmfw_softc	*sc = usb_fifo_softc(fifo);
367 
368 	usbd_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
369 } /* ubtbcmfw_start_write */
370 
371 /*
372  * Called when we about to stop writing (i.e. closing fifo)
373  */
374 
375 static void
376 ubtbcmfw_stop_write(struct usb_fifo *fifo)
377 {
378 	struct ubtbcmfw_softc	*sc = usb_fifo_softc(fifo);
379 
380 	usbd_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
381 } /* ubtbcmfw_stop_write */
382 
383 /*
384  * Called when fifo is open
385  */
386 
387 static int
388 ubtbcmfw_open(struct usb_fifo *fifo, int fflags)
389 {
390 	struct ubtbcmfw_softc	*sc = usb_fifo_softc(fifo);
391 	struct usb_xfer	*xfer;
392 
393 	/*
394 	 * f_open fifo method can only be called with either FREAD
395 	 * or FWRITE flag set at one time.
396 	 */
397 
398 	if (fflags & FREAD)
399 		xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
400 	else if (fflags & FWRITE)
401 		xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
402 	else
403 		return (EINVAL);	/* should not happen */
404 
405 	if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(xfer),
406 			UBTBCMFW_IFQ_MAXLEN) != 0)
407 		return (ENOMEM);
408 
409 	return (0);
410 } /* ubtbcmfw_open */
411 
412 /*
413  * Called when fifo is closed
414  */
415 
416 static void
417 ubtbcmfw_close(struct usb_fifo *fifo, int fflags)
418 {
419 	if (fflags & (FREAD | FWRITE))
420 		usb_fifo_free_buffer(fifo);
421 } /* ubtbcmfw_close */
422 
423 /*
424  * Process ioctl() on USB device
425  */
426 
427 static int
428 ubtbcmfw_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
429     int fflags)
430 {
431 	struct ubtbcmfw_softc	*sc = usb_fifo_softc(fifo);
432 	int			error = 0;
433 
434 	switch (cmd) {
435 	case USB_GET_DEVICE_DESC:
436 		memcpy(data, usbd_get_device_descriptor(sc->sc_udev),
437 			sizeof(struct usb_device_descriptor));
438 		break;
439 
440 	default:
441 		error = EINVAL;
442 		break;
443 	}
444 
445 	return (error);
446 } /* ubtbcmfw_ioctl */
447