Merge from vendor branch NETGRAPH:
[dragonfly.git] / sys / netgraph7 / bluetooth / drivers / ubtbcmfw / ubtbcmfw.c
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: src/sys/netgraph/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.18 2007/06/23 04:34:38 imp Exp $
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 }