Line data Source code
1 : /* $OpenBSD: uhid.c,v 1.71 2018/05/01 18:14:46 landry 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: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
36 : */
37 :
38 : #include <sys/param.h>
39 : #include <sys/systm.h>
40 : #include <sys/kernel.h>
41 : #include <sys/malloc.h>
42 : #include <sys/signalvar.h>
43 : #include <sys/device.h>
44 : #include <sys/ioctl.h>
45 : #include <sys/conf.h>
46 : #include <sys/tty.h>
47 : #include <sys/selinfo.h>
48 : #include <sys/proc.h>
49 : #include <sys/vnode.h>
50 : #include <sys/poll.h>
51 :
52 : #include <dev/usb/usb.h>
53 : #include <dev/usb/usbhid.h>
54 :
55 : #include <dev/usb/usbdevs.h>
56 : #include <dev/usb/usbdi.h>
57 : #include <dev/usb/usbdi_util.h>
58 :
59 : #include <dev/usb/uhidev.h>
60 :
61 : #ifdef UHID_DEBUG
62 : #define DPRINTF(x) do { if (uhiddebug) printf x; } while (0)
63 : #define DPRINTFN(n,x) do { if (uhiddebug>(n)) printf x; } while (0)
64 : int uhiddebug = 0;
65 : #else
66 : #define DPRINTF(x)
67 : #define DPRINTFN(n,x)
68 : #endif
69 :
70 : struct uhid_softc {
71 : struct uhidev sc_hdev;
72 :
73 : u_char *sc_obuf;
74 :
75 : struct clist sc_q;
76 : struct selinfo sc_rsel;
77 : u_char sc_state; /* driver state */
78 : #define UHID_ASLP 0x01 /* waiting for device data */
79 :
80 : int sc_refcnt;
81 : };
82 :
83 : #define UHIDUNIT(dev) (minor(dev))
84 : #define UHID_CHUNK 128 /* chunk size for read */
85 : #define UHID_BSIZE 1020 /* buffer size */
86 :
87 : void uhid_intr(struct uhidev *, void *, u_int len);
88 :
89 : int uhid_do_read(struct uhid_softc *, struct uio *uio, int);
90 : int uhid_do_write(struct uhid_softc *, struct uio *uio, int);
91 : int uhid_do_ioctl(struct uhid_softc*, u_long, caddr_t, int,
92 : struct proc *);
93 :
94 : int uhid_match(struct device *, void *, void *);
95 : void uhid_attach(struct device *, struct device *, void *);
96 : int uhid_detach(struct device *, int);
97 :
98 : struct cfdriver uhid_cd = {
99 : NULL, "uhid", DV_DULL
100 : };
101 :
102 : const struct cfattach uhid_ca = {
103 : sizeof(struct uhid_softc),
104 : uhid_match,
105 : uhid_attach,
106 : uhid_detach,
107 : };
108 :
109 : int
110 0 : uhid_match(struct device *parent, void *match, void *aux)
111 : {
112 0 : struct uhidev_attach_arg *uha = aux;
113 :
114 0 : if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID)
115 0 : return (UMATCH_NONE);
116 :
117 0 : return (UMATCH_IFACECLASS_GENERIC);
118 0 : }
119 :
120 : void
121 0 : uhid_attach(struct device *parent, struct device *self, void *aux)
122 : {
123 0 : struct uhid_softc *sc = (struct uhid_softc *)self;
124 0 : struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
125 0 : int size, repid;
126 0 : void *desc;
127 :
128 0 : sc->sc_hdev.sc_intr = uhid_intr;
129 0 : sc->sc_hdev.sc_parent = uha->parent;
130 0 : sc->sc_hdev.sc_udev = uha->uaa->device;
131 0 : sc->sc_hdev.sc_report_id = uha->reportid;
132 :
133 0 : uhidev_get_report_desc(uha->parent, &desc, &size);
134 0 : repid = uha->reportid;
135 0 : sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
136 0 : sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
137 0 : sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
138 :
139 0 : printf(": input=%d, output=%d, feature=%d\n",
140 0 : sc->sc_hdev.sc_isize, sc->sc_hdev.sc_osize, sc->sc_hdev.sc_fsize);
141 0 : }
142 :
143 : int
144 0 : uhid_detach(struct device *self, int flags)
145 : {
146 0 : struct uhid_softc *sc = (struct uhid_softc *)self;
147 : int s;
148 : int maj, mn;
149 :
150 : DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags));
151 :
152 0 : if (sc->sc_hdev.sc_state & UHIDEV_OPEN) {
153 0 : s = splusb();
154 0 : if (--sc->sc_refcnt >= 0) {
155 : /* Wake everyone */
156 0 : wakeup(&sc->sc_q);
157 : /* Wait for processes to go away. */
158 0 : usb_detach_wait(&sc->sc_hdev.sc_dev);
159 0 : }
160 0 : splx(s);
161 0 : }
162 :
163 : /* locate the major number */
164 0 : for (maj = 0; maj < nchrdev; maj++)
165 0 : if (cdevsw[maj].d_open == uhidopen)
166 : break;
167 :
168 : /* Nuke the vnodes for any open instances (calls close). */
169 0 : mn = self->dv_unit;
170 0 : vdevgone(maj, mn, mn, VCHR);
171 :
172 0 : return (0);
173 : }
174 :
175 : void
176 0 : uhid_intr(struct uhidev *addr, void *data, u_int len)
177 : {
178 0 : struct uhid_softc *sc = (struct uhid_softc *)addr;
179 :
180 : #ifdef UHID_DEBUG
181 : if (uhiddebug > 5) {
182 : u_int32_t i;
183 :
184 : DPRINTF(("uhid_intr: data ="));
185 : for (i = 0; i < len; i++)
186 : DPRINTF((" %02x", ((u_char *)data)[i]));
187 : DPRINTF(("\n"));
188 : }
189 : #endif
190 :
191 0 : (void)b_to_q(data, len, &sc->sc_q);
192 :
193 0 : if (sc->sc_state & UHID_ASLP) {
194 0 : sc->sc_state &= ~UHID_ASLP;
195 : DPRINTFN(5, ("uhid_intr: waking %p\n", &sc->sc_q));
196 0 : wakeup(&sc->sc_q);
197 0 : }
198 0 : selwakeup(&sc->sc_rsel);
199 0 : }
200 :
201 : int
202 0 : uhidopen(dev_t dev, int flag, int mode, struct proc *p)
203 : {
204 : struct uhid_softc *sc;
205 : int error;
206 :
207 0 : if (UHIDUNIT(dev) >= uhid_cd.cd_ndevs)
208 0 : return (ENXIO);
209 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
210 0 : if (sc == NULL)
211 0 : return (ENXIO);
212 :
213 : DPRINTF(("uhidopen: sc=%p\n", sc));
214 :
215 0 : if (usbd_is_dying(sc->sc_hdev.sc_udev))
216 0 : return (ENXIO);
217 :
218 0 : error = uhidev_open(&sc->sc_hdev);
219 0 : if (error)
220 0 : return (error);
221 :
222 0 : clalloc(&sc->sc_q, UHID_BSIZE, 0);
223 :
224 0 : sc->sc_obuf = malloc(sc->sc_hdev.sc_osize, M_USBDEV, M_WAITOK);
225 :
226 0 : return (0);
227 0 : }
228 :
229 : int
230 0 : uhidclose(dev_t dev, int flag, int mode, struct proc *p)
231 : {
232 : struct uhid_softc *sc;
233 :
234 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
235 :
236 : DPRINTF(("uhidclose: sc=%p\n", sc));
237 :
238 0 : clfree(&sc->sc_q);
239 0 : free(sc->sc_obuf, M_USBDEV, sc->sc_hdev.sc_osize);
240 0 : uhidev_close(&sc->sc_hdev);
241 :
242 0 : return (0);
243 : }
244 :
245 : int
246 0 : uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag)
247 : {
248 : int s;
249 : int error = 0;
250 : size_t length;
251 0 : u_char buffer[UHID_CHUNK];
252 :
253 : DPRINTFN(1, ("uhidread\n"));
254 :
255 0 : s = splusb();
256 0 : while (sc->sc_q.c_cc == 0) {
257 0 : if (flag & IO_NDELAY) {
258 0 : splx(s);
259 0 : return (EWOULDBLOCK);
260 : }
261 0 : sc->sc_state |= UHID_ASLP;
262 : DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q));
263 0 : error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0);
264 : DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
265 0 : if (usbd_is_dying(sc->sc_hdev.sc_udev))
266 0 : error = EIO;
267 0 : if (error) {
268 0 : sc->sc_state &= ~UHID_ASLP;
269 0 : break;
270 : }
271 : }
272 0 : splx(s);
273 :
274 : /* Transfer as many chunks as possible. */
275 0 : while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) {
276 0 : length = ulmin(sc->sc_q.c_cc, uio->uio_resid);
277 0 : if (length > sizeof(buffer))
278 : length = sizeof(buffer);
279 :
280 : /* Remove a small chunk from the input queue. */
281 0 : (void) q_to_b(&sc->sc_q, buffer, length);
282 : DPRINTFN(5, ("uhidread: got %zu chars\n", length));
283 :
284 : /* Copy the data to the user process. */
285 0 : if ((error = uiomove(buffer, length, uio)) != 0)
286 : break;
287 : }
288 :
289 0 : return (error);
290 0 : }
291 :
292 : int
293 0 : uhidread(dev_t dev, struct uio *uio, int flag)
294 : {
295 : struct uhid_softc *sc;
296 : int error;
297 :
298 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
299 :
300 0 : sc->sc_refcnt++;
301 0 : error = uhid_do_read(sc, uio, flag);
302 0 : if (--sc->sc_refcnt < 0)
303 0 : usb_detach_wakeup(&sc->sc_hdev.sc_dev);
304 0 : return (error);
305 : }
306 :
307 : int
308 0 : uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag)
309 : {
310 : int error;
311 : int size;
312 :
313 : DPRINTFN(1, ("uhidwrite\n"));
314 :
315 0 : if (usbd_is_dying(sc->sc_hdev.sc_udev))
316 0 : return (EIO);
317 :
318 0 : size = sc->sc_hdev.sc_osize;
319 : error = 0;
320 0 : if (uio->uio_resid != size)
321 0 : return (EINVAL);
322 0 : error = uiomove(sc->sc_obuf, size, uio);
323 0 : if (!error) {
324 0 : if (uhidev_set_report(sc->sc_hdev.sc_parent,
325 0 : UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, sc->sc_obuf,
326 0 : size) != size)
327 0 : error = EIO;
328 : }
329 :
330 0 : return (error);
331 0 : }
332 :
333 : int
334 0 : uhidwrite(dev_t dev, struct uio *uio, int flag)
335 : {
336 : struct uhid_softc *sc;
337 : int error;
338 :
339 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
340 :
341 0 : sc->sc_refcnt++;
342 0 : error = uhid_do_write(sc, uio, flag);
343 0 : if (--sc->sc_refcnt < 0)
344 0 : usb_detach_wakeup(&sc->sc_hdev.sc_dev);
345 0 : return (error);
346 : }
347 :
348 : int
349 0 : uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr,
350 : int flag, struct proc *p)
351 : {
352 : int rc;
353 :
354 : DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
355 :
356 0 : if (usbd_is_dying(sc->sc_hdev.sc_udev))
357 0 : return (EIO);
358 :
359 0 : switch (cmd) {
360 : case FIONBIO:
361 : case FIOASYNC:
362 : /* All handled in the upper FS layer. */
363 : break;
364 :
365 : case USB_GET_DEVICEINFO:
366 0 : usbd_fill_deviceinfo(sc->sc_hdev.sc_udev,
367 0 : (struct usb_device_info *)addr);
368 0 : break;
369 :
370 : case USB_GET_REPORT_DESC:
371 : case USB_GET_REPORT:
372 : case USB_SET_REPORT:
373 : case USB_GET_REPORT_ID:
374 : default:
375 0 : rc = uhidev_ioctl(&sc->sc_hdev, cmd, addr, flag, p);
376 0 : if (rc == -1)
377 : rc = EINVAL;
378 0 : return rc;
379 : }
380 0 : return (0);
381 0 : }
382 :
383 : int
384 0 : uhidioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
385 : {
386 : struct uhid_softc *sc;
387 : int error;
388 :
389 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
390 :
391 0 : sc->sc_refcnt++;
392 0 : error = uhid_do_ioctl(sc, cmd, addr, flag, p);
393 0 : if (--sc->sc_refcnt < 0)
394 0 : usb_detach_wakeup(&sc->sc_hdev.sc_dev);
395 0 : return (error);
396 : }
397 :
398 : int
399 0 : uhidpoll(dev_t dev, int events, struct proc *p)
400 : {
401 : struct uhid_softc *sc;
402 : int revents = 0;
403 : int s;
404 :
405 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
406 :
407 0 : if (usbd_is_dying(sc->sc_hdev.sc_udev))
408 0 : return (POLLERR);
409 :
410 0 : s = splusb();
411 0 : if (events & (POLLOUT | POLLWRNORM))
412 0 : revents |= events & (POLLOUT | POLLWRNORM);
413 0 : if (events & (POLLIN | POLLRDNORM)) {
414 0 : if (sc->sc_q.c_cc > 0)
415 0 : revents |= events & (POLLIN | POLLRDNORM);
416 : else
417 0 : selrecord(p, &sc->sc_rsel);
418 : }
419 :
420 0 : splx(s);
421 0 : return (revents);
422 0 : }
423 :
424 : void filt_uhidrdetach(struct knote *);
425 : int filt_uhidread(struct knote *, long);
426 : int uhidkqfilter(dev_t, struct knote *);
427 :
428 : void
429 0 : filt_uhidrdetach(struct knote *kn)
430 : {
431 0 : struct uhid_softc *sc = (void *)kn->kn_hook;
432 : int s;
433 :
434 0 : s = splusb();
435 0 : SLIST_REMOVE(&sc->sc_rsel.si_note, kn, knote, kn_selnext);
436 0 : splx(s);
437 0 : }
438 :
439 : int
440 0 : filt_uhidread(struct knote *kn, long hint)
441 : {
442 0 : struct uhid_softc *sc = (void *)kn->kn_hook;
443 :
444 0 : kn->kn_data = sc->sc_q.c_cc;
445 0 : return (kn->kn_data > 0);
446 : }
447 :
448 : struct filterops uhidread_filtops =
449 : { 1, NULL, filt_uhidrdetach, filt_uhidread };
450 :
451 : struct filterops uhid_seltrue_filtops =
452 : { 1, NULL, filt_uhidrdetach, filt_seltrue };
453 :
454 : int
455 0 : uhidkqfilter(dev_t dev, struct knote *kn)
456 : {
457 : struct uhid_softc *sc;
458 : struct klist *klist;
459 : int s;
460 :
461 0 : sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
462 :
463 0 : if (usbd_is_dying(sc->sc_hdev.sc_udev))
464 0 : return (EIO);
465 :
466 0 : switch (kn->kn_filter) {
467 : case EVFILT_READ:
468 0 : klist = &sc->sc_rsel.si_note;
469 0 : kn->kn_fop = &uhidread_filtops;
470 0 : break;
471 :
472 : case EVFILT_WRITE:
473 0 : klist = &sc->sc_rsel.si_note;
474 0 : kn->kn_fop = &uhid_seltrue_filtops;
475 0 : break;
476 :
477 : default:
478 0 : return (EINVAL);
479 : }
480 :
481 0 : kn->kn_hook = (void *)sc;
482 :
483 0 : s = splusb();
484 0 : SLIST_INSERT_HEAD(klist, kn, kn_selnext);
485 0 : splx(s);
486 :
487 0 : return (0);
488 0 : }
|