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: src/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.18 2007/06/23 04:34:38 imp Exp $
32  * $DragonFly: src/sys/netgraph7/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.2 2008/06/26 23:05:40 dillon Exp $
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/conf.h>
39 #include <sys/filio.h>
40 #include <sys/fcntl.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/poll.h>
44 #include <sys/proc.h>
45 #include <sys/sysctl.h>
46 #include <sys/uio.h>
47 
48 #include <dev/usb/usb.h>
49 #include <dev/usb/usbdi.h>
50 #include <dev/usb/usbdi_util.h>
51 
52 #include "usbdevs.h"
53 
54 /*
55  * Download firmware to BCM2033.
56  */
57 
58 #define UBTBCMFW_CONFIG_NO	1	/* Config number */
59 #define UBTBCMFW_IFACE_IDX	0 	/* Control interface */
60 #define UBTBCMFW_INTR_IN_EP	0x81	/* Fixed endpoint */
61 #define UBTBCMFW_BULK_OUT_EP	0x02	/* Fixed endpoint */
62 #define UBTBCMFW_INTR_IN	UE_GET_ADDR(UBTBCMFW_INTR_IN_EP)
63 #define UBTBCMFW_BULK_OUT	UE_GET_ADDR(UBTBCMFW_BULK_OUT_EP)
64 
65 struct ubtbcmfw_softc {
66 	device_t		sc_dev;			/* base device */
67 	usbd_device_handle	sc_udev;		/* USB device handle */
68 	struct cdev *sc_ctrl_dev;		/* control device */
69 	struct cdev *sc_intr_in_dev;		/* interrupt device */
70 	struct cdev *sc_bulk_out_dev;	/* bulk device */
71 	usbd_pipe_handle	sc_intr_in_pipe;	/* interrupt pipe */
72 	usbd_pipe_handle	sc_bulk_out_pipe;	/* bulk out pipe */
73 	int			sc_flags;
74 #define UBTBCMFW_CTRL_DEV	(1 << 0)
75 #define UBTBCMFW_INTR_IN_DEV	(1 << 1)
76 #define UBTBCMFW_BULK_OUT_DEV	(1 << 2)
77 	int			sc_refcnt;
78 	int			sc_dying;
79 };
80 
81 typedef struct ubtbcmfw_softc	*ubtbcmfw_softc_p;
82 
83 /*
84  * Device methods
85  */
86 
87 #define UBTBCMFW_UNIT(n)	((minor(n) >> 4) & 0xf)
88 #define UBTBCMFW_ENDPOINT(n)	(minor(n) & 0xf)
89 #define UBTBCMFW_MINOR(u, e)	(((u) << 4) | (e))
90 #define UBTBCMFW_BSIZE		1024
91 
92 static d_open_t		ubtbcmfw_open;
93 static d_close_t	ubtbcmfw_close;
94 static d_read_t		ubtbcmfw_read;
95 static d_write_t	ubtbcmfw_write;
96 static d_ioctl_t	ubtbcmfw_ioctl;
97 static d_poll_t		ubtbcmfw_poll;
98 
99 static struct cdevsw	ubtbcmfw_cdevsw = {
100 	.d_version =	D_VERSION,
101 	.d_flags =	D_NEEDGIANT,
102 	.d_open =	ubtbcmfw_open,
103 	.d_close =	ubtbcmfw_close,
104 	.d_read =	ubtbcmfw_read,
105 	.d_write =	ubtbcmfw_write,
106 	.d_ioctl =	ubtbcmfw_ioctl,
107 	.d_poll =	ubtbcmfw_poll,
108 	.d_name =	"ubtbcmfw",
109 };
110 
111 /*
112  * Module
113  */
114 
115 static device_probe_t ubtbcmfw_match;
116 static device_attach_t ubtbcmfw_attach;
117 static device_detach_t ubtbcmfw_detach;
118 
119 static device_method_t ubtbcmfw_methods[] = {
120 	/* Device interface */
121 	DEVMETHOD(device_probe,		ubtbcmfw_match),
122 	DEVMETHOD(device_attach,	ubtbcmfw_attach),
123 	DEVMETHOD(device_detach,	ubtbcmfw_detach),
124 
125 	{ 0, 0 }
126 };
127 
128 static driver_t ubtbcmfw_driver = {
129 	"ubtbcmfw",
130 	ubtbcmfw_methods,
131 	sizeof(struct ubtbcmfw_softc)
132 };
133 
134 static devclass_t ubtbcmfw_devclass;
135 
136 MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
137 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass,
138 	      usbd_driver_load, 0);
139 
140 /*
141  * Probe for a USB Bluetooth device
142  */
143 
144 static int
145 ubtbcmfw_match(device_t self)
146 {
147 #define	USB_PRODUCT_BROADCOM_BCM2033NF	0x2033
148 	struct usb_attach_arg *uaa = device_get_ivars(self);
149 
150 	if (uaa->iface != NULL)
151 		return (UMATCH_NONE);
152 
153 	/* Match the boot device. */
154 	if (uaa->vendor == USB_VENDOR_BROADCOM &&
155 	    uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF)
156 		return (UMATCH_VENDOR_PRODUCT);
157 
158 	return (UMATCH_NONE);
159 }
160 
161 /*
162  * Attach the device
163  */
164 
165 static int
166 ubtbcmfw_attach(device_t self)
167 {
168 	struct ubtbcmfw_softc *sc = device_get_softc(self);
169 	struct usb_attach_arg *uaa = device_get_ivars(self);
170 	usbd_interface_handle	iface;
171 	usbd_status		err;
172 
173 	sc->sc_dev = self;
174 	sc->sc_udev = uaa->device;
175 
176 	sc->sc_ctrl_dev = sc->sc_intr_in_dev = sc->sc_bulk_out_dev = NULL;
177 	sc->sc_intr_in_pipe = sc->sc_bulk_out_pipe = NULL;
178 	sc->sc_flags = sc->sc_refcnt = sc->sc_dying = 0;
179 
180 	err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1);
181 	if (err) {
182 		printf("%s: setting config no failed. %s\n",
183 			device_get_nameunit(sc->sc_dev), usbd_errstr(err));
184 		goto bad;
185 	}
186 
187 	err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX,
188 			&iface);
189 	if (err) {
190 		printf("%s: getting interface handle failed. %s\n",
191 			device_get_nameunit(sc->sc_dev), usbd_errstr(err));
192 		goto bad;
193 	}
194 
195 	/* Will be used as a bulk pipe */
196 	err = usbd_open_pipe(iface, UBTBCMFW_INTR_IN_EP, 0,
197 			&sc->sc_intr_in_pipe);
198 	if (err) {
199 		printf("%s: open intr in failed. %s\n",
200 			device_get_nameunit(sc->sc_dev), usbd_errstr(err));
201 		goto bad;
202 	}
203 
204 	err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0,
205 			&sc->sc_bulk_out_pipe);
206 	if (err) {
207 		printf("%s: open bulk out failed. %s\n",
208 			device_get_nameunit(sc->sc_dev), usbd_errstr(err));
209 		goto bad;
210 	}
211 
212 	/* Create device nodes */
213 	sc->sc_ctrl_dev = make_dev(&ubtbcmfw_cdevsw,
214 		UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), 0),
215 		UID_ROOT, GID_OPERATOR, 0644,
216 		"%s", device_get_nameunit(sc->sc_dev));
217 
218 	sc->sc_intr_in_dev = make_dev(&ubtbcmfw_cdevsw,
219 		UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), UBTBCMFW_INTR_IN),
220 		UID_ROOT, GID_OPERATOR, 0644,
221 		"%s.%d", device_get_nameunit(sc->sc_dev), UBTBCMFW_INTR_IN);
222 
223 	sc->sc_bulk_out_dev = make_dev(&ubtbcmfw_cdevsw,
224 		UBTBCMFW_MINOR(device_get_unit(sc->sc_dev), UBTBCMFW_BULK_OUT),
225 		UID_ROOT, GID_OPERATOR, 0644,
226 		"%s.%d", device_get_nameunit(sc->sc_dev), UBTBCMFW_BULK_OUT);
227 
228 	return 0;
229 bad:
230 	ubtbcmfw_detach(self);
231 	return ENXIO;
232 }
233 
234 /*
235  * Detach the device
236  */
237 
238 static int
239 ubtbcmfw_detach(device_t self)
240 {
241 	struct ubtbcmfw_softc *sc = device_get_softc(self);
242 
243 	sc->sc_dying = 1;
244 	if (-- sc->sc_refcnt >= 0) {
245 		if (sc->sc_intr_in_pipe != NULL)
246 			usbd_abort_pipe(sc->sc_intr_in_pipe);
247 
248 		if (sc->sc_bulk_out_pipe != NULL)
249 			usbd_abort_pipe(sc->sc_bulk_out_pipe);
250 
251 		usb_detach_wait(sc->sc_dev);
252 	}
253 
254 	/* Destroy device nodes */
255 	if (sc->sc_bulk_out_dev != NULL) {
256 		destroy_dev(sc->sc_bulk_out_dev);
257 		sc->sc_bulk_out_dev = NULL;
258 	}
259 
260 	if (sc->sc_intr_in_dev != NULL) {
261 		destroy_dev(sc->sc_intr_in_dev);
262 		sc->sc_intr_in_dev = NULL;
263 	}
264 
265 	if (sc->sc_ctrl_dev != NULL) {
266 		destroy_dev(sc->sc_ctrl_dev);
267 		sc->sc_ctrl_dev = NULL;
268 	}
269 
270 	/* Close pipes */
271 	if (sc->sc_intr_in_pipe != NULL) {
272 		usbd_close_pipe(sc->sc_intr_in_pipe);
273 		sc->sc_intr_in_pipe = NULL;
274 	}
275 
276 	if (sc->sc_bulk_out_pipe != NULL) {
277 		usbd_close_pipe(sc->sc_bulk_out_pipe);
278 		sc->sc_intr_in_pipe = NULL;
279 	}
280 
281 	return (0);
282 }
283 
284 /*
285  * Open endpoint device
286  * XXX FIXME softc locking
287  */
288 
289 static int
290 ubtbcmfw_open(struct cdev *dev, int flag, int mode, struct thread *p)
291 {
292 	ubtbcmfw_softc_p	sc = NULL;
293 	int			error = 0;
294 
295 	/* checks for sc != NULL */
296 	sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
297 	if (sc == NULL)
298 		return (ENXIO);
299 	if (sc->sc_dying)
300 		return (ENXIO);
301 
302 	switch (UBTBCMFW_ENDPOINT(dev)) {
303 	case USB_CONTROL_ENDPOINT:
304 		if (!(sc->sc_flags & UBTBCMFW_CTRL_DEV))
305 			sc->sc_flags |= UBTBCMFW_CTRL_DEV;
306 		else
307 			error = EBUSY;
308 		break;
309 
310 	case UBTBCMFW_INTR_IN:
311 		if (!(sc->sc_flags & UBTBCMFW_INTR_IN_DEV)) {
312 			if (sc->sc_intr_in_pipe != NULL)
313 				sc->sc_flags |= UBTBCMFW_INTR_IN_DEV;
314 			else
315 				error = ENXIO;
316 		} else
317 			error = EBUSY;
318 		break;
319 
320 	case UBTBCMFW_BULK_OUT:
321 		if (!(sc->sc_flags & UBTBCMFW_BULK_OUT_DEV)) {
322 			if (sc->sc_bulk_out_pipe != NULL)
323 				sc->sc_flags |= UBTBCMFW_BULK_OUT_DEV;
324 			else
325 				error = ENXIO;
326 		} else
327 			error = EBUSY;
328 		break;
329 
330 	default:
331 		error = ENXIO;
332 		break;
333 	}
334 
335 	return (error);
336 }
337 
338 /*
339  * Close endpoint device
340  * XXX FIXME softc locking
341  */
342 
343 static int
344 ubtbcmfw_close(struct cdev *dev, int flag, int mode, struct thread *p)
345 {
346 	ubtbcmfw_softc_p	sc = NULL;
347 
348 	sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
349 	if (sc == NULL)
350 		return (ENXIO);
351 
352 	switch (UBTBCMFW_ENDPOINT(dev)) {
353 	case USB_CONTROL_ENDPOINT:
354 		sc->sc_flags &= ~UBTBCMFW_CTRL_DEV;
355 		break;
356 
357 	case UBTBCMFW_INTR_IN:
358 		if (sc->sc_intr_in_pipe != NULL)
359 			usbd_abort_pipe(sc->sc_intr_in_pipe);
360 
361 		sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV;
362 		break;
363 
364 	case UBTBCMFW_BULK_OUT:
365 		if (sc->sc_bulk_out_pipe != NULL)
366 			usbd_abort_pipe(sc->sc_bulk_out_pipe);
367 
368 		sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV;
369 		break;
370 	}
371 
372 	return (0);
373 }
374 
375 /*
376  * Read from the endpoint device
377  * XXX FIXME softc locking
378  */
379 
380 static int
381 ubtbcmfw_read(struct cdev *dev, struct uio *uio, int flag)
382 {
383 	ubtbcmfw_softc_p	sc = NULL;
384 	u_int8_t		buf[UBTBCMFW_BSIZE];
385 	usbd_xfer_handle	xfer;
386 	usbd_status		err;
387 	int			n, tn, error = 0;
388 
389 	sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
390 	if (sc == NULL || sc->sc_dying)
391 		return (ENXIO);
392 
393 	if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN)
394 		return (EOPNOTSUPP);
395 	if (sc->sc_intr_in_pipe == NULL)
396 		return (ENXIO);
397 
398 	xfer = usbd_alloc_xfer(sc->sc_udev);
399 	if (xfer == NULL)
400 		return (ENOMEM);
401 
402 	sc->sc_refcnt ++;
403 
404 	while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) {
405 		tn = n;
406 		err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe,
407 				USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT,
408 				buf, &tn, "bcmrd");
409 		switch (err) {
410 		case USBD_NORMAL_COMPLETION:
411 			error = uiomove(buf, (size_t)tn, uio);
412 			break;
413 
414 		case USBD_INTERRUPTED:
415 			error = EINTR;
416 			break;
417 
418 		case USBD_TIMEOUT:
419 			error = ETIMEDOUT;
420 			break;
421 
422 		default:
423 			error = EIO;
424 			break;
425 		}
426 
427 		if (error != 0 || tn < n)
428 			break;
429 	}
430 
431 	usbd_free_xfer(xfer);
432 
433 	if (-- sc->sc_refcnt < 0)
434 		usb_detach_wakeup(sc->sc_dev);
435 
436 	return (error);
437 }
438 
439 /*
440  * Write into the endpoint device
441  * XXX FIXME softc locking
442  */
443 
444 static int
445 ubtbcmfw_write(struct cdev *dev, struct uio *uio, int flag)
446 {
447 	ubtbcmfw_softc_p	sc = NULL;
448 	u_int8_t		buf[UBTBCMFW_BSIZE];
449 	usbd_xfer_handle	xfer;
450 	usbd_status		err;
451 	int			n, error = 0;
452 
453 	sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
454 	if (sc == NULL || sc->sc_dying)
455 		return (ENXIO);
456 
457 	if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT)
458 		return (EOPNOTSUPP);
459 	if (sc->sc_bulk_out_pipe == NULL)
460 		return (ENXIO);
461 
462 	xfer = usbd_alloc_xfer(sc->sc_udev);
463 	if (xfer == NULL)
464 		return (ENOMEM);
465 
466 	sc->sc_refcnt ++;
467 
468 	while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) {
469 		error = uiomove(buf, (size_t)n, uio);
470 		if (error != 0)
471 			break;
472 
473 		err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe,
474 				0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr");
475 		switch (err) {
476 		case USBD_NORMAL_COMPLETION:
477 			break;
478 
479 		case USBD_INTERRUPTED:
480 			error = EINTR;
481 			break;
482 
483 		case USBD_TIMEOUT:
484 			error = ETIMEDOUT;
485 			break;
486 
487 		default:
488 			error = EIO;
489 			break;
490 		}
491 
492 		if (error != 0)
493 			break;
494 	}
495 
496 	usbd_free_xfer(xfer);
497 
498 	if (-- sc->sc_refcnt < 0)
499 		usb_detach_wakeup(sc->sc_dev);
500 
501 	return (error);
502 }
503 
504 /*
505  * Process ioctl on the endpoint device
506  * XXX FIXME softc locking
507  */
508 
509 static int
510 ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
511   struct thread *p)
512 {
513 	ubtbcmfw_softc_p	sc = NULL;
514 	int			error = 0;
515 
516 	sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
517 	if (sc == NULL || sc->sc_dying)
518 		return (ENXIO);
519 
520 	if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT)
521 		return (EOPNOTSUPP);
522 
523 	sc->sc_refcnt ++;
524 
525 	switch (cmd) {
526 	case USB_GET_DEVICE_DESC:
527 		*(usb_device_descriptor_t *) data =
528 				*usbd_get_device_descriptor(sc->sc_udev);
529 		break;
530 
531 	default:
532 		error = EINVAL;
533 		break;
534 	}
535 
536 	if (-- sc->sc_refcnt < 0)
537 		usb_detach_wakeup(sc->sc_dev);
538 
539 	return (error);
540 }
541 
542 /*
543  * Poll the endpoint device
544  * XXX FIXME softc locking
545  */
546 
547 static int
548 ubtbcmfw_poll(struct cdev *dev, int events, struct thread *p)
549 {
550 	ubtbcmfw_softc_p	sc = NULL;
551 	int			revents = 0;
552 
553 	sc = devclass_get_softc(ubtbcmfw_devclass, UBTBCMFW_UNIT(dev));
554 	if (sc == NULL)
555 		return (ENXIO);
556 
557 	switch (UBTBCMFW_ENDPOINT(dev)) {
558 	case UBTBCMFW_INTR_IN:
559 		if (sc->sc_intr_in_pipe != NULL)
560 			revents |= events & (POLLIN | POLLRDNORM);
561 		else
562 			revents = ENXIO;
563 		break;
564 
565 	case UBTBCMFW_BULK_OUT:
566 		if (sc->sc_bulk_out_pipe != NULL)
567 			revents |= events & (POLLOUT | POLLWRNORM);
568 		else
569 			revents = ENXIO;
570 		break;
571 
572 	default:
573 		revents = EOPNOTSUPP;
574 		break;
575 	}
576 
577 	return (revents);
578 }
579