xref: /freebsd/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c (revision 7cc9e6ddbb1a6cd38b2f44d4dd9dfc890d2fc9d0)
1 /*
2  * ubtbcmfw.c
3  *
4  * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
29  * $FreeBSD$
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/conf.h>
36 #include <sys/filio.h>
37 #include <sys/fcntl.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
40 #include <sys/poll.h>
41 #include <sys/proc.h>
42 #include <sys/sysctl.h>
43 #include <sys/uio.h>
44 
45 #include <dev/usb/usb.h>
46 #include <dev/usb/usbdi.h>
47 #include <dev/usb/usbdi_util.h>
48 
49 #include "usbdevs.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 #define UBTBCMFW_INTR_IN_EP	0x81	/* Fixed endpoint */
58 #define UBTBCMFW_BULK_OUT_EP	0x02	/* Fixed endpoint */
59 #define UBTBCMFW_INTR_IN	UE_GET_ADDR(UBTBCMFW_INTR_IN_EP)
60 #define UBTBCMFW_BULK_OUT	UE_GET_ADDR(UBTBCMFW_BULK_OUT_EP)
61 
62 struct ubtbcmfw_softc {
63 	USBBASEDEVICE		sc_dev;			/* base device */
64 	usbd_device_handle	sc_udev;		/* USB device handle */
65 	struct cdev *sc_ctrl_dev;		/* control device */
66 	struct cdev *sc_intr_in_dev;		/* interrupt device */
67 	struct cdev *sc_bulk_out_dev;	/* bulk device */
68 	usbd_pipe_handle	sc_intr_in_pipe;	/* interrupt pipe */
69 	usbd_pipe_handle	sc_bulk_out_pipe;	/* bulk out pipe */
70 	int			sc_flags;
71 #define UBTBCMFW_CTRL_DEV	(1 << 0)
72 #define UBTBCMFW_INTR_IN_DEV	(1 << 1)
73 #define UBTBCMFW_BULK_OUT_DEV	(1 << 2)
74 	int			sc_refcnt;
75 	int			sc_dying;
76 };
77 
78 typedef struct ubtbcmfw_softc	*ubtbcmfw_softc_p;
79 
80 /*
81  * Device methods
82  */
83 
84 #define UBTBCMFW_UNIT(n)	((minor(n) >> 4) & 0xf)
85 #define UBTBCMFW_ENDPOINT(n)	(minor(n) & 0xf)
86 #define UBTBCMFW_MINOR(u, e)	(((u) << 4) | (e))
87 #define UBTBCMFW_BSIZE		1024
88 
89 Static d_open_t		ubtbcmfw_open;
90 Static d_close_t	ubtbcmfw_close;
91 Static d_read_t		ubtbcmfw_read;
92 Static d_write_t	ubtbcmfw_write;
93 Static d_ioctl_t	ubtbcmfw_ioctl;
94 Static d_poll_t		ubtbcmfw_poll;
95 
96 Static struct cdevsw	ubtbcmfw_cdevsw = {
97 	.d_version =	D_VERSION,
98 	.d_flags =	D_NEEDGIANT,
99 	.d_open =	ubtbcmfw_open,
100 	.d_close =	ubtbcmfw_close,
101 	.d_read =	ubtbcmfw_read,
102 	.d_write =	ubtbcmfw_write,
103 	.d_ioctl =	ubtbcmfw_ioctl,
104 	.d_poll =	ubtbcmfw_poll,
105 	.d_name =	"ubtbcmfw",
106 };
107 
108 /*
109  * Module
110  */
111 
112 USB_DECLARE_DRIVER(ubtbcmfw);
113 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass,
114 	      usbd_driver_load, 0);
115 
116 /*
117  * Probe for a USB Bluetooth device
118  */
119 
120 USB_MATCH(ubtbcmfw)
121 {
122 #define	USB_PRODUCT_BROADCOM_BCM2033NF	0x2033
123 
124 	USB_MATCH_START(ubtbcmfw, uaa);
125 
126 	if (uaa->iface != NULL)
127 		return (UMATCH_NONE);
128 
129 	/* Match the boot device. */
130 	if (uaa->vendor == USB_VENDOR_BROADCOM &&
131 	    uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF)
132 		return (UMATCH_VENDOR_PRODUCT);
133 
134 	return (UMATCH_NONE);
135 }
136 
137 /*
138  * Attach the device
139  */
140 
141 USB_ATTACH(ubtbcmfw)
142 {
143 	USB_ATTACH_START(ubtbcmfw, sc, uaa);
144 	usbd_interface_handle	iface;
145 	usbd_status		err;
146 	char			devinfo[1024];
147 
148 	sc->sc_udev = uaa->device;
149 	usbd_devinfo(sc->sc_udev, 0, devinfo);
150 	USB_ATTACH_SETUP;
151 	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
152 
153 	sc->sc_ctrl_dev = sc->sc_intr_in_dev = sc->sc_bulk_out_dev = NULL;
154 	sc->sc_intr_in_pipe = sc->sc_bulk_out_pipe = NULL;
155 	sc->sc_flags = sc->sc_refcnt = sc->sc_dying = 0;
156 
157 	err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1);
158 	if (err) {
159 		printf("%s: setting config no failed. %s\n",
160 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
161 		goto bad;
162 	}
163 
164 	err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX,
165 			&iface);
166 	if (err) {
167 		printf("%s: getting interface handle failed. %s\n",
168 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
169 		goto bad;
170 	}
171 
172 	/* Will be used as a bulk pipe */
173 	err = usbd_open_pipe(iface, UBTBCMFW_INTR_IN_EP, 0,
174 			&sc->sc_intr_in_pipe);
175 	if (err) {
176 		printf("%s: open intr in failed. %s\n",
177 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
178 		goto bad;
179 	}
180 
181 	err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0,
182 			&sc->sc_bulk_out_pipe);
183 	if (err) {
184 		printf("%s: open bulk out failed. %s\n",
185 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
186 		goto bad;
187 	}
188 
189 	/* Create device nodes */
190 	sc->sc_ctrl_dev = make_dev(&ubtbcmfw_cdevsw,
191 		UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), 0),
192 		UID_ROOT, GID_OPERATOR, 0644,
193 		"%s", USBDEVNAME(sc->sc_dev));
194 
195 	sc->sc_intr_in_dev = make_dev(&ubtbcmfw_cdevsw,
196 		UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), UBTBCMFW_INTR_IN),
197 		UID_ROOT, GID_OPERATOR, 0644,
198 		"%s.%d", USBDEVNAME(sc->sc_dev), UBTBCMFW_INTR_IN);
199 
200 	sc->sc_bulk_out_dev = make_dev(&ubtbcmfw_cdevsw,
201 		UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), UBTBCMFW_BULK_OUT),
202 		UID_ROOT, GID_OPERATOR, 0644,
203 		"%s.%d", USBDEVNAME(sc->sc_dev), UBTBCMFW_BULK_OUT);
204 
205 	USB_ATTACH_SUCCESS_RETURN;
206 bad:
207 	ubtbcmfw_detach(self);
208 
209         USB_ATTACH_ERROR_RETURN;
210 }
211 
212 /*
213  * Detach the device
214  */
215 
216 USB_DETACH(ubtbcmfw)
217 {
218 	USB_DETACH_START(ubtbcmfw, sc);
219 
220 	sc->sc_dying = 1;
221 
222 	if (-- sc->sc_refcnt >= 0) {
223 		if (sc->sc_intr_in_pipe != NULL)
224 			usbd_abort_pipe(sc->sc_intr_in_pipe);
225 
226 		if (sc->sc_bulk_out_pipe != NULL)
227 			usbd_abort_pipe(sc->sc_bulk_out_pipe);
228 
229 		usb_detach_wait(USBDEV(sc->sc_dev));
230 	}
231 
232 	/* Destroy device nodes */
233 	if (sc->sc_bulk_out_dev != NULL) {
234 		destroy_dev(sc->sc_bulk_out_dev);
235 		sc->sc_bulk_out_dev = NULL;
236 	}
237 
238 	if (sc->sc_intr_in_dev != NULL) {
239 		destroy_dev(sc->sc_intr_in_dev);
240 		sc->sc_intr_in_dev = NULL;
241 	}
242 
243 	if (sc->sc_ctrl_dev != NULL) {
244 		destroy_dev(sc->sc_ctrl_dev);
245 		sc->sc_ctrl_dev = NULL;
246 	}
247 
248 	/* Close pipes */
249 	if (sc->sc_intr_in_pipe != NULL) {
250 		usbd_close_pipe(sc->sc_intr_in_pipe);
251 		sc->sc_intr_in_pipe = NULL;
252 	}
253 
254 	if (sc->sc_bulk_out_pipe != NULL) {
255 		usbd_close_pipe(sc->sc_bulk_out_pipe);
256 		sc->sc_intr_in_pipe = NULL;
257 	}
258 
259 	return (0);
260 }
261 
262 /*
263  * Open endpoint device
264  * XXX FIXME softc locking
265  */
266 
267 Static int
268 ubtbcmfw_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
269 {
270 	ubtbcmfw_softc_p	sc = NULL;
271 	int			error = 0;
272 
273 	/* checks for sc != NULL */
274 	USB_GET_SC_OPEN(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
275 	if (sc->sc_dying)
276 		return (ENXIO);
277 
278 	switch (UBTBCMFW_ENDPOINT(dev)) {
279 	case USB_CONTROL_ENDPOINT:
280 		if (!(sc->sc_flags & UBTBCMFW_CTRL_DEV))
281 			sc->sc_flags |= UBTBCMFW_CTRL_DEV;
282 		else
283 			error = EBUSY;
284 		break;
285 
286 	case UBTBCMFW_INTR_IN:
287 		if (!(sc->sc_flags & UBTBCMFW_INTR_IN_DEV)) {
288 			if (sc->sc_intr_in_pipe != NULL)
289 				sc->sc_flags |= UBTBCMFW_INTR_IN_DEV;
290 			else
291 				error = ENXIO;
292 		} else
293 			error = EBUSY;
294 		break;
295 
296 	case UBTBCMFW_BULK_OUT:
297 		if (!(sc->sc_flags & UBTBCMFW_BULK_OUT_DEV)) {
298 			if (sc->sc_bulk_out_pipe != NULL)
299 				sc->sc_flags |= UBTBCMFW_BULK_OUT_DEV;
300 			else
301 				error = ENXIO;
302 		} else
303 			error = EBUSY;
304 		break;
305 
306 	default:
307 		error = ENXIO;
308 		break;
309 	}
310 
311 	return (error);
312 }
313 
314 /*
315  * Close endpoint device
316  * XXX FIXME softc locking
317  */
318 
319 Static int
320 ubtbcmfw_close(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
321 {
322 	ubtbcmfw_softc_p	sc = NULL;
323 
324 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
325 	if (sc == NULL)
326 		return (ENXIO);
327 
328 	switch (UBTBCMFW_ENDPOINT(dev)) {
329 	case USB_CONTROL_ENDPOINT:
330 		sc->sc_flags &= ~UBTBCMFW_CTRL_DEV;
331 		break;
332 
333 	case UBTBCMFW_INTR_IN:
334 		if (sc->sc_intr_in_pipe != NULL)
335 			usbd_abort_pipe(sc->sc_intr_in_pipe);
336 
337 		sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV;
338 		break;
339 
340 	case UBTBCMFW_BULK_OUT:
341 		if (sc->sc_bulk_out_pipe != NULL)
342 			usbd_abort_pipe(sc->sc_bulk_out_pipe);
343 
344 		sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV;
345 		break;
346 	}
347 
348 	return (0);
349 }
350 
351 /*
352  * Read from the endpoint device
353  * XXX FIXME softc locking
354  */
355 
356 Static int
357 ubtbcmfw_read(struct cdev *dev, struct uio *uio, int flag)
358 {
359 	ubtbcmfw_softc_p	sc = NULL;
360 	u_int8_t		buf[UBTBCMFW_BSIZE];
361 	usbd_xfer_handle	xfer;
362 	usbd_status		err;
363 	int			n, tn, error = 0;
364 
365 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
366 	if (sc == NULL || sc->sc_dying)
367 		return (ENXIO);
368 
369 	if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN)
370 		return (EOPNOTSUPP);
371 	if (sc->sc_intr_in_pipe == NULL)
372 		return (ENXIO);
373 
374 	xfer = usbd_alloc_xfer(sc->sc_udev);
375 	if (xfer == NULL)
376 		return (ENOMEM);
377 
378 	sc->sc_refcnt ++;
379 
380 	while ((n = min(sizeof(buf), uio->uio_resid)) != 0) {
381 		tn = n;
382 		err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe,
383 				USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT,
384 				buf, &tn, "bcmrd");
385 		switch (err) {
386 		case USBD_NORMAL_COMPLETION:
387 			error = uiomove(buf, tn, uio);
388 			break;
389 
390 		case USBD_INTERRUPTED:
391 			error = EINTR;
392 			break;
393 
394 		case USBD_TIMEOUT:
395 			error = ETIMEDOUT;
396 			break;
397 
398 		default:
399 			error = EIO;
400 			break;
401 		}
402 
403 		if (error != 0 || tn < n)
404 			break;
405 	}
406 
407 	usbd_free_xfer(xfer);
408 
409 	if (-- sc->sc_refcnt < 0)
410 		usb_detach_wakeup(USBDEV(sc->sc_dev));
411 
412 	return (error);
413 }
414 
415 /*
416  * Write into the endpoint device
417  * XXX FIXME softc locking
418  */
419 
420 Static int
421 ubtbcmfw_write(struct cdev *dev, struct uio *uio, int flag)
422 {
423 	ubtbcmfw_softc_p	sc = NULL;
424 	u_int8_t		buf[UBTBCMFW_BSIZE];
425 	usbd_xfer_handle	xfer;
426 	usbd_status		err;
427 	int			n, error = 0;
428 
429 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
430 	if (sc == NULL || sc->sc_dying)
431 		return (ENXIO);
432 
433 	if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT)
434 		return (EOPNOTSUPP);
435 	if (sc->sc_bulk_out_pipe == NULL)
436 		return (ENXIO);
437 
438 	xfer = usbd_alloc_xfer(sc->sc_udev);
439 	if (xfer == NULL)
440 		return (ENOMEM);
441 
442 	sc->sc_refcnt ++;
443 
444 	while ((n = min(sizeof(buf), uio->uio_resid)) != 0) {
445 		error = uiomove(buf, n, uio);
446 		if (error != 0)
447 			break;
448 
449 		err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe,
450 				0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr");
451 		switch (err) {
452 		case USBD_NORMAL_COMPLETION:
453 			break;
454 
455 		case USBD_INTERRUPTED:
456 			error = EINTR;
457 			break;
458 
459 		case USBD_TIMEOUT:
460 			error = ETIMEDOUT;
461 			break;
462 
463 		default:
464 			error = EIO;
465 			break;
466 		}
467 
468 		if (error != 0)
469 			break;
470 	}
471 
472 	usbd_free_xfer(xfer);
473 
474 	if (-- sc->sc_refcnt < 0)
475 		usb_detach_wakeup(USBDEV(sc->sc_dev));
476 
477 	return (error);
478 }
479 
480 /*
481  * Process ioctl on the endpoint device
482  * XXX FIXME softc locking
483  */
484 
485 Static int
486 ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
487 {
488 	ubtbcmfw_softc_p	sc = NULL;
489 	int			error = 0;
490 
491 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
492 	if (sc == NULL || sc->sc_dying)
493 		return (ENXIO);
494 
495 	if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT)
496 		return (EOPNOTSUPP);
497 
498 	sc->sc_refcnt ++;
499 
500 	switch (cmd) {
501 	case USB_GET_DEVICE_DESC:
502 		*(usb_device_descriptor_t *) data =
503 				*usbd_get_device_descriptor(sc->sc_udev);
504 		break;
505 
506 	default:
507 		error = EINVAL;
508 		break;
509 	}
510 
511 	if (-- sc->sc_refcnt < 0)
512 		usb_detach_wakeup(USBDEV(sc->sc_dev));
513 
514 	return (error);
515 }
516 
517 /*
518  * Poll the endpoint device
519  * XXX FIXME softc locking
520  */
521 
522 Static int
523 ubtbcmfw_poll(struct cdev *dev, int events, usb_proc_ptr p)
524 {
525 	ubtbcmfw_softc_p	sc = NULL;
526 	int			revents = 0;
527 
528 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
529 	if (sc == NULL)
530 		return (ENXIO);
531 
532 	switch (UBTBCMFW_ENDPOINT(dev)) {
533 	case UBTBCMFW_INTR_IN:
534 		if (sc->sc_intr_in_pipe != NULL)
535 			revents |= events & (POLLIN | POLLRDNORM);
536 		else
537 			revents = ENXIO;
538 		break;
539 
540 	case UBTBCMFW_BULK_OUT:
541 		if (sc->sc_bulk_out_pipe != NULL)
542 			revents |= events & (POLLOUT | POLLWRNORM);
543 		else
544 			revents = ENXIO;
545 		break;
546 
547 	default:
548 		revents = EOPNOTSUPP;
549 		break;
550 	}
551 
552 	return (revents);
553 }
554 
555