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