File: | dev/usb/uhid.c |
Warning: | line 336, column 2 Value stored to 'error' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: uhid.c,v 1.90 2023/04/20 10:49:57 brynet 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 | |
54 | #include <dev/usb/usb.h> |
55 | #include <dev/usb/usbhid.h> |
56 | |
57 | #include <dev/usb/usbdevs.h> |
58 | #include <dev/usb/usbdi.h> |
59 | #include <dev/usb/usbdi_util.h> |
60 | |
61 | #include <dev/usb/uhidev.h> |
62 | #include <dev/usb/uhid.h> |
63 | |
64 | #ifdef UHID_DEBUG |
65 | #define DPRINTF(x) do { if (uhiddebug) printf x; } while (0) |
66 | #define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0) |
67 | int uhiddebug = 0; |
68 | #else |
69 | #define DPRINTF(x) |
70 | #define DPRINTFN(n,x) |
71 | #endif |
72 | |
73 | int uhid_match(struct device *, void *, void *); |
74 | |
75 | struct cfdriver uhid_cd = { |
76 | NULL((void *)0), "uhid", DV_DULL |
77 | }; |
78 | |
79 | const struct cfattach uhid_ca = { |
80 | sizeof(struct uhid_softc), |
81 | uhid_match, |
82 | uhid_attach, |
83 | uhid_detach, |
84 | }; |
85 | |
86 | struct uhid_softc * |
87 | uhid_lookup(dev_t dev) |
88 | { |
89 | struct uhid_softc *sc = NULL((void *)0); |
90 | struct cdevsw *cdev; |
91 | struct cfdriver *cd; |
92 | |
93 | cdev = &cdevsw[major(dev)(((unsigned)(dev) >> 8) & 0xff)]; |
94 | if (cdev->d_open == uhidopen) |
95 | cd = &uhid_cd; |
96 | #if NFIDO1 > 0 |
97 | else if (cdev->d_open == fidoopen) |
98 | cd = &fido_cd; |
99 | #endif |
100 | #if NUJOY1 > 0 |
101 | else if (cdev->d_open == ujoyopen) |
102 | cd = &ujoy_cd; |
103 | #endif |
104 | else |
105 | return (NULL((void *)0)); |
106 | if (UHIDUNIT(dev)(((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8))) < cd->cd_ndevs) |
107 | sc = cd->cd_devs[UHIDUNIT(dev)(((unsigned)((dev) & 0xff) | (((dev) & 0xffff0000) >> 8)))]; |
108 | |
109 | return (sc); |
110 | } |
111 | |
112 | int |
113 | uhid_match(struct device *parent, void *match, void *aux) |
114 | { |
115 | struct uhidev_attach_arg *uha = aux; |
116 | |
117 | if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)((uha)->claimed != ((void *)0))) |
118 | return (UMATCH_NONE0); |
119 | |
120 | return (UMATCH_IFACECLASS_GENERIC2); |
121 | } |
122 | |
123 | void |
124 | uhid_attach(struct device *parent, struct device *self, void *aux) |
125 | { |
126 | struct uhid_softc *sc = (struct uhid_softc *)self; |
127 | struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; |
128 | int size, repid; |
129 | void *desc; |
130 | |
131 | sc->sc_hdev.sc_intr = uhid_intr; |
132 | sc->sc_hdev.sc_parent = uha->parent; |
133 | sc->sc_hdev.sc_udev = uha->uaa->device; |
134 | sc->sc_hdev.sc_report_id = uha->reportid; |
135 | |
136 | uhidev_get_report_desc(uha->parent, &desc, &size); |
137 | repid = uha->reportid; |
138 | sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); |
139 | sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); |
140 | sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); |
141 | |
142 | printf(": input=%d, output=%d, feature=%d\n", |
143 | sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize, sc->sc_hdev.sc_fsize); |
144 | } |
145 | |
146 | int |
147 | uhid_detach(struct device *self, int flags) |
148 | { |
149 | struct uhid_softc *sc = (struct uhid_softc *)self; |
150 | int s; |
151 | int maj, mn; |
152 | |
153 | DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); |
154 | |
155 | if (sc->sc_hdev.sc_state & UHIDEV_OPEN0x01) { |
156 | s = splusb()splraise(0x2); |
157 | if (--sc->sc_refcnt >= 0) { |
158 | /* Wake everyone */ |
159 | wakeup(&sc->sc_q); |
160 | /* Wait for processes to go away. */ |
161 | usb_detach_wait(&sc->sc_hdev.sc_dev); |
162 | } |
163 | splx(s)spllower(s); |
164 | } |
165 | |
166 | /* locate the major number */ |
167 | for (maj = 0; maj < nchrdev; maj++) |
168 | if (cdevsw[maj].d_open == uhidopen) |
169 | break; |
170 | |
171 | /* Nuke the vnodes for any open instances (calls close). */ |
172 | mn = self->dv_unit; |
173 | vdevgone(maj, mn, mn, VCHR); |
174 | |
175 | s = splusb()splraise(0x2); |
176 | klist_invalidate(&sc->sc_rsel.si_note); |
177 | splx(s)spllower(s); |
178 | |
179 | return (0); |
180 | } |
181 | |
182 | void |
183 | uhid_intr(struct uhidev *addr, void *data, u_int len) |
184 | { |
185 | struct uhid_softc *sc = (struct uhid_softc *)addr; |
186 | |
187 | #ifdef UHID_DEBUG |
188 | if (uhiddebug > 5) { |
189 | u_int32_t i; |
190 | |
191 | DPRINTF(("uhid_intr: data =")); |
192 | for (i = 0; i < len; i++) |
193 | DPRINTF((" %02x", ((u_char *)data)[i])); |
194 | DPRINTF(("\n")); |
195 | } |
196 | #endif |
197 | |
198 | (void)b_to_q(data, len, &sc->sc_q); |
199 | |
200 | if (sc->sc_state & UHID_ASLP0x01) { |
201 | sc->sc_state &= ~UHID_ASLP0x01; |
202 | DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q)); |
203 | wakeup(&sc->sc_q); |
204 | } |
205 | selwakeup(&sc->sc_rsel); |
206 | } |
207 | |
208 | int |
209 | uhidopen(dev_t dev, int flag, int mode, struct proc *p) |
210 | { |
211 | return (uhid_do_open(dev, flag, mode, p)); |
212 | } |
213 | |
214 | int |
215 | uhid_do_open(dev_t dev, int flag, int mode, struct proc *p) |
216 | { |
217 | struct uhid_softc *sc; |
218 | int error; |
219 | |
220 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
221 | return (ENXIO6); |
222 | |
223 | DPRINTF(("uhidopen: sc=%p\n", sc)); |
224 | |
225 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
226 | return (ENXIO6); |
227 | |
228 | if (sc->sc_hdev.sc_state & UHIDEV_OPEN0x01) |
229 | return (EBUSY16); |
230 | |
231 | clalloc(&sc->sc_q, UHID_BSIZE1020, 0); |
232 | |
233 | error = uhidev_open(&sc->sc_hdev); |
234 | if (error) { |
235 | clfree(&sc->sc_q); |
236 | return (error); |
237 | } |
238 | |
239 | sc->sc_obuf = malloc(sc->sc_hdev.sc_osize, M_USBDEV102, M_WAITOK0x0001); |
240 | |
241 | return (0); |
242 | } |
243 | |
244 | int |
245 | uhidclose(dev_t dev, int flag, int mode, struct proc *p) |
246 | { |
247 | struct uhid_softc *sc; |
248 | |
249 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
250 | return (ENXIO6); |
251 | |
252 | DPRINTF(("uhidclose: sc=%p\n", sc)); |
253 | |
254 | clfree(&sc->sc_q); |
255 | free(sc->sc_obuf, M_USBDEV102, sc->sc_hdev.sc_osize); |
256 | uhidev_close(&sc->sc_hdev); |
257 | |
258 | return (0); |
259 | } |
260 | |
261 | int |
262 | uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) |
263 | { |
264 | int s; |
265 | int error = 0; |
266 | size_t length; |
267 | u_char buffer[UHID_CHUNK128]; |
268 | |
269 | DPRINTFN(1, ("uhidread\n")); |
270 | |
271 | s = splusb()splraise(0x2); |
272 | while (sc->sc_q.c_cc == 0) { |
273 | if (flag & IO_NDELAY0x10) { |
274 | splx(s)spllower(s); |
275 | return (EWOULDBLOCK35); |
276 | } |
277 | sc->sc_state |= UHID_ASLP0x01; |
278 | DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); |
279 | error = tsleep_nsec(&sc->sc_q, PZERO22|PCATCH0x100, "uhidrea", INFSLP0xffffffffffffffffULL); |
280 | DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); |
281 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
282 | error = EIO5; |
283 | if (error) { |
284 | sc->sc_state &= ~UHID_ASLP0x01; |
285 | break; |
286 | } |
287 | } |
288 | splx(s)spllower(s); |
289 | |
290 | /* Transfer as many chunks as possible. */ |
291 | while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { |
292 | length = ulmin(sc->sc_q.c_cc, uio->uio_resid); |
293 | if (length > sizeof(buffer)) |
294 | length = sizeof(buffer); |
295 | |
296 | /* Remove a small chunk from the input queue. */ |
297 | (void) q_to_b(&sc->sc_q, buffer, length); |
298 | DPRINTFN(5, ("uhidread: got %zu chars\n", length)); |
299 | |
300 | /* Copy the data to the user process. */ |
301 | if ((error = uiomove(buffer, length, uio)) != 0) |
302 | break; |
303 | } |
304 | |
305 | return (error); |
306 | } |
307 | |
308 | int |
309 | uhidread(dev_t dev, struct uio *uio, int flag) |
310 | { |
311 | struct uhid_softc *sc; |
312 | int error; |
313 | |
314 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
315 | return (ENXIO6); |
316 | |
317 | sc->sc_refcnt++; |
318 | error = uhid_do_read(sc, uio, flag); |
319 | if (--sc->sc_refcnt < 0) |
320 | usb_detach_wakeup(&sc->sc_hdev.sc_dev); |
321 | return (error); |
322 | } |
323 | |
324 | int |
325 | uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) |
326 | { |
327 | int error; |
328 | int size; |
329 | |
330 | DPRINTFN(1, ("uhidwrite\n")); |
331 | |
332 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
333 | return (EIO5); |
334 | |
335 | size = sc->sc_hdev.sc_osize; |
336 | error = 0; |
Value stored to 'error' is never read | |
337 | if (uio->uio_resid > size) |
338 | return (EMSGSIZE40); |
339 | else if (uio->uio_resid < size) { |
340 | /* don't leak kernel memory to the USB device */ |
341 | 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)); |
342 | } |
343 | error = uiomove(sc->sc_obuf, uio->uio_resid, uio); |
344 | if (!error) { |
345 | if (uhidev_set_report(sc->sc_hdev.sc_parent, |
346 | UHID_OUTPUT_REPORT0x02, sc->sc_hdev.sc_report_id, sc->sc_obuf, |
347 | size) != size) |
348 | error = EIO5; |
349 | } |
350 | |
351 | return (error); |
352 | } |
353 | |
354 | int |
355 | uhidwrite(dev_t dev, struct uio *uio, int flag) |
356 | { |
357 | struct uhid_softc *sc; |
358 | int error; |
359 | |
360 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
361 | return (ENXIO6); |
362 | |
363 | sc->sc_refcnt++; |
364 | error = uhid_do_write(sc, uio, flag); |
365 | if (--sc->sc_refcnt < 0) |
366 | usb_detach_wakeup(&sc->sc_hdev.sc_dev); |
367 | return (error); |
368 | } |
369 | |
370 | int |
371 | uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, |
372 | int flag, struct proc *p) |
373 | { |
374 | int rc; |
375 | |
376 | DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); |
377 | |
378 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
379 | return (EIO5); |
380 | |
381 | switch (cmd) { |
382 | case FIONBIO((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('f')) << 8) | ((126))): |
383 | case FIOASYNC((unsigned long)0x80000000 | ((sizeof(int) & 0x1fff) << 16) | ((('f')) << 8) | ((125))): |
384 | /* All handled in the upper FS layer. */ |
385 | break; |
386 | |
387 | case USB_GET_DEVICEINFO((unsigned long)0x40000000 | ((sizeof(struct usb_device_info) & 0x1fff) << 16) | ((('U')) << 8) | ((112))): |
388 | usbd_fill_deviceinfo(sc->sc_hdev.sc_udev, |
389 | (struct usb_device_info *)addr); |
390 | break; |
391 | |
392 | case USB_GET_REPORT_DESC((unsigned long)0x40000000 | ((sizeof(struct usb_ctl_report_desc ) & 0x1fff) << 16) | ((('U')) << 8) | ((21))): |
393 | case USB_GET_REPORT(((unsigned long)0x80000000|(unsigned long)0x40000000) | ((sizeof (struct usb_ctl_report) & 0x1fff) << 16) | ((('U')) << 8) | ((23))): |
394 | case USB_SET_REPORT((unsigned long)0x80000000 | ((sizeof(struct usb_ctl_report) & 0x1fff) << 16) | ((('U')) << 8) | ((24))): |
395 | case USB_GET_REPORT_ID((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) << 16) | ((('U')) << 8) | ((25))): |
396 | default: |
397 | rc = uhidev_ioctl(&sc->sc_hdev, cmd, addr, flag, p); |
398 | if (rc == -1) |
399 | rc = ENOTTY25; |
400 | return rc; |
401 | } |
402 | return (0); |
403 | } |
404 | |
405 | int |
406 | uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) |
407 | { |
408 | struct uhid_softc *sc; |
409 | int error; |
410 | |
411 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
412 | return (ENXIO6); |
413 | |
414 | sc->sc_refcnt++; |
415 | error = uhid_do_ioctl(sc, cmd, addr, flag, p); |
416 | if (--sc->sc_refcnt < 0) |
417 | usb_detach_wakeup(&sc->sc_hdev.sc_dev); |
418 | return (error); |
419 | } |
420 | |
421 | void filt_uhidrdetach(struct knote *); |
422 | int filt_uhidread(struct knote *, long); |
423 | int uhidkqfilter(dev_t, struct knote *); |
424 | |
425 | void |
426 | filt_uhidrdetach(struct knote *kn) |
427 | { |
428 | struct uhid_softc *sc = (void *)kn->kn_hook; |
429 | int s; |
430 | |
431 | s = splusb()splraise(0x2); |
432 | klist_remove_locked(&sc->sc_rsel.si_note, kn); |
433 | splx(s)spllower(s); |
434 | } |
435 | |
436 | int |
437 | filt_uhidread(struct knote *kn, long hint) |
438 | { |
439 | struct uhid_softc *sc = (void *)kn->kn_hook; |
440 | |
441 | kn->kn_datakn_kevent.data = sc->sc_q.c_cc; |
442 | return (kn->kn_datakn_kevent.data > 0); |
443 | } |
444 | |
445 | const struct filterops uhidread_filtops = { |
446 | .f_flags = FILTEROP_ISFD0x00000001, |
447 | .f_attach = NULL((void *)0), |
448 | .f_detach = filt_uhidrdetach, |
449 | .f_event = filt_uhidread, |
450 | }; |
451 | |
452 | int |
453 | uhidkqfilter(dev_t dev, struct knote *kn) |
454 | { |
455 | struct uhid_softc *sc; |
456 | struct klist *klist; |
457 | int s; |
458 | |
459 | if ((sc = uhid_lookup(dev)) == NULL((void *)0)) |
460 | return (ENXIO6); |
461 | |
462 | if (usbd_is_dying(sc->sc_hdev.sc_udev)) |
463 | return (ENXIO6); |
464 | |
465 | switch (kn->kn_filterkn_kevent.filter) { |
466 | case EVFILT_READ(-1): |
467 | klist = &sc->sc_rsel.si_note; |
468 | kn->kn_fop = &uhidread_filtops; |
469 | break; |
470 | |
471 | case EVFILT_WRITE(-2): |
472 | return (seltrue_kqfilter(dev, kn)); |
473 | |
474 | default: |
475 | return (EINVAL22); |
476 | } |
477 | |
478 | kn->kn_hook = (void *)sc; |
479 | |
480 | s = splusb()splraise(0x2); |
481 | klist_insert_locked(klist, kn); |
482 | splx(s)spllower(s); |
483 | |
484 | return (0); |
485 | } |