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