xref: /freebsd/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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 	USBBASEDEVICE		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 USB_DECLARE_DRIVER(ubtbcmfw);
115 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass,
116 	      usbd_driver_load, 0);
117 
118 /*
119  * Probe for a USB Bluetooth device
120  */
121 
122 USB_MATCH(ubtbcmfw)
123 {
124 #define	USB_PRODUCT_BROADCOM_BCM2033NF	0x2033
125 
126 	USB_MATCH_START(ubtbcmfw, uaa);
127 
128 	if (uaa->iface != NULL)
129 		return (UMATCH_NONE);
130 
131 	/* Match the boot device. */
132 	if (uaa->vendor == USB_VENDOR_BROADCOM &&
133 	    uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF)
134 		return (UMATCH_VENDOR_PRODUCT);
135 
136 	return (UMATCH_NONE);
137 }
138 
139 /*
140  * Attach the device
141  */
142 
143 USB_ATTACH(ubtbcmfw)
144 {
145 	USB_ATTACH_START(ubtbcmfw, sc, uaa);
146 	usbd_interface_handle	iface;
147 	usbd_status		err;
148 	char			devinfo[1024];
149 
150 	sc->sc_udev = uaa->device;
151 	usbd_devinfo(sc->sc_udev, 0, devinfo);
152 	USB_ATTACH_SETUP;
153 	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
154 
155 	sc->sc_ctrl_dev = sc->sc_intr_in_dev = sc->sc_bulk_out_dev = NULL;
156 	sc->sc_intr_in_pipe = sc->sc_bulk_out_pipe = NULL;
157 	sc->sc_flags = sc->sc_refcnt = sc->sc_dying = 0;
158 
159 	err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1);
160 	if (err) {
161 		printf("%s: setting config no failed. %s\n",
162 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
163 		goto bad;
164 	}
165 
166 	err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX,
167 			&iface);
168 	if (err) {
169 		printf("%s: getting interface handle failed. %s\n",
170 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
171 		goto bad;
172 	}
173 
174 	/* Will be used as a bulk pipe */
175 	err = usbd_open_pipe(iface, UBTBCMFW_INTR_IN_EP, 0,
176 			&sc->sc_intr_in_pipe);
177 	if (err) {
178 		printf("%s: open intr in failed. %s\n",
179 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
180 		goto bad;
181 	}
182 
183 	err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0,
184 			&sc->sc_bulk_out_pipe);
185 	if (err) {
186 		printf("%s: open bulk out failed. %s\n",
187 			USBDEVNAME(sc->sc_dev), usbd_errstr(err));
188 		goto bad;
189 	}
190 
191 	/* Create device nodes */
192 	sc->sc_ctrl_dev = make_dev(&ubtbcmfw_cdevsw,
193 		UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), 0),
194 		UID_ROOT, GID_OPERATOR, 0644,
195 		"%s", USBDEVNAME(sc->sc_dev));
196 
197 	sc->sc_intr_in_dev = make_dev(&ubtbcmfw_cdevsw,
198 		UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), UBTBCMFW_INTR_IN),
199 		UID_ROOT, GID_OPERATOR, 0644,
200 		"%s.%d", USBDEVNAME(sc->sc_dev), UBTBCMFW_INTR_IN);
201 
202 	sc->sc_bulk_out_dev = make_dev(&ubtbcmfw_cdevsw,
203 		UBTBCMFW_MINOR(USBDEVUNIT(sc->sc_dev), UBTBCMFW_BULK_OUT),
204 		UID_ROOT, GID_OPERATOR, 0644,
205 		"%s.%d", USBDEVNAME(sc->sc_dev), UBTBCMFW_BULK_OUT);
206 
207 	USB_ATTACH_SUCCESS_RETURN;
208 bad:
209 	ubtbcmfw_detach(self);
210 
211         USB_ATTACH_ERROR_RETURN;
212 }
213 
214 /*
215  * Detach the device
216  */
217 
218 USB_DETACH(ubtbcmfw)
219 {
220 	USB_DETACH_START(ubtbcmfw, sc);
221 
222 	sc->sc_dying = 1;
223 
224 	if (-- sc->sc_refcnt >= 0) {
225 		if (sc->sc_intr_in_pipe != NULL)
226 			usbd_abort_pipe(sc->sc_intr_in_pipe);
227 
228 		if (sc->sc_bulk_out_pipe != NULL)
229 			usbd_abort_pipe(sc->sc_bulk_out_pipe);
230 
231 		usb_detach_wait(USBDEV(sc->sc_dev));
232 	}
233 
234 	/* Destroy device nodes */
235 	if (sc->sc_bulk_out_dev != NULL) {
236 		destroy_dev(sc->sc_bulk_out_dev);
237 		sc->sc_bulk_out_dev = NULL;
238 	}
239 
240 	if (sc->sc_intr_in_dev != NULL) {
241 		destroy_dev(sc->sc_intr_in_dev);
242 		sc->sc_intr_in_dev = NULL;
243 	}
244 
245 	if (sc->sc_ctrl_dev != NULL) {
246 		destroy_dev(sc->sc_ctrl_dev);
247 		sc->sc_ctrl_dev = NULL;
248 	}
249 
250 	/* Close pipes */
251 	if (sc->sc_intr_in_pipe != NULL) {
252 		usbd_close_pipe(sc->sc_intr_in_pipe);
253 		sc->sc_intr_in_pipe = NULL;
254 	}
255 
256 	if (sc->sc_bulk_out_pipe != NULL) {
257 		usbd_close_pipe(sc->sc_bulk_out_pipe);
258 		sc->sc_intr_in_pipe = NULL;
259 	}
260 
261 	return (0);
262 }
263 
264 /*
265  * Open endpoint device
266  * XXX FIXME softc locking
267  */
268 
269 Static int
270 ubtbcmfw_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
271 {
272 	ubtbcmfw_softc_p	sc = NULL;
273 	int			error = 0;
274 
275 	/* checks for sc != NULL */
276 	USB_GET_SC_OPEN(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
277 	if (sc->sc_dying)
278 		return (ENXIO);
279 
280 	switch (UBTBCMFW_ENDPOINT(dev)) {
281 	case USB_CONTROL_ENDPOINT:
282 		if (!(sc->sc_flags & UBTBCMFW_CTRL_DEV))
283 			sc->sc_flags |= UBTBCMFW_CTRL_DEV;
284 		else
285 			error = EBUSY;
286 		break;
287 
288 	case UBTBCMFW_INTR_IN:
289 		if (!(sc->sc_flags & UBTBCMFW_INTR_IN_DEV)) {
290 			if (sc->sc_intr_in_pipe != NULL)
291 				sc->sc_flags |= UBTBCMFW_INTR_IN_DEV;
292 			else
293 				error = ENXIO;
294 		} else
295 			error = EBUSY;
296 		break;
297 
298 	case UBTBCMFW_BULK_OUT:
299 		if (!(sc->sc_flags & UBTBCMFW_BULK_OUT_DEV)) {
300 			if (sc->sc_bulk_out_pipe != NULL)
301 				sc->sc_flags |= UBTBCMFW_BULK_OUT_DEV;
302 			else
303 				error = ENXIO;
304 		} else
305 			error = EBUSY;
306 		break;
307 
308 	default:
309 		error = ENXIO;
310 		break;
311 	}
312 
313 	return (error);
314 }
315 
316 /*
317  * Close endpoint device
318  * XXX FIXME softc locking
319  */
320 
321 Static int
322 ubtbcmfw_close(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
323 {
324 	ubtbcmfw_softc_p	sc = NULL;
325 
326 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
327 	if (sc == NULL)
328 		return (ENXIO);
329 
330 	switch (UBTBCMFW_ENDPOINT(dev)) {
331 	case USB_CONTROL_ENDPOINT:
332 		sc->sc_flags &= ~UBTBCMFW_CTRL_DEV;
333 		break;
334 
335 	case UBTBCMFW_INTR_IN:
336 		if (sc->sc_intr_in_pipe != NULL)
337 			usbd_abort_pipe(sc->sc_intr_in_pipe);
338 
339 		sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV;
340 		break;
341 
342 	case UBTBCMFW_BULK_OUT:
343 		if (sc->sc_bulk_out_pipe != NULL)
344 			usbd_abort_pipe(sc->sc_bulk_out_pipe);
345 
346 		sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV;
347 		break;
348 	}
349 
350 	return (0);
351 }
352 
353 /*
354  * Read from the endpoint device
355  * XXX FIXME softc locking
356  */
357 
358 Static int
359 ubtbcmfw_read(struct cdev *dev, struct uio *uio, int flag)
360 {
361 	ubtbcmfw_softc_p	sc = NULL;
362 	u_int8_t		buf[UBTBCMFW_BSIZE];
363 	usbd_xfer_handle	xfer;
364 	usbd_status		err;
365 	int			n, tn, error = 0;
366 
367 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
368 	if (sc == NULL || sc->sc_dying)
369 		return (ENXIO);
370 
371 	if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN)
372 		return (EOPNOTSUPP);
373 	if (sc->sc_intr_in_pipe == NULL)
374 		return (ENXIO);
375 
376 	xfer = usbd_alloc_xfer(sc->sc_udev);
377 	if (xfer == NULL)
378 		return (ENOMEM);
379 
380 	sc->sc_refcnt ++;
381 
382 	while ((n = min(sizeof(buf), uio->uio_resid)) != 0) {
383 		tn = n;
384 		err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe,
385 				USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT,
386 				buf, &tn, "bcmrd");
387 		switch (err) {
388 		case USBD_NORMAL_COMPLETION:
389 			error = uiomove(buf, tn, uio);
390 			break;
391 
392 		case USBD_INTERRUPTED:
393 			error = EINTR;
394 			break;
395 
396 		case USBD_TIMEOUT:
397 			error = ETIMEDOUT;
398 			break;
399 
400 		default:
401 			error = EIO;
402 			break;
403 		}
404 
405 		if (error != 0 || tn < n)
406 			break;
407 	}
408 
409 	usbd_free_xfer(xfer);
410 
411 	if (-- sc->sc_refcnt < 0)
412 		usb_detach_wakeup(USBDEV(sc->sc_dev));
413 
414 	return (error);
415 }
416 
417 /*
418  * Write into the endpoint device
419  * XXX FIXME softc locking
420  */
421 
422 Static int
423 ubtbcmfw_write(struct cdev *dev, struct uio *uio, int flag)
424 {
425 	ubtbcmfw_softc_p	sc = NULL;
426 	u_int8_t		buf[UBTBCMFW_BSIZE];
427 	usbd_xfer_handle	xfer;
428 	usbd_status		err;
429 	int			n, error = 0;
430 
431 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
432 	if (sc == NULL || sc->sc_dying)
433 		return (ENXIO);
434 
435 	if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT)
436 		return (EOPNOTSUPP);
437 	if (sc->sc_bulk_out_pipe == NULL)
438 		return (ENXIO);
439 
440 	xfer = usbd_alloc_xfer(sc->sc_udev);
441 	if (xfer == NULL)
442 		return (ENOMEM);
443 
444 	sc->sc_refcnt ++;
445 
446 	while ((n = min(sizeof(buf), uio->uio_resid)) != 0) {
447 		error = uiomove(buf, n, uio);
448 		if (error != 0)
449 			break;
450 
451 		err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe,
452 				0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr");
453 		switch (err) {
454 		case USBD_NORMAL_COMPLETION:
455 			break;
456 
457 		case USBD_INTERRUPTED:
458 			error = EINTR;
459 			break;
460 
461 		case USBD_TIMEOUT:
462 			error = ETIMEDOUT;
463 			break;
464 
465 		default:
466 			error = EIO;
467 			break;
468 		}
469 
470 		if (error != 0)
471 			break;
472 	}
473 
474 	usbd_free_xfer(xfer);
475 
476 	if (-- sc->sc_refcnt < 0)
477 		usb_detach_wakeup(USBDEV(sc->sc_dev));
478 
479 	return (error);
480 }
481 
482 /*
483  * Process ioctl on the endpoint device
484  * XXX FIXME softc locking
485  */
486 
487 Static int
488 ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
489 {
490 	ubtbcmfw_softc_p	sc = NULL;
491 	int			error = 0;
492 
493 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
494 	if (sc == NULL || sc->sc_dying)
495 		return (ENXIO);
496 
497 	if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT)
498 		return (EOPNOTSUPP);
499 
500 	sc->sc_refcnt ++;
501 
502 	switch (cmd) {
503 	case USB_GET_DEVICE_DESC:
504 		*(usb_device_descriptor_t *) data =
505 				*usbd_get_device_descriptor(sc->sc_udev);
506 		break;
507 
508 	default:
509 		error = EINVAL;
510 		break;
511 	}
512 
513 	if (-- sc->sc_refcnt < 0)
514 		usb_detach_wakeup(USBDEV(sc->sc_dev));
515 
516 	return (error);
517 }
518 
519 /*
520  * Poll the endpoint device
521  * XXX FIXME softc locking
522  */
523 
524 Static int
525 ubtbcmfw_poll(struct cdev *dev, int events, usb_proc_ptr p)
526 {
527 	ubtbcmfw_softc_p	sc = NULL;
528 	int			revents = 0;
529 
530 	USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
531 	if (sc == NULL)
532 		return (ENXIO);
533 
534 	switch (UBTBCMFW_ENDPOINT(dev)) {
535 	case UBTBCMFW_INTR_IN:
536 		if (sc->sc_intr_in_pipe != NULL)
537 			revents |= events & (POLLIN | POLLRDNORM);
538 		else
539 			revents = ENXIO;
540 		break;
541 
542 	case UBTBCMFW_BULK_OUT:
543 		if (sc->sc_bulk_out_pipe != NULL)
544 			revents |= events & (POLLOUT | POLLWRNORM);
545 		else
546 			revents = ENXIO;
547 		break;
548 
549 	default:
550 		revents = EOPNOTSUPP;
551 		break;
552 	}
553 
554 	return (revents);
555 }
556 
557