uhid.c revision 1.2 1 /* $NetBSD: uhid.c,v 1.2 1998/07/13 10:49:41 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 mouse 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 sc->sc_disconnected = 1;
207 }
208
209 void
210 uhid_intr(reqh, addr, status)
211 usbd_request_handle reqh;
212 usbd_private_handle addr;
213 usbd_status status;
214 {
215 struct uhid_softc *sc = addr;
216
217 DPRINTFN(5, ("uhid_intr: status=%d\n", status));
218 DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
219 sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
220
221 if (status == USBD_CANCELLED)
222 return;
223
224 if (status != USBD_NORMAL_COMPLETION) {
225 DPRINTF(("uhid_intr: status=%d\n", status));
226 sc->sc_state |= UHID_NEEDCLEAR;
227 return;
228 }
229
230 (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
231
232 if (sc->sc_state & UHID_ASLP) {
233 sc->sc_state &= ~UHID_ASLP;
234 DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
235 wakeup((caddr_t)sc);
236 }
237 selwakeup(&sc->sc_rsel);
238 }
239
240 int
241 uhidopen(dev, flag, mode, p)
242 dev_t dev;
243 int flag;
244 int mode;
245 struct proc *p;
246 {
247 int unit = UHIDUNIT(dev);
248 struct uhid_softc *sc;
249 usbd_status r;
250
251 if (unit >= uhid_cd.cd_ndevs)
252 return ENXIO;
253 sc = uhid_cd.cd_devs[unit];
254 if (!sc)
255 return ENXIO;
256
257 DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
258
259 if (sc->sc_disconnected)
260 return (EIO);
261
262 if (sc->sc_state & UHID_OPEN)
263 return EBUSY;
264
265 if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1)
266 return ENOMEM;
267
268 sc->sc_state |= UHID_OPEN;
269 sc->sc_state &= ~UHID_IMMED;
270
271 sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK);
272 sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK);
273
274 /* Set up interrupt pipe. */
275 r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
276 USBD_SHORT_XFER_OK,
277 &sc->sc_intrpipe, sc, sc->sc_ibuf,
278 sc->sc_isize, uhid_intr);
279 if (r != USBD_NORMAL_COMPLETION) {
280 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, error=%d\n",r));
281 sc->sc_state &= ~UHID_OPEN;
282 return (EIO);
283 }
284 usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc);
285
286 return 0;
287 }
288
289 int
290 uhidclose(dev, flag, mode, p)
291 dev_t dev;
292 int flag;
293 int mode;
294 struct proc *p;
295 {
296 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
297
298 if (sc->sc_disconnected)
299 return (EIO);
300
301 DPRINTF(("uhidclose: sc=%p\n", sc));
302
303 /* Disable interrupts. */
304 usbd_abort_pipe(sc->sc_intrpipe);
305 usbd_close_pipe(sc->sc_intrpipe);
306
307 sc->sc_state &= ~UHID_OPEN;
308
309 clfree(&sc->sc_q);
310
311 free(sc->sc_ibuf, M_USB);
312 free(sc->sc_obuf, M_USB);
313
314 return 0;
315 }
316
317 int
318 uhidread(dev, uio, flag)
319 dev_t dev;
320 struct uio *uio;
321 int flag;
322 {
323 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
324 int s;
325 int error = 0;
326 size_t length;
327 u_char buffer[UHID_CHUNK];
328 usbd_status r;
329
330 if (sc->sc_disconnected)
331 return (EIO);
332
333 DPRINTFN(1, ("uhidread\n"));
334 if (sc->sc_state & UHID_IMMED) {
335 DPRINTFN(1, ("uhidread immed\n"));
336
337 r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
338 sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
339 if (r != USBD_NORMAL_COMPLETION)
340 return (EIO);
341 return (uiomove(buffer, sc->sc_isize, uio));
342 }
343
344 s = spltty();
345 while (sc->sc_q.c_cc == 0) {
346 if (flag & IO_NDELAY) {
347 splx(s);
348 return EWOULDBLOCK;
349 }
350 sc->sc_state |= UHID_ASLP;
351 DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
352 error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0);
353 DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
354 if (error) {
355 sc->sc_state &= ~UHID_ASLP;
356 splx(s);
357 return (error);
358 }
359 if (sc->sc_state & UHID_NEEDCLEAR) {
360 DPRINTFN(-1,("uhidread: clearing stall\n"));
361 sc->sc_state &= ~UHID_NEEDCLEAR;
362 usbd_clear_endpoint_stall(sc->sc_intrpipe);
363 }
364 }
365 splx(s);
366
367 /* Transfer as many chunks as possible. */
368 while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
369 length = min(sc->sc_q.c_cc, uio->uio_resid);
370 if (length > sizeof(buffer))
371 length = sizeof(buffer);
372
373 /* Remove a small chunk from the input queue. */
374 (void) q_to_b(&sc->sc_q, buffer, length);
375 DPRINTFN(5, ("uhidread: got %d chars\n", length));
376
377 /* Copy the data to the user process. */
378 if ((error = uiomove(buffer, length, uio)) != 0)
379 break;
380 }
381
382 return (error);
383 }
384
385 int
386 uhidwrite(dev, uio, flag)
387 dev_t dev;
388 struct uio *uio;
389 int flag;
390 {
391 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
392 int error;
393 int size;
394 usbd_status r;
395
396 if (sc->sc_disconnected)
397 return (EIO);
398
399 DPRINTFN(1, ("uhidwrite\n"));
400
401 size = sc->sc_osize;
402 error = 0;
403 while (uio->uio_resid > 0) {
404 if (uio->uio_resid != size)
405 return (0);
406 if ((error = uiomove(sc->sc_obuf, size, uio)) != 0)
407 break;
408 if (sc->sc_oid)
409 r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
410 sc->sc_obuf[0],
411 sc->sc_obuf+1, size-1);
412 else
413 r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
414 0, sc->sc_obuf, size);
415 if (r != USBD_NORMAL_COMPLETION) {
416 error = EIO;
417 break;
418 }
419 }
420 return (error);
421 }
422
423 int
424 uhidioctl(dev, cmd, addr, flag, p)
425 dev_t dev;
426 u_long cmd;
427 caddr_t addr;
428 int flag;
429 struct proc *p;
430 {
431 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
432 struct usb_ctl_report_desc *rd;
433 struct usb_ctl_report *re;
434 int size, id;
435 usbd_status r;
436
437 if (sc->sc_disconnected)
438 return (EIO);
439
440 DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
441 switch (cmd) {
442 case FIONBIO:
443 /* All handled in the upper FS layer. */
444 break;
445
446 case USB_GET_REPORT_DESC:
447 rd = (struct usb_ctl_report_desc *)addr;
448 size = min(sc->sc_repdesc_size, sizeof rd->data);
449 rd->size = size;
450 memcpy(rd->data, sc->sc_repdesc, size);
451 break;
452
453 case USB_SET_IMMED:
454 if (*(int *)addr)
455 sc->sc_state |= UHID_IMMED;
456 else
457 sc->sc_state &= ~UHID_IMMED;
458 break;
459
460 case USB_GET_REPORT:
461 re = (struct usb_ctl_report *)addr;
462 switch (re->report) {
463 case UHID_INPUT_REPORT:
464 size = sc->sc_isize;
465 id = sc->sc_iid;
466 break;
467 case UHID_OUTPUT_REPORT:
468 size = sc->sc_osize;
469 id = sc->sc_oid;
470 break;
471 case UHID_FEATURE_REPORT:
472 size = sc->sc_fsize;
473 id = sc->sc_fid;
474 break;
475 default:
476 return (EINVAL);
477 }
478 r = usbd_get_report(sc->sc_iface, re->report, id,
479 re->data, size);
480 if (r != USBD_NORMAL_COMPLETION)
481 return (EIO);
482 break;
483
484 default:
485 return (EINVAL);
486 }
487 return (0);
488 }
489
490 int
491 uhidpoll(dev, events, p)
492 dev_t dev;
493 int events;
494 struct proc *p;
495 {
496 struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
497 int revents = 0;
498 int s;
499
500 if (sc->sc_disconnected)
501 return (EIO);
502
503 s = spltty();
504 if (events & (POLLOUT | POLLWRNORM))
505 revents |= events & (POLLOUT | POLLWRNORM);
506 if (events & (POLLIN | POLLRDNORM))
507 if (sc->sc_q.c_cc > 0)
508 revents |= events & (POLLIN | POLLRDNORM);
509 else
510 selrecord(p, &sc->sc_rsel);
511
512 splx(s);
513 return (revents);
514 }
515