File: | dev/usb/uhid.c |
Warning: | line 332, column 2 Value stored to 'error' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: uhid.c,v 1.88 2021/11/15 15:36:24 anton Exp $ */ |
2 | /* $NetBSD: uhid.c,v 1.57 2003/03/11 16:44:00 augustss Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (c) 1998 The NetBSD Foundation, Inc. |
6 | * All rights reserved. |
7 | * |
8 | * This code is derived from software contributed to The NetBSD Foundation |
9 | * by Lennart Augustsson (lennart@augustsson.net) at |
10 | * Carlstedt Research & Technology. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | /* |
35 | * HID spec: https://www.usb.org/sites/default/files/hid1_11.pdf |
36 | */ |
37 | |
38 | #include "fido.h" |
39 | #include "ujoy.h" |
40 | |
41 | #include <sys/param.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/kernel.h> |
44 | #include <sys/malloc.h> |
45 | #include <sys/signalvar.h> |
46 | #include <sys/device.h> |
47 | #include <sys/ioctl.h> |
48 | #include <sys/conf.h> |
49 | #include <sys/tty.h> |
50 | #include <sys/selinfo.h> |
51 | #include <sys/proc.h> |
52 | #include <sys/vnode.h> |
53 | #include <sys/poll.h> |
54 | |
55 | #include <dev/usb/usb.h> |
56 | #include <dev/usb/usbhid.h> |
57 | |
58 | #include <dev/usb/usbdevs.h> |
59 | #include <dev/usb/usbdi.h> |
60 | #include <dev/usb/usbdi_util.h> |
61 | |
62 | #include <dev/usb/uhidev.h> |
63 | #include <dev/usb/uhid.h> |
64 | |
65 | #ifdef UHID_DEBUG |
66 | #define DPRINTF(x) do { if (uhiddebug) printf x; } while (0) |
67 | #define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0) |
68 | int uhiddebug = 0; |
69 | #else |
70 | #define DPRINTF(x) |
71 | #define DPRINTFN(n,x) |
72 | #endif |
73 | |
74 | int uhid_match(struct device *, void *, void *); |
75 | |
76 | struct cfdriver uhid_cd = { |
77 | NULL((void *)0), "uhid", DV_DULL |
78 | }; |
79 | |
80 | const struct cfattach uhid_ca = { |
81 | sizeof(struct uhid_softc), |
82 | uhid_match, |
83 | uhid_attach, |
84 | uhid_detach, |
85 | }; |
86 | |
87 | struct uhid_softc * |
88 | uhid_lookup(dev_t dev) |
89 | { |
90 | struct uhid_softc *sc = NULL((void *)0); |
91 | struct cdevsw *cdev; |
92 | struct cfdriver *cd; |
93 | |
94 | cdev = &cdevsw[major(dev)(((unsigned)(dev) >> 8) & 0xff)]; |
95 | if (cdev->d_open == uhidopen) |
96 | cd = &uhid_cd; |
97 | #if NFIDO1 > 0 |
98 | else if (cdev->d_open == fidoopen) |
99 | cd = &fido_cd; |
100 | #endif |
101 | #if NUJOY1 > 0 |
102 | else if (cdev->d_open == ujoyopen) |
103 | cd = &ujoy_cd; |
104 | #endif |
105 | else |
106 | return (NULL((void *)0)); |
107 | if (UHIDUNIT(dev)(((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8))) < cd->cd_ndevs) |
108 | sc = cd->cd_devs[UHIDUNIT(dev)(((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8)))]; |
109 | |
110 | return (sc); |
111 | } |
112 | |
113 | int |
114 | uhid_match(struct device *parent, void *match, void *aux) |
115 | { |
116 | struct uhidev_attach_arg *uha = aux; |
117 | |
118 | if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)((uha)->claimed != ((void *)0))) |
119 | return (UMATCH_NONE0); |
120 | |
121 | return (UMATCH_IFACECLASS_GENERIC2); |
122 | } |
123 | |
124 | void |
125 | uhid_attach(struct device *parent, struct device *self, void *aux) |
126 | { |
127 | struct uhid_softc *sc = (struct uhid_softc *)self; |
128 | struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; |
129 | int size, repid; |
130 | void *desc; |
131 | |
132 | sc->sc_hdev.sc_intr = uhid_intr; |
133 | sc->sc_hdev.sc_parent = uha->parent; |
134 | sc->sc_hdev.sc_udev = uha->uaa->device; |
135 | sc->sc_hdev.sc_report_id = uha->reportid; |
136 | |
137 | uhidev_get_report_desc(uha->parent, &desc, &size); |
138 | repid = uha->reportid; |
139 | sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); |
140 | sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); |
141 | sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); |
142 | |
143 | printf(": input=%d, output=%d, feature=%d\n", |
144 | sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize, sc->sc_hdev.sc_fsize); |
145 | } |
146 | |
147 | int |
148 | uhid_detach(struct device *self, int flags) |
149 | { |
150 | struct uhid_softc *sc = (struct uhid_softc *)self; |
151 | int s; |
152 | int maj, mn; |
153 | |
154 | DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); |
155 | |
156 | if (sc->sc_hdev.sc_state & UHIDEV_OPEN0x01) { |
157 | s = splusb()splraise(0x5); |
158 | if (--sc->sc_refcnt >= 0) { |
159 | /* Wake everyone */ |
160 | wakeup(&sc->sc_q); |
161 | /* Wait for processes to go away. */ |
162 | usb_detach_wait(&sc->sc_hdev.sc_dev); |
163 | } |
164 | splx(s)spllower(s); |
165 | } |
166 | |
167 | /* locate the major number */ |
168 | for (maj = 0; maj < nchrdev; maj++) |
169 | if (cdevsw[maj].d_open == uhidopen) |
170 | break; |
171 | |
172 | /* Nuke the vnodes for any open instances (calls close). */ |
173 | mn = self->dv_unit; |
174 | vdevgone(maj, mn, mn, VCHR); |
175 | |
176 | s = splusb()splraise(0x5); |
177 | klist_invalidate(&sc->sc_rsel.si_note); |
178 | splx(s)spllower(s); |
179 | |
180 | return (0); |
181 | } |
182 | |
183 | void |
184 | uhid_intr(struct uhidev *addr, void *data, u_int len) |
185 | { |
186 | struct uhid_softc *sc = (struct uhid_softc *)addr; |
187 | |
188 | #ifdef UHID_DEBUG |
189 | if (uhiddebug > 5) { |
190 | u_int32_t i; |
191 | |
192 | DPRINTF(("uhid_intr: data =")); |
193 | for (i = 0; i < len; i++) |
194 | DPRINTF((" %02x", ((u_char *)data)[i])); |
195 | DPRINTF(("\n")); |
196 | } |
197 | #endif |
198 | |
199 | (void)b_to_q(data, len, &sc->sc_q); |
200 | |
201 | if (sc->sc_state & UHID_ASLP0x01) { |
202 | sc->sc_state &= ~UHID_ASLP0x01; |
203 | DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); |
204 | wakeup(&sc->sc_q); |
205 | } |
206 | selwakeup(&sc->sc_rsel); |
207 | } |
208 | |
209 | int |
210 | uhidopen(dev_t dev, int flag, int mode, struct proc *p) |
211 | { |
212 | return (uhid_do_open(dev, flag, mode, p)); |
213 | } |
214 | |
215 | int |
216 | uhid_do_open(dev_t dev, int flag, int mode, struct proc *p) |
217 | { |
218 | struct uhid_softc *sc; |
219 | int error; |
220 | |
221 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
222 | return (ENXIO6); |
223 | |
224 | DPRINTF(("uhidopen: sc=%p\n", sc)); |
225 | |
226 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
227 | return (ENXIO6); |
228 | |
229 | error = uhidev_open(&sc->sc_hdev); |
230 | if (error) |
231 | return (error); |
232 | |
233 | clalloc(&sc->sc_q, UHID_BSIZE1020, 0); |
234 | |
235 | sc->sc_obuf = malloc(sc->sc_hdev.sc_osize, M_USBDEV102, M_WAITOK0x0001); |
236 | |
237 | return (0); |
238 | } |
239 | |
240 | int |
241 | uhidclose(dev_t dev, int flag, int mode, struct proc *p) |
242 | { |
243 | struct uhid_softc *sc; |
244 | |
245 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
246 | return (ENXIO6); |
247 | |
248 | DPRINTF(("uhidclose: sc=%p\n", sc)); |
249 | |
250 | clfree(&sc->sc_q); |
251 | free(sc->sc_obuf, M_USBDEV102, sc->sc_hdev.sc_osize); |
252 | uhidev_close(&sc->sc_hdev); |
253 | |
254 | return (0); |
255 | } |
256 | |
257 | int |
258 | uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) |
259 | { |
260 | int s; |
261 | int error = 0; |
262 | size_t length; |
263 | u_char buffer[UHID_CHUNK128]; |
264 | |
265 | DPRINTFN(1, ("uhidread\n")); |
266 | |
267 | s = splusb()splraise(0x5); |
268 | while (sc->sc_q.c_cc == 0) { |
269 | if (flag & IO_NDELAY0x10) { |
270 | splx(s)spllower(s); |
271 | return (EWOULDBLOCK35); |
272 | } |
273 | sc->sc_state |= UHID_ASLP0x01; |
274 | DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); |
275 | error = tsleep_nsec(&sc->sc_q, PZERO22|PCATCH0x100, "uhidrea", INFSLP0xffffffffffffffffULL); |
276 | DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); |
277 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
278 | error = EIO5; |
279 | if (error) { |
280 | sc->sc_state &= ~UHID_ASLP0x01; |
281 | break; |
282 | } |
283 | } |
284 | splx(s)spllower(s); |
285 | |
286 | /* Transfer as many chunks as possible. */ |
287 | while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { |
288 | length = ulmin(sc->sc_q.c_cc, uio->uio_resid); |
289 | if (length > sizeof(buffer)) |
290 | length = sizeof(buffer); |
291 | |
292 | /* Remove a small chunk from the input queue. */ |
293 | (void) q_to_b(&sc->sc_q, buffer, length); |
294 | DPRINTFN(5, ("uhidread: got %zu chars\n", length)); |
295 | |
296 | /* Copy the data to the user process. */ |
297 | if ((error = uiomove(buffer, length, uio)) != 0) |
298 | break; |
299 | } |
300 | |
301 | return (error); |
302 | } |
303 | |
304 | int |
305 | uhidread(dev_t dev, struct uio *uio, int flag) |
306 | { |
307 | struct uhid_softc *sc; |
308 | int error; |
309 | |
310 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
311 | return (ENXIO6); |
312 | |
313 | sc->sc_refcnt++; |
314 | error = uhid_do_read(sc, uio, flag); |
315 | if (--sc->sc_refcnt < 0) |
316 | usb_detach_wakeup(&sc->sc_hdev.sc_dev); |
317 | return (error); |
318 | } |
319 | |
320 | int |
321 | uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) |
322 | { |
323 | int error; |
324 | int size; |
325 | |
326 | DPRINTFN(1, ("uhidwrite\n")); |
327 | |
328 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
329 | return (EIO5); |
330 | |
331 | size = sc->sc_hdev.sc_osize; |
332 | error = 0; |
Value stored to 'error' is never read | |
333 | if (uio->uio_resid > size) |
334 | return (EMSGSIZE40); |
335 | else if (uio->uio_resid < size) { |
336 | /* don't leak kernel memory to the USB device */ |
337 | memset(sc->sc_obuf + uio->uio_resid, 0, size - uio->uio_resid)__builtin_memset((sc->sc_obuf + uio->uio_resid), (0), ( size - uio->uio_resid)); |
338 | } |
339 | error = uiomove(sc->sc_obuf, uio->uio_resid, uio); |
340 | if (!error) { |
341 | if (uhidev_set_report(sc->sc_hdev.sc_parent, |
342 | UHID_OUTPUT_REPORT0x02, sc->sc_hdev.sc_report_id, sc->sc_obuf, |
343 | size) != size) |
344 | error = EIO5; |
345 | } |
346 | |
347 | return (error); |
348 | } |
349 | |
350 | int |
351 | uhidwrite(dev_t dev, struct uio *uio, int flag) |
352 | { |
353 | struct uhid_softc *sc; |
354 | int error; |
355 | |
356 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
357 | return (ENXIO6); |
358 | |
359 | sc->sc_refcnt++; |
360 | error = uhid_do_write(sc, uio, flag); |
361 | if (--sc->sc_refcnt < 0) |
362 | usb_detach_wakeup(&sc->sc_hdev.sc_dev); |
363 | return (error); |
364 | } |
365 | |
366 | int |
367 | uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, |
368 | int flag, struct proc *p) |
369 | { |
370 | int rc; |
371 | |
372 | DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); |
373 | |
374 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
375 | return (EIO5); |
376 | |
377 | switch (cmd) { |
378 | case FIONBIO((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('f')) << 8) | ((126))): |
379 | case FIOASYNC((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('f')) << 8) | ((125))): |
380 | /* All handled in the upper FS layer. */ |
381 | break; |
382 | |
383 | case USB_GET_DEVICEINFO((unsigned long)0x40000000 | ((sizeof(struct usb_device_info) & 0x1fff) << 16) | ((('U')) << 8) | ((112))): |
384 | usbd_fill_deviceinfo(sc->sc_hdev.sc_udev, |
385 | (struct usb_device_info *)addr); |
386 | break; |
387 | |
388 | case USB_GET_REPORT_DESC((unsigned long)0x40000000 | ((sizeof(struct usb_ctl_report_desc ) & 0x1fff) << 16) | ((('U')) << 8) | ((21))): |
389 | case USB_GET_REPORT(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct usb_ctl_report) & 0x1fff) << 16) | ((('U')) << 8) | ((23))): |
390 | case USB_SET_REPORT((unsigned long)0x80000000 | ((sizeof(struct usb_ctl_report) & 0x1fff) << 16) | ((('U')) << 8) | ((24))): |
391 | case USB_GET_REPORT_ID((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) << 16) | ((('U')) << 8) | ((25))): |
392 | default: |
393 | rc = uhidev_ioctl(&sc->sc_hdev, cmd, addr, flag, p); |
394 | if (rc == -1) |
395 | rc = ENOTTY25; |
396 | return rc; |
397 | } |
398 | return (0); |
399 | } |
400 | |
401 | int |
402 | uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) |
403 | { |
404 | struct uhid_softc *sc; |
405 | int error; |
406 | |
407 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
408 | return (ENXIO6); |
409 | |
410 | sc->sc_refcnt++; |
411 | error = uhid_do_ioctl(sc, cmd, addr, flag, p); |
412 | if (--sc->sc_refcnt < 0) |
413 | usb_detach_wakeup(&sc->sc_hdev.sc_dev); |
414 | return (error); |
415 | } |
416 | |
417 | int |
418 | uhidpoll(dev_t dev, int events, struct proc *p) |
419 | { |
420 | struct uhid_softc *sc; |
421 | int revents = 0; |
422 | int s; |
423 | |
424 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
425 | return (POLLERR0x0008); |
426 | |
427 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
428 | return (POLLHUP0x0010); |
429 | |
430 | s = splusb()splraise(0x5); |
431 | if (events & (POLLOUT0x0004 | POLLWRNORM0x0004)) |
432 | revents |= events & (POLLOUT0x0004 | POLLWRNORM0x0004); |
433 | if (events & (POLLIN0x0001 | POLLRDNORM0x0040)) { |
434 | if (sc->sc_q.c_cc > 0) |
435 | revents |= events & (POLLIN0x0001 | POLLRDNORM0x0040); |
436 | else |
437 | selrecord(p, &sc->sc_rsel); |
438 | } |
439 | |
440 | splx(s)spllower(s); |
441 | return (revents); |
442 | } |
443 | |
444 | void filt_uhidrdetach(struct knote *); |
445 | int filt_uhidread(struct knote *, long); |
446 | int uhidkqfilter(dev_t, struct knote *); |
447 | |
448 | void |
449 | filt_uhidrdetach(struct knote *kn) |
450 | { |
451 | struct uhid_softc *sc = (void *)kn->kn_hook; |
452 | int s; |
453 | |
454 | s = splusb()splraise(0x5); |
455 | klist_remove_locked(&sc->sc_rsel.si_note, kn); |
456 | splx(s)spllower(s); |
457 | } |
458 | |
459 | int |
460 | filt_uhidread(struct knote *kn, long hint) |
461 | { |
462 | struct uhid_softc *sc = (void *)kn->kn_hook; |
463 | |
464 | kn->kn_datakn_kevent.data = sc->sc_q.c_cc; |
465 | return (kn->kn_datakn_kevent.data > 0); |
466 | } |
467 | |
468 | const struct filterops uhidread_filtops = { |
469 | .f_flags = FILTEROP_ISFD0x00000001, |
470 | .f_attach = NULL((void *)0), |
471 | .f_detach = filt_uhidrdetach, |
472 | .f_event = filt_uhidread, |
473 | }; |
474 | |
475 | int |
476 | uhidkqfilter(dev_t dev, struct knote *kn) |
477 | { |
478 | struct uhid_softc *sc; |
479 | struct klist *klist; |
480 | int s; |
481 | |
482 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
483 | return (ENXIO6); |
484 | |
485 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
486 | return (ENXIO6); |
487 | |
488 | switch (kn->kn_filterkn_kevent.filter) { |
489 | case EVFILT_READ(-1): |
490 | klist = &sc->sc_rsel.si_note; |
491 | kn->kn_fop = &uhidread_filtops; |
492 | break; |
493 | |
494 | case EVFILT_WRITE(-2): |
495 | return (seltrue_kqfilter(dev, kn)); |
496 | |
497 | default: |
498 | return (EINVAL22); |
499 | } |
500 | |
501 | kn->kn_hook = (void *)sc; |
502 | |
503 | s = splusb()splraise(0x5); |
504 | klist_insert_locked(klist, kn); |
505 | splx(s)spllower(s); |
506 | |
507 | return (0); |
508 | } |