Commit | Line | Data |
---|---|---|
b06ebda0 MD |
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 $ | |
5a975a3d | 32 | * $DragonFly: src/sys/netgraph7/bluetooth/drivers/ubtbcmfw/ubtbcmfw.c,v 1.2 2008/06/26 23:05:40 dillon Exp $ |
b06ebda0 MD |
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 | ||
e54488bb | 404 | while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) { |
b06ebda0 MD |
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: | |
e54488bb | 411 | error = uiomove(buf, (size_t)tn, uio); |
b06ebda0 MD |
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 | ||
e54488bb MD |
468 | while ((n = (int)szmin(sizeof(buf), uio->uio_resid)) != 0) { |
469 | error = uiomove(buf, (size_t)n, uio); | |
b06ebda0 MD |
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 | } |