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