1 | /* $NetBSD: uhid.c,v 1.98 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) at |
9 | * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna.com.au). |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions and the following disclaimer. |
16 | * 2. Redistributions in binary form must reproduce the above copyright |
17 | * notice, this list of conditions and the following disclaimer in the |
18 | * documentation and/or other materials provided with the distribution. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
21 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
24 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ |
32 | |
33 | /* |
34 | * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.98 2016/07/07 06:55:42 msaitoh Exp $" ); |
39 | |
40 | #ifdef _KERNEL_OPT |
41 | #include "opt_compat_netbsd.h" |
42 | #include "opt_usb.h" |
43 | #endif |
44 | |
45 | #include <sys/param.h> |
46 | #include <sys/systm.h> |
47 | #include <sys/kernel.h> |
48 | #include <sys/kmem.h> |
49 | #include <sys/signalvar.h> |
50 | #include <sys/device.h> |
51 | #include <sys/ioctl.h> |
52 | #include <sys/conf.h> |
53 | #include <sys/tty.h> |
54 | #include <sys/file.h> |
55 | #include <sys/select.h> |
56 | #include <sys/proc.h> |
57 | #include <sys/vnode.h> |
58 | #include <sys/poll.h> |
59 | #include <sys/intr.h> |
60 | |
61 | #include <dev/usb/usb.h> |
62 | #include <dev/usb/usbhid.h> |
63 | |
64 | #include <dev/usb/usbdevs.h> |
65 | #include <dev/usb/usbdi.h> |
66 | #include <dev/usb/usbdi_util.h> |
67 | #include <dev/usb/hid.h> |
68 | #include <dev/usb/usb_quirks.h> |
69 | |
70 | #include <dev/usb/uhidev.h> |
71 | |
72 | #ifdef UHID_DEBUG |
73 | #define DPRINTF(x) if (uhiddebug) printf x |
74 | #define DPRINTFN(n,x) if (uhiddebug>(n)) printf x |
75 | int uhiddebug = 0; |
76 | #else |
77 | #define DPRINTF(x) |
78 | #define DPRINTFN(n,x) |
79 | #endif |
80 | |
81 | struct uhid_softc { |
82 | struct uhidev sc_hdev; |
83 | |
84 | kmutex_t sc_access_lock; /* serialises syscall accesses */ |
85 | kmutex_t sc_lock; /* protects refcnt, others */ |
86 | kcondvar_t sc_cv; |
87 | kcondvar_t sc_detach_cv; |
88 | |
89 | int sc_isize; |
90 | int sc_osize; |
91 | int sc_fsize; |
92 | |
93 | u_char *sc_obuf; |
94 | |
95 | struct clist sc_q; /* protected by sc_lock */ |
96 | struct selinfo sc_rsel; |
97 | proc_t *sc_async; /* process that wants SIGIO */ |
98 | void *sc_sih; |
99 | u_char sc_state; /* driver state */ |
100 | #define UHID_ASLP 0x01 /* waiting for device data */ |
101 | #define UHID_IMMED 0x02 /* return read data immediately */ |
102 | |
103 | int sc_refcnt; |
104 | u_char sc_dying; |
105 | }; |
106 | |
107 | #define UHIDUNIT(dev) (minor(dev)) |
108 | #define UHID_CHUNK 128 /* chunk size for read */ |
109 | #define UHID_BSIZE 1020 /* buffer size */ |
110 | |
111 | dev_type_open(uhidopen); |
112 | dev_type_close(uhidclose); |
113 | dev_type_read(uhidread); |
114 | dev_type_write(uhidwrite); |
115 | dev_type_ioctl(uhidioctl); |
116 | dev_type_poll(uhidpoll); |
117 | dev_type_kqfilter(uhidkqfilter); |
118 | |
119 | const struct cdevsw uhid_cdevsw = { |
120 | .d_open = uhidopen, |
121 | .d_close = uhidclose, |
122 | .d_read = uhidread, |
123 | .d_write = uhidwrite, |
124 | .d_ioctl = uhidioctl, |
125 | .d_stop = nostop, |
126 | .d_tty = notty, |
127 | .d_poll = uhidpoll, |
128 | .d_mmap = nommap, |
129 | .d_kqfilter = uhidkqfilter, |
130 | .d_discard = nodiscard, |
131 | .d_flag = D_OTHER |
132 | }; |
133 | |
134 | Static void uhid_intr(struct uhidev *, void *, u_int); |
135 | Static void uhid_softintr(void *); |
136 | |
137 | Static int uhid_do_read(struct uhid_softc *, struct uio *, int); |
138 | Static int uhid_do_write(struct uhid_softc *, struct uio *, int); |
139 | Static int uhid_do_ioctl(struct uhid_softc*, u_long, void *, int, struct lwp *); |
140 | |
141 | int uhid_match(device_t, cfdata_t, void *); |
142 | void uhid_attach(device_t, device_t, void *); |
143 | int uhid_detach(device_t, int); |
144 | int uhid_activate(device_t, enum devact); |
145 | extern struct cfdriver uhid_cd; |
146 | CFATTACH_DECL_NEW(uhid, sizeof(struct uhid_softc), uhid_match, uhid_attach, |
147 | uhid_detach, uhid_activate); |
148 | |
149 | int |
150 | uhid_match(device_t parent, cfdata_t match, void *aux) |
151 | { |
152 | #ifdef UHID_DEBUG |
153 | struct uhidev_attach_arg *uha = aux; |
154 | #endif |
155 | |
156 | DPRINTF(("uhid_match: report=%d\n" , uha->reportid)); |
157 | |
158 | if (match->cf_flags & 1) |
159 | return UMATCH_HIGHEST; |
160 | else |
161 | return UMATCH_IFACECLASS_GENERIC; |
162 | } |
163 | |
164 | void |
165 | uhid_attach(device_t parent, device_t self, void *aux) |
166 | { |
167 | struct uhid_softc *sc = device_private(self); |
168 | struct uhidev_attach_arg *uha = aux; |
169 | int size, repid; |
170 | void *desc; |
171 | |
172 | sc->sc_hdev.sc_dev = self; |
173 | selinit(&sc->sc_rsel); |
174 | sc->sc_hdev.sc_intr = uhid_intr; |
175 | sc->sc_hdev.sc_parent = uha->parent; |
176 | sc->sc_hdev.sc_report_id = uha->reportid; |
177 | sc->sc_sih = softint_establish(SOFTINT_CLOCK, uhid_softintr, sc); |
178 | |
179 | uhidev_get_report_desc(uha->parent, &desc, &size); |
180 | repid = uha->reportid; |
181 | sc->sc_isize = hid_report_size(desc, size, hid_input, repid); |
182 | sc->sc_osize = hid_report_size(desc, size, hid_output, repid); |
183 | sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid); |
184 | |
185 | aprint_naive("\n" ); |
186 | aprint_normal(": input=%d, output=%d, feature=%d\n" , |
187 | sc->sc_isize, sc->sc_osize, sc->sc_fsize); |
188 | |
189 | mutex_init(&sc->sc_access_lock, MUTEX_DEFAULT, IPL_NONE); |
190 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); |
191 | cv_init(&sc->sc_cv, "uhidrea" ); |
192 | cv_init(&sc->sc_detach_cv, "uhiddet" ); |
193 | |
194 | if (!pmf_device_register(self, NULL, NULL)) |
195 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
196 | |
197 | return; |
198 | } |
199 | |
200 | int |
201 | uhid_activate(device_t self, enum devact act) |
202 | { |
203 | struct uhid_softc *sc = device_private(self); |
204 | |
205 | switch (act) { |
206 | case DVACT_DEACTIVATE: |
207 | sc->sc_dying = 1; |
208 | return 0; |
209 | default: |
210 | return EOPNOTSUPP; |
211 | } |
212 | } |
213 | |
214 | int |
215 | uhid_detach(device_t self, int flags) |
216 | { |
217 | struct uhid_softc *sc = device_private(self); |
218 | int maj, mn; |
219 | |
220 | DPRINTF(("uhid_detach: sc=%p flags=%d\n" , sc, flags)); |
221 | |
222 | sc->sc_dying = 1; |
223 | |
224 | pmf_device_deregister(self); |
225 | |
226 | mutex_enter(&sc->sc_lock); |
227 | if (sc->sc_hdev.sc_state & UHIDEV_OPEN) { |
228 | if (--sc->sc_refcnt >= 0) { |
229 | /* Wake everyone */ |
230 | cv_broadcast(&sc->sc_cv); |
231 | /* Wait for processes to go away. */ |
232 | usb_detach_wait(sc->sc_hdev.sc_dev, |
233 | &sc->sc_detach_cv, &sc->sc_lock); |
234 | } |
235 | } |
236 | mutex_exit(&sc->sc_lock); |
237 | |
238 | /* locate the major number */ |
239 | maj = cdevsw_lookup_major(&uhid_cdevsw); |
240 | |
241 | /* Nuke the vnodes for any open instances (calls close). */ |
242 | mn = device_unit(self); |
243 | vdevgone(maj, mn, mn, VCHR); |
244 | |
245 | #if 0 |
246 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, |
247 | sc->sc_hdev.sc_parent->sc_udev, sc->sc_hdev.sc_dev); |
248 | #endif |
249 | cv_destroy(&sc->sc_cv); |
250 | cv_destroy(&sc->sc_detach_cv); |
251 | mutex_destroy(&sc->sc_lock); |
252 | mutex_destroy(&sc->sc_access_lock); |
253 | seldestroy(&sc->sc_rsel); |
254 | softint_disestablish(sc->sc_sih); |
255 | |
256 | return 0; |
257 | } |
258 | |
259 | void |
260 | uhid_intr(struct uhidev *addr, void *data, u_int len) |
261 | { |
262 | struct uhid_softc *sc = (struct uhid_softc *)addr; |
263 | |
264 | #ifdef UHID_DEBUG |
265 | if (uhiddebug > 5) { |
266 | uint32_t i; |
267 | |
268 | DPRINTF(("uhid_intr: data =" )); |
269 | for (i = 0; i < len; i++) |
270 | DPRINTF((" %02x" , ((u_char *)data)[i])); |
271 | DPRINTF(("\n" )); |
272 | } |
273 | #endif |
274 | |
275 | mutex_enter(&sc->sc_lock); |
276 | (void)b_to_q(data, len, &sc->sc_q); |
277 | |
278 | if (sc->sc_state & UHID_ASLP) { |
279 | sc->sc_state &= ~UHID_ASLP; |
280 | DPRINTFN(5, ("uhid_intr: waking %p\n" , &sc->sc_q)); |
281 | cv_broadcast(&sc->sc_cv); |
282 | } |
283 | selnotify(&sc->sc_rsel, 0, 0); |
284 | if (sc->sc_async != NULL) { |
285 | DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n" , sc->sc_async)); |
286 | softint_schedule(sc->sc_sih); |
287 | } |
288 | mutex_exit(&sc->sc_lock); |
289 | } |
290 | |
291 | void |
292 | uhid_softintr(void *cookie) |
293 | { |
294 | struct uhid_softc *sc; |
295 | |
296 | sc = cookie; |
297 | |
298 | mutex_enter(proc_lock); |
299 | if (sc->sc_async != NULL) |
300 | psignal(sc->sc_async, SIGIO); |
301 | mutex_exit(proc_lock); |
302 | } |
303 | |
304 | int |
305 | uhidopen(dev_t dev, int flag, int mode, struct lwp *l) |
306 | { |
307 | struct uhid_softc *sc; |
308 | int error; |
309 | |
310 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
311 | if (sc == NULL) |
312 | return ENXIO; |
313 | |
314 | DPRINTF(("uhidopen: sc=%p\n" , sc)); |
315 | |
316 | if (sc->sc_dying) |
317 | return ENXIO; |
318 | |
319 | mutex_enter(&sc->sc_access_lock); |
320 | |
321 | /* |
322 | * uhid interrupts aren't enabled yet, so setup sc_q now, as |
323 | * long as they're not already allocated. |
324 | */ |
325 | if (sc->sc_hdev.sc_state & UHIDEV_OPEN) { |
326 | mutex_exit(&sc->sc_access_lock); |
327 | return EBUSY; |
328 | } |
329 | if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { |
330 | mutex_exit(&sc->sc_access_lock); |
331 | return ENOMEM; |
332 | } |
333 | |
334 | error = uhidev_open(&sc->sc_hdev); |
335 | if (error) { |
336 | clfree(&sc->sc_q); |
337 | mutex_exit(&sc->sc_access_lock); |
338 | return error; |
339 | } |
340 | mutex_exit(&sc->sc_access_lock); |
341 | |
342 | if (sc->sc_osize > 0) |
343 | sc->sc_obuf = kmem_alloc(sc->sc_osize, KM_SLEEP); |
344 | else |
345 | sc->sc_obuf = NULL; |
346 | sc->sc_state &= ~UHID_IMMED; |
347 | |
348 | mutex_enter(proc_lock); |
349 | sc->sc_async = NULL; |
350 | mutex_exit(proc_lock); |
351 | |
352 | return 0; |
353 | } |
354 | |
355 | int |
356 | uhidclose(dev_t dev, int flag, int mode, struct lwp *l) |
357 | { |
358 | struct uhid_softc *sc; |
359 | |
360 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
361 | |
362 | DPRINTF(("uhidclose: sc=%p\n" , sc)); |
363 | |
364 | mutex_enter(proc_lock); |
365 | sc->sc_async = NULL; |
366 | mutex_exit(proc_lock); |
367 | |
368 | mutex_enter(&sc->sc_access_lock); |
369 | |
370 | uhidev_stop(&sc->sc_hdev); |
371 | |
372 | clfree(&sc->sc_q); |
373 | if (sc->sc_osize > 0) |
374 | kmem_free(sc->sc_obuf, sc->sc_osize); |
375 | |
376 | uhidev_close(&sc->sc_hdev); |
377 | |
378 | mutex_exit(&sc->sc_access_lock); |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | int |
384 | uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) |
385 | { |
386 | int error = 0; |
387 | int ; |
388 | size_t length; |
389 | u_char buffer[UHID_CHUNK]; |
390 | usbd_status err; |
391 | |
392 | DPRINTFN(1, ("uhidread\n" )); |
393 | if (sc->sc_state & UHID_IMMED) { |
394 | DPRINTFN(1, ("uhidread immed\n" )); |
395 | extra = sc->sc_hdev.sc_report_id != 0; |
396 | err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT, |
397 | buffer, sc->sc_isize + extra); |
398 | if (err) |
399 | return EIO; |
400 | return uiomove(buffer+extra, sc->sc_isize, uio); |
401 | } |
402 | |
403 | mutex_enter(&sc->sc_lock); |
404 | while (sc->sc_q.c_cc == 0) { |
405 | if (flag & IO_NDELAY) { |
406 | mutex_exit(&sc->sc_lock); |
407 | return EWOULDBLOCK; |
408 | } |
409 | sc->sc_state |= UHID_ASLP; |
410 | DPRINTFN(5, ("uhidread: sleep on %p\n" , &sc->sc_q)); |
411 | error = cv_wait_sig(&sc->sc_cv, &sc->sc_lock); |
412 | DPRINTFN(5, ("uhidread: woke, error=%d\n" , error)); |
413 | if (sc->sc_dying) |
414 | error = EIO; |
415 | if (error) { |
416 | sc->sc_state &= ~UHID_ASLP; |
417 | break; |
418 | } |
419 | } |
420 | |
421 | /* Transfer as many chunks as possible. */ |
422 | while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { |
423 | length = min(sc->sc_q.c_cc, uio->uio_resid); |
424 | if (length > sizeof(buffer)) |
425 | length = sizeof(buffer); |
426 | |
427 | /* Remove a small chunk from the input queue. */ |
428 | (void) q_to_b(&sc->sc_q, buffer, length); |
429 | DPRINTFN(5, ("uhidread: got %lu chars\n" , (u_long)length)); |
430 | |
431 | /* Copy the data to the user process. */ |
432 | mutex_exit(&sc->sc_lock); |
433 | if ((error = uiomove(buffer, length, uio)) != 0) |
434 | return error; |
435 | mutex_enter(&sc->sc_lock); |
436 | } |
437 | |
438 | mutex_exit(&sc->sc_lock); |
439 | return error; |
440 | } |
441 | |
442 | int |
443 | uhidread(dev_t dev, struct uio *uio, int flag) |
444 | { |
445 | struct uhid_softc *sc; |
446 | int error; |
447 | |
448 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
449 | |
450 | mutex_enter(&sc->sc_lock); |
451 | sc->sc_refcnt++; |
452 | mutex_exit(&sc->sc_lock); |
453 | |
454 | mutex_enter(&sc->sc_access_lock); |
455 | error = uhid_do_read(sc, uio, flag); |
456 | mutex_exit(&sc->sc_access_lock); |
457 | |
458 | mutex_enter(&sc->sc_lock); |
459 | if (--sc->sc_refcnt < 0) |
460 | usb_detach_broadcast(sc->sc_hdev.sc_dev, &sc->sc_detach_cv); |
461 | mutex_exit(&sc->sc_lock); |
462 | return error; |
463 | } |
464 | |
465 | int |
466 | uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) |
467 | { |
468 | int error; |
469 | int size; |
470 | usbd_status err; |
471 | |
472 | DPRINTFN(1, ("uhidwrite\n" )); |
473 | |
474 | if (sc->sc_dying) |
475 | return EIO; |
476 | |
477 | size = sc->sc_osize; |
478 | error = 0; |
479 | if (uio->uio_resid != size || size == 0) |
480 | return EINVAL; |
481 | error = uiomove(sc->sc_obuf, size, uio); |
482 | if (!error) { |
483 | err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, |
484 | sc->sc_obuf, size); |
485 | if (err) |
486 | error = EIO; |
487 | } |
488 | |
489 | return error; |
490 | } |
491 | |
492 | int |
493 | uhidwrite(dev_t dev, struct uio *uio, int flag) |
494 | { |
495 | struct uhid_softc *sc; |
496 | int error; |
497 | |
498 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
499 | |
500 | mutex_enter(&sc->sc_lock); |
501 | sc->sc_refcnt++; |
502 | mutex_exit(&sc->sc_lock); |
503 | |
504 | mutex_enter(&sc->sc_access_lock); |
505 | error = uhid_do_write(sc, uio, flag); |
506 | mutex_exit(&sc->sc_access_lock); |
507 | |
508 | mutex_enter(&sc->sc_lock); |
509 | if (--sc->sc_refcnt < 0) |
510 | usb_detach_broadcast(sc->sc_hdev.sc_dev, &sc->sc_detach_cv); |
511 | mutex_exit(&sc->sc_lock); |
512 | return error; |
513 | } |
514 | |
515 | int |
516 | uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, void *addr, |
517 | int flag, struct lwp *l) |
518 | { |
519 | struct usb_ctl_report_desc *rd; |
520 | struct usb_ctl_report *re; |
521 | u_char buffer[UHID_CHUNK]; |
522 | int size, ; |
523 | usbd_status err; |
524 | void *desc; |
525 | |
526 | DPRINTFN(2, ("uhidioctl: cmd=%lx\n" , cmd)); |
527 | |
528 | if (sc->sc_dying) |
529 | return EIO; |
530 | |
531 | switch (cmd) { |
532 | case FIONBIO: |
533 | /* All handled in the upper FS layer. */ |
534 | break; |
535 | |
536 | case FIOASYNC: |
537 | mutex_enter(proc_lock); |
538 | if (*(int *)addr) { |
539 | if (sc->sc_async != NULL) |
540 | return EBUSY; |
541 | sc->sc_async = l->l_proc; |
542 | DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n" , l->l_proc)); |
543 | } else |
544 | sc->sc_async = NULL; |
545 | mutex_exit(proc_lock); |
546 | break; |
547 | |
548 | /* XXX this is not the most general solution. */ |
549 | case TIOCSPGRP: |
550 | mutex_enter(proc_lock); |
551 | if (sc->sc_async == NULL) { |
552 | mutex_exit(proc_lock); |
553 | return EINVAL; |
554 | } |
555 | if (*(int *)addr != sc->sc_async->p_pgid) { |
556 | mutex_exit(proc_lock); |
557 | return EPERM; |
558 | } |
559 | mutex_exit(proc_lock); |
560 | break; |
561 | |
562 | case FIOSETOWN: |
563 | mutex_enter(proc_lock); |
564 | if (sc->sc_async == NULL) { |
565 | mutex_exit(proc_lock); |
566 | return EINVAL; |
567 | } |
568 | if (-*(int *)addr != sc->sc_async->p_pgid |
569 | && *(int *)addr != sc->sc_async->p_pid) { |
570 | mutex_exit(proc_lock); |
571 | return EPERM; |
572 | } |
573 | mutex_exit(proc_lock); |
574 | break; |
575 | |
576 | case USB_GET_REPORT_DESC: |
577 | uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); |
578 | rd = (struct usb_ctl_report_desc *)addr; |
579 | size = min(size, sizeof(rd->ucrd_data)); |
580 | rd->ucrd_size = size; |
581 | memcpy(rd->ucrd_data, desc, size); |
582 | break; |
583 | |
584 | case USB_SET_IMMED: |
585 | if (*(int *)addr) { |
586 | extra = sc->sc_hdev.sc_report_id != 0; |
587 | err = uhidev_get_report(&sc->sc_hdev, UHID_INPUT_REPORT, |
588 | buffer, sc->sc_isize + extra); |
589 | if (err) |
590 | return EOPNOTSUPP; |
591 | |
592 | sc->sc_state |= UHID_IMMED; |
593 | } else |
594 | sc->sc_state &= ~UHID_IMMED; |
595 | break; |
596 | |
597 | case USB_GET_REPORT: |
598 | re = (struct usb_ctl_report *)addr; |
599 | switch (re->ucr_report) { |
600 | case UHID_INPUT_REPORT: |
601 | size = sc->sc_isize; |
602 | break; |
603 | case UHID_OUTPUT_REPORT: |
604 | size = sc->sc_osize; |
605 | break; |
606 | case UHID_FEATURE_REPORT: |
607 | size = sc->sc_fsize; |
608 | break; |
609 | default: |
610 | return EINVAL; |
611 | } |
612 | extra = sc->sc_hdev.sc_report_id != 0; |
613 | err = uhidev_get_report(&sc->sc_hdev, re->ucr_report, |
614 | re->ucr_data, size + extra); |
615 | if (extra) |
616 | memcpy(re->ucr_data, re->ucr_data+1, size); |
617 | if (err) |
618 | return EIO; |
619 | break; |
620 | |
621 | case USB_SET_REPORT: |
622 | re = (struct usb_ctl_report *)addr; |
623 | switch (re->ucr_report) { |
624 | case UHID_INPUT_REPORT: |
625 | size = sc->sc_isize; |
626 | break; |
627 | case UHID_OUTPUT_REPORT: |
628 | size = sc->sc_osize; |
629 | break; |
630 | case UHID_FEATURE_REPORT: |
631 | size = sc->sc_fsize; |
632 | break; |
633 | default: |
634 | return EINVAL; |
635 | } |
636 | err = uhidev_set_report(&sc->sc_hdev, re->ucr_report, |
637 | re->ucr_data, size); |
638 | if (err) |
639 | return EIO; |
640 | break; |
641 | |
642 | case USB_GET_REPORT_ID: |
643 | *(int *)addr = sc->sc_hdev.sc_report_id; |
644 | break; |
645 | |
646 | case USB_GET_DEVICE_DESC: |
647 | *(usb_device_descriptor_t *)addr = |
648 | *usbd_get_device_descriptor(sc->sc_hdev.sc_parent->sc_udev); |
649 | break; |
650 | |
651 | case USB_GET_DEVICEINFO: |
652 | usbd_fill_deviceinfo(sc->sc_hdev.sc_parent->sc_udev, |
653 | (struct usb_device_info *)addr, 0); |
654 | break; |
655 | #ifdef COMPAT_30 |
656 | case USB_GET_DEVICEINFO_OLD: |
657 | usbd_fill_deviceinfo_old(sc->sc_hdev.sc_parent->sc_udev, |
658 | (struct usb_device_info_old *)addr, 0); |
659 | |
660 | break; |
661 | #endif |
662 | case USB_GET_STRING_DESC: |
663 | { |
664 | struct usb_string_desc *si = (struct usb_string_desc *)addr; |
665 | err = usbd_get_string_desc(sc->sc_hdev.sc_parent->sc_udev, |
666 | si->usd_string_index, |
667 | si->usd_language_id, &si->usd_desc, &size); |
668 | if (err) |
669 | return EINVAL; |
670 | break; |
671 | } |
672 | |
673 | default: |
674 | return EINVAL; |
675 | } |
676 | return 0; |
677 | } |
678 | |
679 | int |
680 | uhidioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) |
681 | { |
682 | struct uhid_softc *sc; |
683 | int error; |
684 | |
685 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
686 | if (sc == NULL) |
687 | return ENXIO; |
688 | |
689 | if (sc->sc_dying) |
690 | return EIO; |
691 | |
692 | mutex_enter(&sc->sc_lock); |
693 | sc->sc_refcnt++; |
694 | mutex_exit(&sc->sc_lock); |
695 | |
696 | mutex_enter(&sc->sc_access_lock); |
697 | error = uhid_do_ioctl(sc, cmd, addr, flag, l); |
698 | mutex_exit(&sc->sc_access_lock); |
699 | |
700 | mutex_enter(&sc->sc_lock); |
701 | if (--sc->sc_refcnt < 0) |
702 | usb_detach_broadcast(sc->sc_hdev.sc_dev, &sc->sc_detach_cv); |
703 | mutex_exit(&sc->sc_lock); |
704 | return error; |
705 | } |
706 | |
707 | int |
708 | uhidpoll(dev_t dev, int events, struct lwp *l) |
709 | { |
710 | struct uhid_softc *sc; |
711 | int revents = 0; |
712 | |
713 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
714 | if (sc == NULL) |
715 | return ENXIO; |
716 | |
717 | if (sc->sc_dying) |
718 | return EIO; |
719 | |
720 | mutex_enter(&sc->sc_lock); |
721 | if (events & (POLLOUT | POLLWRNORM)) |
722 | revents |= events & (POLLOUT | POLLWRNORM); |
723 | if (events & (POLLIN | POLLRDNORM)) { |
724 | if (sc->sc_q.c_cc > 0) |
725 | revents |= events & (POLLIN | POLLRDNORM); |
726 | else |
727 | selrecord(l, &sc->sc_rsel); |
728 | } |
729 | mutex_exit(&sc->sc_lock); |
730 | |
731 | return revents; |
732 | } |
733 | |
734 | static void |
735 | filt_uhidrdetach(struct knote *kn) |
736 | { |
737 | struct uhid_softc *sc = kn->kn_hook; |
738 | |
739 | mutex_enter(&sc->sc_lock); |
740 | SLIST_REMOVE(&sc->sc_rsel.sel_klist, kn, knote, kn_selnext); |
741 | mutex_exit(&sc->sc_lock); |
742 | } |
743 | |
744 | static int |
745 | filt_uhidread(struct knote *kn, long hint) |
746 | { |
747 | struct uhid_softc *sc = kn->kn_hook; |
748 | |
749 | kn->kn_data = sc->sc_q.c_cc; |
750 | return kn->kn_data > 0; |
751 | } |
752 | |
753 | static const struct filterops uhidread_filtops = |
754 | { 1, NULL, filt_uhidrdetach, filt_uhidread }; |
755 | |
756 | static const struct filterops uhid_seltrue_filtops = |
757 | { 1, NULL, filt_uhidrdetach, filt_seltrue }; |
758 | |
759 | int |
760 | uhidkqfilter(dev_t dev, struct knote *kn) |
761 | { |
762 | struct uhid_softc *sc; |
763 | struct klist *klist; |
764 | |
765 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
766 | |
767 | if (sc->sc_dying) |
768 | return ENXIO; |
769 | |
770 | switch (kn->kn_filter) { |
771 | case EVFILT_READ: |
772 | klist = &sc->sc_rsel.sel_klist; |
773 | kn->kn_fop = &uhidread_filtops; |
774 | break; |
775 | |
776 | case EVFILT_WRITE: |
777 | klist = &sc->sc_rsel.sel_klist; |
778 | kn->kn_fop = &uhid_seltrue_filtops; |
779 | break; |
780 | |
781 | default: |
782 | return EINVAL; |
783 | } |
784 | |
785 | kn->kn_hook = sc; |
786 | |
787 | mutex_enter(&sc->sc_lock); |
788 | SLIST_INSERT_HEAD(klist, kn, kn_selnext); |
789 | mutex_exit(&sc->sc_lock); |
790 | |
791 | return 0; |
792 | } |
793 | |