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