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