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