6 * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
30 * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
34 #include <sys/param.h>
35 #include <sys/systm.h>
38 #include <sys/filio.h>
39 #include <sys/fcntl.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
44 #include <sys/sysctl.h>
47 #include <dev/usb/usb.h>
48 #include <dev/usb/usbdi.h>
49 #include <dev/usb/usbdi_util.h>
54 * Download firmware to BCM2033.
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)
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 */
73 #define UBTBCMFW_CTRL_DEV (1 << 0)
74 #define UBTBCMFW_INTR_IN_DEV (1 << 1)
75 #define UBTBCMFW_BULK_OUT_DEV (1 << 2)
80 typedef struct ubtbcmfw_softc *ubtbcmfw_softc_p;
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
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;
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",
114 USB_DECLARE_DRIVER(ubtbcmfw);
115 DRIVER_MODULE(ubtbcmfw, uhub, ubtbcmfw_driver, ubtbcmfw_devclass,
116 usbd_driver_load, 0);
119 * Probe for a USB Bluetooth device
124 #define USB_PRODUCT_BROADCOM_BCM2033NF 0x2033
126 USB_MATCH_START(ubtbcmfw, uaa);
128 if (uaa->iface != NULL)
129 return (UMATCH_NONE);
131 /* Match the boot device. */
132 if (uaa->vendor == USB_VENDOR_BROADCOM &&
133 uaa->product == USB_PRODUCT_BROADCOM_BCM2033NF)
134 return (UMATCH_VENDOR_PRODUCT);
136 return (UMATCH_NONE);
145 USB_ATTACH_START(ubtbcmfw, sc, uaa);
146 usbd_interface_handle iface;
150 sc->sc_udev = uaa->device;
151 usbd_devinfo(sc->sc_udev, 0, devinfo);
153 printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
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;
159 err = usbd_set_config_no(sc->sc_udev, UBTBCMFW_CONFIG_NO, 1);
161 printf("%s: setting config no failed. %s\n",
162 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
166 err = usbd_device2interface_handle(sc->sc_udev, UBTBCMFW_IFACE_IDX,
169 printf("%s: getting interface handle failed. %s\n",
170 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
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);
178 printf("%s: open intr in failed. %s\n",
179 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
183 err = usbd_open_pipe(iface, UBTBCMFW_BULK_OUT_EP, 0,
184 &sc->sc_bulk_out_pipe);
186 printf("%s: open bulk out failed. %s\n",
187 USBDEVNAME(sc->sc_dev), usbd_errstr(err));
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));
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);
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);
207 USB_ATTACH_SUCCESS_RETURN;
209 ubtbcmfw_detach(self);
211 USB_ATTACH_ERROR_RETURN;
220 USB_DETACH_START(ubtbcmfw, sc);
224 if (-- sc->sc_refcnt >= 0) {
225 if (sc->sc_intr_in_pipe != NULL)
226 usbd_abort_pipe(sc->sc_intr_in_pipe);
228 if (sc->sc_bulk_out_pipe != NULL)
229 usbd_abort_pipe(sc->sc_bulk_out_pipe);
231 usb_detach_wait(USBDEV(sc->sc_dev));
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;
240 if (sc->sc_intr_in_dev != NULL) {
241 destroy_dev(sc->sc_intr_in_dev);
242 sc->sc_intr_in_dev = NULL;
245 if (sc->sc_ctrl_dev != NULL) {
246 destroy_dev(sc->sc_ctrl_dev);
247 sc->sc_ctrl_dev = NULL;
251 if (sc->sc_intr_in_pipe != NULL) {
252 usbd_close_pipe(sc->sc_intr_in_pipe);
253 sc->sc_intr_in_pipe = NULL;
256 if (sc->sc_bulk_out_pipe != NULL) {
257 usbd_close_pipe(sc->sc_bulk_out_pipe);
258 sc->sc_intr_in_pipe = NULL;
265 * Open endpoint device
266 * XXX FIXME softc locking
270 ubtbcmfw_open(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
272 ubtbcmfw_softc_p sc = NULL;
275 /* checks for sc != NULL */
276 USB_GET_SC_OPEN(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
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;
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;
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;
317 * Close endpoint device
318 * XXX FIXME softc locking
322 ubtbcmfw_close(struct cdev *dev, int flag, int mode, usb_proc_ptr p)
324 ubtbcmfw_softc_p sc = NULL;
326 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
330 switch (UBTBCMFW_ENDPOINT(dev)) {
331 case USB_CONTROL_ENDPOINT:
332 sc->sc_flags &= ~UBTBCMFW_CTRL_DEV;
335 case UBTBCMFW_INTR_IN:
336 if (sc->sc_intr_in_pipe != NULL)
337 usbd_abort_pipe(sc->sc_intr_in_pipe);
339 sc->sc_flags &= ~UBTBCMFW_INTR_IN_DEV;
342 case UBTBCMFW_BULK_OUT:
343 if (sc->sc_bulk_out_pipe != NULL)
344 usbd_abort_pipe(sc->sc_bulk_out_pipe);
346 sc->sc_flags &= ~UBTBCMFW_BULK_OUT_DEV;
354 * Read from the endpoint device
355 * XXX FIXME softc locking
359 ubtbcmfw_read(struct cdev *dev, struct uio *uio, int flag)
361 ubtbcmfw_softc_p sc = NULL;
362 u_int8_t buf[UBTBCMFW_BSIZE];
363 usbd_xfer_handle xfer;
365 int n, tn, error = 0;
367 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
368 if (sc == NULL || sc->sc_dying)
371 if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_INTR_IN)
373 if (sc->sc_intr_in_pipe == NULL)
376 xfer = usbd_alloc_xfer(sc->sc_udev);
382 while ((n = min(sizeof(buf), uio->uio_resid)) != 0) {
384 err = usbd_bulk_transfer(xfer, sc->sc_intr_in_pipe,
385 USBD_SHORT_XFER_OK, USBD_DEFAULT_TIMEOUT,
388 case USBD_NORMAL_COMPLETION:
389 error = uiomove(buf, tn, uio);
392 case USBD_INTERRUPTED:
405 if (error != 0 || tn < n)
409 usbd_free_xfer(xfer);
411 if (-- sc->sc_refcnt < 0)
412 usb_detach_wakeup(USBDEV(sc->sc_dev));
418 * Write into the endpoint device
419 * XXX FIXME softc locking
423 ubtbcmfw_write(struct cdev *dev, struct uio *uio, int flag)
425 ubtbcmfw_softc_p sc = NULL;
426 u_int8_t buf[UBTBCMFW_BSIZE];
427 usbd_xfer_handle xfer;
431 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
432 if (sc == NULL || sc->sc_dying)
435 if (UBTBCMFW_ENDPOINT(dev) != UBTBCMFW_BULK_OUT)
437 if (sc->sc_bulk_out_pipe == NULL)
440 xfer = usbd_alloc_xfer(sc->sc_udev);
446 while ((n = min(sizeof(buf), uio->uio_resid)) != 0) {
447 error = uiomove(buf, n, uio);
451 err = usbd_bulk_transfer(xfer, sc->sc_bulk_out_pipe,
452 0, USBD_DEFAULT_TIMEOUT, buf, &n, "bcmwr");
454 case USBD_NORMAL_COMPLETION:
457 case USBD_INTERRUPTED:
474 usbd_free_xfer(xfer);
476 if (-- sc->sc_refcnt < 0)
477 usb_detach_wakeup(USBDEV(sc->sc_dev));
483 * Process ioctl on the endpoint device
484 * XXX FIXME softc locking
488 ubtbcmfw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p)
490 ubtbcmfw_softc_p sc = NULL;
493 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
494 if (sc == NULL || sc->sc_dying)
497 if (UBTBCMFW_ENDPOINT(dev) != USB_CONTROL_ENDPOINT)
503 case USB_GET_DEVICE_DESC:
504 *(usb_device_descriptor_t *) data =
505 *usbd_get_device_descriptor(sc->sc_udev);
513 if (-- sc->sc_refcnt < 0)
514 usb_detach_wakeup(USBDEV(sc->sc_dev));
520 * Poll the endpoint device
521 * XXX FIXME softc locking
525 ubtbcmfw_poll(struct cdev *dev, int events, usb_proc_ptr p)
527 ubtbcmfw_softc_p sc = NULL;
530 USB_GET_SC(ubtbcmfw, UBTBCMFW_UNIT(dev), sc);
534 switch (UBTBCMFW_ENDPOINT(dev)) {
535 case UBTBCMFW_INTR_IN:
536 if (sc->sc_intr_in_pipe != NULL)
537 revents |= events & (POLLIN | POLLRDNORM);
542 case UBTBCMFW_BULK_OUT:
543 if (sc->sc_bulk_out_pipe != NULL)
544 revents |= events & (POLLOUT | POLLWRNORM);
550 revents = EOPNOTSUPP;