uhid.c revision 1.11 1 /* $NetBSD: uhid.c,v 1.11 1998/12/10 23:16:47 augustss Exp $ */
2
3 /*
4 * Copyright (c) 1998 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 (augustss (at) carlstedt.se) at
9 * Carlstedt Research & Technology.
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 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/device.h>
46 #include <sys/ioctl.h>
47 #include <sys/tty.h>
48 #include <sys/file.h>
49 #include <sys/select.h>
50 #include <sys/proc.h>
51 #include <sys/vnode.h>
52 #include <sys/device.h>
53 #include <sys/poll.h>
54
55 #include <dev/usb/usb.h>
56 #include <dev/usb/usbhid.h>
57
58 #include <dev/usb/usbdi.h>
59 #include <dev/usb/usbdi_util.h>
60 #include <dev/usb/hid.h>
61 #include <dev/usb/usb_quirks.h>
62
63 #ifdef USB_DEBUG
64 #define DPRINTF(x) if (uhiddebug) printf x
65 #define DPRINTFN(n,x) if (uhiddebug>(n)) printf x
66 int uhiddebug = 0;
67 #else
68 #define DPRINTF(x)
69 #define DPRINTFN(n,x)
70 #endif
71
72 struct uhid_softc {
73 struct device sc_dev; /* base device */
74 usbd_interface_handle sc_iface; /* interface */
75 usbd_pipe_handle sc_intrpipe; /* interrupt pipe */
76 int sc_ep_addr;
77
78 int sc_isize;
79 int sc_osize;
80 int sc_fsize;
81 u_int8_t sc_iid;
82 u_int8_t sc_oid;
83 u_int8_t sc_fid;
84
85 char *sc_ibuf;
86 char *sc_obuf;
87
88 void *sc_repdesc;
89 int sc_repdesc_size;
90
91 struct clist sc_q;
92 struct selinfo sc_rsel;
93 u_char sc_state; /* driver state */
94 #define UHID_OPEN 0x01 /* device is open */
95 #define UHID_ASLP 0x02 /* waiting for device data */
96 #define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */
97 #define UHID_IMMED 0x08 /* return read data immediately */
98 int sc_disconnected; /* device is gone */
99 };
100
101 #define UHIDUNIT(dev) (minor(dev))
102 #define UHID_CHUNK 128 /* chunk size for read */
103 #define UHID_BSIZE 1020 /* buffer size */
104
105 int uhid_match __P((struct device *, struct cfdata *, void *));
106 void uhid_attach __P((struct device *, struct device *, void *));
107
108 int uhidopen __P((dev_t, int, int, struct proc *));
109 int uhidclose __P((dev_t, int, int, struct proc *p));
110 int uhidread __P((dev_t, struct uio *uio, int));
111 int uhidwrite __P((dev_t, struct uio *uio, int));
112 int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
113 int uhidpoll __P((dev_t, int, struct proc *));
114 void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
115 void uhid_disco __P((void *));
116
117 extern struct cfdriver uhid_cd;
118
119 struct cfattach uhid_ca = {
120 sizeof(struct uhid_softc), uhid_match, uhid_attach
121 };
122
123 int
124 uhid_match(parent, match, aux)
125 struct device *parent;
126 struct cfdata *match;
127 void *aux;
128 {
129 struct usb_attach_arg *uaa = aux;
130 usb_interface_descriptor_t *id;
131
132 if (!uaa->iface)
133 return (UMATCH_NONE);
134 id = usbd_get_interface_descriptor(uaa->iface);
135 if (!id || id->bInterfaceClass != UCLASS_HID)
136 return (UMATCH_NONE);
137 return (UMATCH_IFACECLASS_GENERIC);
138 }
139
140 void
141 uhid_attach(parent, self, aux)
142 struct device *parent;
143 struct device *self;
144 void *aux;
145 {
146 struct uhid_softc *sc = (struct uhid_softc *)self;
147 struct usb_attach_arg *uaa = aux;
148 usbd_interface_handle iface = uaa->iface;
149 usb_interface_descriptor_t *id;
150 usb_endpoint_descriptor_t *ed;
151 int size;
152 void *desc;
153 usbd_status r;
154 char devinfo[1024];
155
156 sc->sc_disconnected = 1;
157 sc->sc_iface = iface;
158 id = usbd_get_interface_descriptor(iface);
159 usbd_devinfo(uaa->device, 0, devinfo);
160 printf("\n%s: %s, iclass %d/%d\n", sc->sc_dev.dv_xname,
161 devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
162 ed = usbd_interface2endpoint_descriptor(iface, 0);
163 if (!ed) {
164 printf("%s: could not read endpoint descriptor\n",
165 sc->sc_dev.dv_xname);
166 return;
167 }
168
169 DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d "
170 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
171 " bInterval=%d\n",
172 ed->bLength, ed->bDescriptorType,
173 ed->bEndpointAddress & UE_ADDR,
174 ed->bEndpointAddress & UE_IN ? "in" : "out",
175 ed->bmAttributes & UE_XFERTYPE,
176 UGETW(ed->wMaxPacketSize), ed->bInterval));
177
178 if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
179 (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
180 printf("%s: unexpected endpoint\n",
181 sc->sc_dev.dv_xname);
182 return;
183 }
184
185 sc->sc_ep_addr = ed->bEndpointAddress;
186 sc->sc_disconnected = 0;
187
188 r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB);
189 if (r != USBD_NORMAL_COMPLETION) {
190 printf("%s: no report descriptor\n", sc->sc_dev.dv_xname);
191 return;
192 }
193
194 (void)usbd_set_idle(iface, 0, 0);
195
196 sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid);
197 sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid);
198 sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
199
200 sc->sc_repdesc = desc;
201 sc->sc_repdesc_size = size;
202 }
203
204 void
205 uhid_disco(p)
206 void *p;
207 {
208 struct uhid_softc *sc = p;
209
210 DPRINTF(("ums_hid: sc=%p\n", sc));
211 usbd_abort_pipe(sc->sc_intrpipe);
212 sc->sc_disconnected = 1;
213 }
214
215 void
216 uhid_intr(reqh, addr, status)
217 usbd_request_handle reqh;
218 usbd_private_handle addr;
219 usbd_status status;
220 {
221 struct uhid_softc *sc = addr;
222
223 DPRINTFN(5, ("uhid_intr: status=%d\n", status));
224 DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
225 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
226
227 if (status == USBD_CANCELLED)
228 return;
229
230 if (status != USBD_NORMAL_COMPLETION) {
231 DPRINTF(("uhid_intr: status=%d\n", status));
232 sc->sc_state |= UHID_NEEDCLEAR;
233 return;
234 }
235
236 (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
237
238 if (sc->sc_state & UHID_ASLP) {
239 sc->sc_state &= ~UHID_ASLP;
240 DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
241 wakeup((caddr_t)sc);
242 }
243 selwakeup(&sc->sc_rsel);
244 }
245
246 int
247 uhidopen(dev, flag, mode, p)
248 dev_t dev;
249 int flag;
250 int mode;
251 struct proc *p;
252 {
253 int unit = UHIDUNIT(dev);
254 struct uhid_softc *sc;
255 usbd_status r;
256
257 if (unit >= uhid_cd.cd_ndevs)
258 return ENXIO;
259 sc = uhid_cd.cd_devs[unit];
260 if (!sc)
261 return ENXIO;
262
263 DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
264
265 if (sc->sc_disconnected)
266 return (EIO);
267
268 if (sc->sc_state & UHID_OPEN)
269 return EBUSY;
270
271 if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1)
272 return ENOMEM;
273
274 sc->sc_state |= UHID_OPEN;
275 sc->sc_state &= ~UHID_IMMED;
276
277 sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK);
278 sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK);
279
280 /* Set up interrupt pipe. */
281 r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
282 USBD_SHORT_XFER_OK,
283 &sc->sc_intrpipe, sc, sc->sc_ibuf,
284 sc->sc_isize, uhid_intr);
285 if (r != USBD_NORMAL_COMPLETION) {
286 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
287 "error=%d\n",r));
288 sc->sc_state &= ~UHID_OPEN;
289 return (EIO);
290 }
291 usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc);
292
293 return 0;
294 }
295
296 int
297 uhidclose(dev, flag, mode, p)
298 dev_t dev;
299 int flag;
300 int mode;
301 struct proc *p;
302 {
303 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
304
305 if (sc->sc_disconnected)
306 return (EIO);
307
308 DPRINTF(("uhidclose: sc=%p\n", sc));
309
310 /* Disable interrupts. */
311 usbd_abort_pipe(sc->sc_intrpipe);
312 usbd_close_pipe(sc->sc_intrpipe);
313
314 sc->sc_state &= ~UHID_OPEN;
315
316 clfree(&sc->sc_q);
317
318 free(sc->sc_ibuf, M_USB);
319 free(sc->sc_obuf, M_USB);
320
321 return 0;
322 }
323
324 int
325 uhidread(dev, uio, flag)
326 dev_t dev;
327 struct uio *uio;
328 int flag;
329 {
330 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
331 int s;
332 int error = 0;
333 size_t length;
334 u_char buffer[UHID_CHUNK];
335 usbd_status r;
336
337 if (sc->sc_disconnected)
338 return (EIO);
339
340 DPRINTFN(1, ("uhidread\n"));
341 if (sc->sc_state & UHID_IMMED) {
342 DPRINTFN(1, ("uhidread immed\n"));
343
344 r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
345 sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
346 if (r != USBD_NORMAL_COMPLETION)
347 return (EIO);
348 return (uiomove(buffer, sc->sc_isize, uio));
349 }
350
351 s = splusb();
352 while (sc->sc_q.c_cc == 0) {
353 if (flag & IO_NDELAY) {
354 splx(s);
355 return EWOULDBLOCK;
356 }
357 sc->sc_state |= UHID_ASLP;
358 DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
359 error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0);
360 DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
361 if (error) {
362 sc->sc_state &= ~UHID_ASLP;
363 splx(s);
364 return (error);
365 }
366 if (sc->sc_state & UHID_NEEDCLEAR) {
367 DPRINTFN(-1,("uhidread: clearing stall\n"));
368 sc->sc_state &= ~UHID_NEEDCLEAR;
369 usbd_clear_endpoint_stall(sc->sc_intrpipe);
370 }
371 }
372 splx(s);
373
374 /* Transfer as many chunks as possible. */
375 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
376 length = min(sc->sc_q.c_cc, uio->uio_resid);
377 if (length > sizeof(buffer))
378 length = sizeof(buffer);
379
380 /* Remove a small chunk from the input queue. */
381 (void) q_to_b(&sc->sc_q, buffer, length);
382 DPRINTFN(5, ("uhidread: got %d chars\n", length));
383
384 /* Copy the data to the user process. */
385 if ((error = uiomove(buffer, length, uio)) != 0)
386 break;
387 }
388
389 return (error);
390 }
391
392 int
393 uhidwrite(dev, uio, flag)
394 dev_t dev;
395 struct uio *uio;
396 int flag;
397 {
398 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
399 int error;
400 int size;
401 usbd_status r;
402
403 if (sc->sc_disconnected)
404 return (EIO);
405
406 DPRINTFN(1, ("uhidwrite\n"));
407
408 size = sc->sc_osize;
409 error = 0;
410 while (uio->uio_resid > 0) {
411 if (uio->uio_resid != size)
412 return (0);
413 if ((error = uiomove(sc->sc_obuf, size, uio)) != 0)
414 break;
415 if (sc->sc_oid)
416 r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
417 sc->sc_obuf[0],
418 sc->sc_obuf+1, size-1);
419 else
420 r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
421 0, sc->sc_obuf, size);
422 if (r != USBD_NORMAL_COMPLETION) {
423 error = EIO;
424 break;
425 }
426 }
427 return (error);
428 }
429
430 int
431 uhidioctl(dev, cmd, addr, flag, p)
432 dev_t dev;
433 u_long cmd;
434 caddr_t addr;
435 int flag;
436 struct proc *p;
437 {
438 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
439 struct usb_ctl_report_desc *rd;
440 struct usb_ctl_report *re;
441 int size, id;
442 usbd_status r;
443
444 if (sc->sc_disconnected)
445 return (EIO);
446
447 DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
448 switch (cmd) {
449 case FIONBIO:
450 /* All handled in the upper FS layer. */
451 break;
452
453 case USB_GET_REPORT_DESC:
454 rd = (struct usb_ctl_report_desc *)addr;
455 size = min(sc->sc_repdesc_size, sizeof rd->data);
456 rd->size = size;
457 memcpy(rd->data, sc->sc_repdesc, size);
458 break;
459
460 case USB_SET_IMMED:
461 if (*(int *)addr) {
462 /* XXX should read into ibuf, but does it matter */
463 r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
464 sc->sc_iid, sc->sc_ibuf,
465 sc->sc_isize);
466 if (r != USBD_NORMAL_COMPLETION)
467 return (EOPNOTSUPP);
468 sc->sc_state |= UHID_IMMED;
469 } else
470 sc->sc_state &= ~UHID_IMMED;
471 break;
472
473 case USB_GET_REPORT:
474 re = (struct usb_ctl_report *)addr;
475 switch (re->report) {
476 case UHID_INPUT_REPORT:
477 size = sc->sc_isize;
478 id = sc->sc_iid;
479 break;
480 case UHID_OUTPUT_REPORT:
481 size = sc->sc_osize;
482 id = sc->sc_oid;
483 break;
484 case UHID_FEATURE_REPORT:
485 size = sc->sc_fsize;
486 id = sc->sc_fid;
487 break;
488 default:
489 return (EINVAL);
490 }
491 r = usbd_get_report(sc->sc_iface, re->report, id,
492 re->data, size);
493 if (r != USBD_NORMAL_COMPLETION)
494 return (EIO);
495 break;
496
497 default:
498 return (EINVAL);
499 }
500 return (0);
501 }
502
503 int
504 uhidpoll(dev, events, p)
505 dev_t dev;
506 int events;
507 struct proc *p;
508 {
509 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
510 int revents = 0;
511 int s;
512
513 if (sc->sc_disconnected)
514 return (EIO);
515
516 s = splusb();
517 if (events & (POLLOUT | POLLWRNORM))
518 revents |= events & (POLLOUT | POLLWRNORM);
519 if (events & (POLLIN | POLLRDNORM)) {
520 if (sc->sc_q.c_cc > 0)
521 revents |= events & (POLLIN | POLLRDNORM);
522 else
523 selrecord(p, &sc->sc_rsel);
524 }
525
526 splx(s);
527 return (revents);
528 }
529