xref: /netbsd-src/sys/dev/usb/uhid.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: uhid.c,v 1.15 1999/01/10 11:13:36 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@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  * HID spec: http://www.usb.org/developers/data/usbhid10.pdf
42  */
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #if defined(__NetBSD__)
49 #include <sys/device.h>
50 #include <sys/ioctl.h>
51 #elif defined(__FreeBSD__)
52 #include <sys/ioccom.h>
53 #include <sys/filio.h>
54 #include <sys/module.h>
55 #include <sys/bus.h>
56 #include <sys/ioccom.h>
57 #endif
58 #include <sys/tty.h>
59 #include <sys/file.h>
60 #include <sys/select.h>
61 #include <sys/proc.h>
62 #include <sys/vnode.h>
63 #include <sys/poll.h>
64 
65 #include <dev/usb/usb.h>
66 #include <dev/usb/usbhid.h>
67 
68 #include <dev/usb/usbdi.h>
69 #include <dev/usb/usbdi_util.h>
70 #include <dev/usb/hid.h>
71 #include <dev/usb/usb_quirks.h>
72 
73 #ifdef USB_DEBUG
74 #define DPRINTF(x)	if (uhiddebug) printf x
75 #define DPRINTFN(n,x)	if (uhiddebug>(n)) printf x
76 int	uhiddebug = 0;
77 #else
78 #define DPRINTF(x)
79 #define DPRINTFN(n,x)
80 #endif
81 
82 struct uhid_softc {
83 	bdevice sc_dev;		/* base device */
84 	usbd_interface_handle sc_iface;	/* interface */
85 	usbd_pipe_handle sc_intrpipe;	/* interrupt pipe */
86 	int sc_ep_addr;
87 
88 	int sc_isize;
89 	int sc_osize;
90 	int sc_fsize;
91 	u_int8_t sc_iid;
92 	u_int8_t sc_oid;
93 	u_int8_t sc_fid;
94 
95 	char *sc_ibuf;
96 	char *sc_obuf;
97 
98 	void *sc_repdesc;
99 	int sc_repdesc_size;
100 
101 	struct clist sc_q;
102 	struct selinfo sc_rsel;
103 	u_char sc_state;	/* driver state */
104 #define	UHID_OPEN	0x01	/* device is open */
105 #define	UHID_ASLP	0x02	/* waiting for device data */
106 #define UHID_NEEDCLEAR	0x04	/* needs clearing endpoint stall */
107 #define UHID_IMMED	0x08	/* return read data immediately */
108 	int sc_disconnected;	/* device is gone */
109 };
110 
111 #define	UHIDUNIT(dev)	(minor(dev))
112 #define	UHID_CHUNK	128	/* chunk size for read */
113 #define	UHID_BSIZE	1020	/* buffer size */
114 
115 int uhidopen __P((dev_t, int, int, struct proc *));
116 int uhidclose __P((dev_t, int, int, struct proc *p));
117 int uhidread __P((dev_t, struct uio *uio, int));
118 int uhidwrite __P((dev_t, struct uio *uio, int));
119 int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
120 int uhidpoll __P((dev_t, int, struct proc *));
121 void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
122 void uhid_disco __P((void *));
123 
124 USB_DECLARE_DRIVER(uhid);
125 
126 USB_MATCH(uhid)
127 {
128 	USB_MATCH_START(uhid, uaa);
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 USB_ATTACH(uhid)
140 {
141 	USB_ATTACH_START(uhid, sc, uaa);
142 	usbd_interface_handle iface = uaa->iface;
143 	usb_interface_descriptor_t *id;
144 	usb_endpoint_descriptor_t *ed;
145 	int size;
146 	void *desc;
147 	usbd_status r;
148 	char devinfo[1024];
149 
150 	sc->sc_disconnected = 1;
151 	sc->sc_iface = iface;
152 	id = usbd_get_interface_descriptor(iface);
153 	usbd_devinfo(uaa->device, 0, devinfo);
154 	USB_ATTACH_SETUP;
155 	printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev),
156 	       devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
157 
158 	ed = usbd_interface2endpoint_descriptor(iface, 0);
159 	if (!ed) {
160 		printf("%s: could not read endpoint descriptor\n",
161 		       USBDEVNAME(sc->sc_dev));
162 		USB_ATTACH_ERROR_RETURN;
163 	}
164 
165 	DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d "
166 		     "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
167 		     " bInterval=%d\n",
168 		     ed->bLength, ed->bDescriptorType,
169 		     ed->bEndpointAddress & UE_ADDR,
170 		     ed->bEndpointAddress & UE_IN ? "in" : "out",
171 		     ed->bmAttributes & UE_XFERTYPE,
172 		     UGETW(ed->wMaxPacketSize), ed->bInterval));
173 
174 	if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
175 	    (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
176 		printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev));
177 		USB_ATTACH_ERROR_RETURN;
178 	}
179 
180 	sc->sc_ep_addr = ed->bEndpointAddress;
181 	sc->sc_disconnected = 0;
182 
183 	r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB);
184 	if (r != USBD_NORMAL_COMPLETION) {
185 		printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev));
186 		USB_ATTACH_ERROR_RETURN;
187 	}
188 
189 	(void)usbd_set_idle(iface, 0, 0);
190 
191 	sc->sc_isize = hid_report_size(desc, size, hid_input,   &sc->sc_iid);
192 	sc->sc_osize = hid_report_size(desc, size, hid_output,  &sc->sc_oid);
193 	sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
194 
195 	sc->sc_repdesc = desc;
196 	sc->sc_repdesc_size = size;
197 
198 	USB_ATTACH_SUCCESS_RETURN;
199 }
200 
201 #if defined(__FreeBSD__)
202 static int
203 uhid_detach(device_t self)
204 {
205         struct uhid_softc *sc = device_get_softc(self);
206 	char *devinfo = (char *) device_get_desc(self);
207 
208 	if (devinfo) {
209 		device_set_desc(self, NULL);
210 		free(devinfo, M_USB);
211 	}
212 	return 0;
213 }
214 #endif
215 
216 void
217 uhid_disco(p)
218 	void *p;
219 {
220 	struct uhid_softc *sc = p;
221 
222 	DPRINTF(("ums_hid: sc=%p\n", sc));
223 	usbd_abort_pipe(sc->sc_intrpipe);
224 	sc->sc_disconnected = 1;
225 }
226 
227 void
228 uhid_intr(reqh, addr, status)
229 	usbd_request_handle reqh;
230 	usbd_private_handle addr;
231 	usbd_status status;
232 {
233 	struct uhid_softc *sc = addr;
234 
235 	DPRINTFN(5, ("uhid_intr: status=%d\n", status));
236 	DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
237 		     sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
238 
239 	if (status == USBD_CANCELLED)
240 		return;
241 
242 	if (status != USBD_NORMAL_COMPLETION) {
243 		DPRINTF(("uhid_intr: status=%d\n", status));
244 		sc->sc_state |= UHID_NEEDCLEAR;
245 		return;
246 	}
247 
248 	(void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
249 
250 	if (sc->sc_state & UHID_ASLP) {
251 		sc->sc_state &= ~UHID_ASLP;
252 		DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
253 		wakeup((caddr_t)sc);
254 	}
255 	selwakeup(&sc->sc_rsel);
256 }
257 
258 int
259 uhidopen(dev, flag, mode, p)
260 	dev_t dev;
261 	int flag;
262 	int mode;
263 	struct proc *p;
264 {
265 	usbd_status r;
266 	USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc);
267 
268 	if (!sc)
269 		return (ENXIO);
270 
271 	DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
272 
273 	if (sc->sc_disconnected)
274 		return (EIO);
275 
276 	if (sc->sc_state & UHID_OPEN)
277 		return (EBUSY);
278 
279 #if defined(__NetBSD__)
280 	if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1)
281 		return (ENOMEM);
282 #elif defined(__FreeBSD__)
283 	clist_alloc_cblocks(&sc->sc_q, UHID_BSIZE, 0);
284 #endif
285 
286 	sc->sc_state |= UHID_OPEN;
287 	sc->sc_state &= ~UHID_IMMED;
288 
289 	sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK);
290 	sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK);
291 
292 	/* Set up interrupt pipe. */
293 	r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
294 				USBD_SHORT_XFER_OK,
295 				&sc->sc_intrpipe, sc, sc->sc_ibuf,
296 				sc->sc_isize, uhid_intr);
297 	if (r != USBD_NORMAL_COMPLETION) {
298 		DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
299 			 "error=%d\n",r));
300 		sc->sc_state &= ~UHID_OPEN;
301 		return (EIO);
302 	}
303 	usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc);
304 
305 	return (0);
306 }
307 
308 int
309 uhidclose(dev, flag, mode, p)
310 	dev_t dev;
311 	int flag;
312 	int mode;
313 	struct proc *p;
314 {
315 	USB_GET_SC(uhid, UHIDUNIT(dev), sc);
316 
317 	if (sc->sc_disconnected)
318 		return (EIO);
319 
320 	DPRINTF(("uhidclose: sc=%p\n", sc));
321 
322 	/* Disable interrupts. */
323 	usbd_abort_pipe(sc->sc_intrpipe);
324 	usbd_close_pipe(sc->sc_intrpipe);
325 
326 	sc->sc_state &= ~UHID_OPEN;
327 
328 #if defined(__NetBSD__)
329 	clfree(&sc->sc_q);
330 #elif defined(__FreeBSD__)
331 	clist_free_cblocks(&sc->sc_q);
332 #endif
333 
334 	free(sc->sc_ibuf, M_USB);
335 	free(sc->sc_obuf, M_USB);
336 
337 	return (0);
338 }
339 
340 int
341 uhidread(dev, uio, flag)
342 	dev_t dev;
343 	struct uio *uio;
344 	int flag;
345 {
346 	int s;
347 	int error = 0;
348 	size_t length;
349 	u_char buffer[UHID_CHUNK];
350 	usbd_status r;
351 	USB_GET_SC(uhid, UHIDUNIT(dev), sc);
352 
353 	if (sc->sc_disconnected)
354 		return (EIO);
355 
356 	DPRINTFN(1, ("uhidread\n"));
357 	if (sc->sc_state & UHID_IMMED) {
358 		DPRINTFN(1, ("uhidread immed\n"));
359 
360 		r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
361 				    sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
362 		if (r != USBD_NORMAL_COMPLETION)
363 			return (EIO);
364 		return (uiomove(buffer, sc->sc_isize, uio));
365 	}
366 
367 	s = splusb();
368 	while (sc->sc_q.c_cc == 0) {
369 		if (flag & IO_NDELAY) {
370 			splx(s);
371 			return EWOULDBLOCK;
372 		}
373 		sc->sc_state |= UHID_ASLP;
374 		DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
375 		error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0);
376 		DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
377 		if (error) {
378 			sc->sc_state &= ~UHID_ASLP;
379 			splx(s);
380 			return (error);
381 		}
382 		if (sc->sc_state & UHID_NEEDCLEAR) {
383 			DPRINTFN(-1,("uhidread: clearing stall\n"));
384 			sc->sc_state &= ~UHID_NEEDCLEAR;
385 			usbd_clear_endpoint_stall(sc->sc_intrpipe);
386 		}
387 	}
388 	splx(s);
389 
390 	/* Transfer as many chunks as possible. */
391 	while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
392 		length = min(sc->sc_q.c_cc, uio->uio_resid);
393 		if (length > sizeof(buffer))
394 			length = sizeof(buffer);
395 
396 		/* Remove a small chunk from the input queue. */
397 		(void) q_to_b(&sc->sc_q, buffer, length);
398 		DPRINTFN(5, ("uhidread: got %d chars\n", length));
399 
400 		/* Copy the data to the user process. */
401 		if ((error = uiomove(buffer, length, uio)) != 0)
402 			break;
403 	}
404 
405 	return (error);
406 }
407 
408 int
409 uhidwrite(dev, uio, flag)
410 	dev_t dev;
411 	struct uio *uio;
412 	int flag;
413 {
414 	int error;
415 	int size;
416 	usbd_status r;
417 	USB_GET_SC(uhid, UHIDUNIT(dev), sc);
418 
419 	if (sc->sc_disconnected)
420 		return (EIO);
421 
422 	DPRINTFN(1, ("uhidwrite\n"));
423 
424 	size = sc->sc_osize;
425 	error = 0;
426 	while (uio->uio_resid > 0) {
427 		if (uio->uio_resid != size)
428 			return (0);
429 		if ((error = uiomove(sc->sc_obuf, size, uio)) != 0)
430 			break;
431 		if (sc->sc_oid)
432 			r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
433 					    sc->sc_obuf[0],
434 					    sc->sc_obuf+1, size-1);
435 		else
436 			r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
437 					    0, sc->sc_obuf, size);
438 		if (r != USBD_NORMAL_COMPLETION) {
439 			error = EIO;
440 			break;
441 		}
442 	}
443 	return (error);
444 }
445 
446 int
447 uhidioctl(dev, cmd, addr, flag, p)
448 	dev_t dev;
449 	u_long cmd;
450 	caddr_t addr;
451 	int flag;
452 	struct proc *p;
453 {
454 	struct usb_ctl_report_desc *rd;
455 	struct usb_ctl_report *re;
456 	int size, id;
457 	usbd_status r;
458 	USB_GET_SC(uhid, UHIDUNIT(dev), sc);
459 
460 	if (sc->sc_disconnected)
461 		return (EIO);
462 
463 	DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
464 	switch (cmd) {
465 	case FIONBIO:
466 		/* All handled in the upper FS layer. */
467 		break;
468 
469 	case USB_GET_REPORT_DESC:
470 		rd = (struct usb_ctl_report_desc *)addr;
471 		size = min(sc->sc_repdesc_size, sizeof rd->data);
472 		rd->size = size;
473 		memcpy(rd->data, sc->sc_repdesc, size);
474 		break;
475 
476 	case USB_SET_IMMED:
477 		if (*(int *)addr) {
478 			/* XXX should read into ibuf, but does it matter */
479 			r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
480 					    sc->sc_iid, sc->sc_ibuf,
481 					    sc->sc_isize);
482 			if (r != USBD_NORMAL_COMPLETION)
483 				return (EOPNOTSUPP);
484 
485 			sc->sc_state |=  UHID_IMMED;
486 		} else
487 			sc->sc_state &= ~UHID_IMMED;
488 		break;
489 
490 	case USB_GET_REPORT:
491 		re = (struct usb_ctl_report *)addr;
492 		switch (re->report) {
493 		case UHID_INPUT_REPORT:
494 			size = sc->sc_isize;
495 			id = sc->sc_iid;
496 			break;
497 		case UHID_OUTPUT_REPORT:
498 			size = sc->sc_osize;
499 			id = sc->sc_oid;
500 			break;
501 		case UHID_FEATURE_REPORT:
502 			size = sc->sc_fsize;
503 			id = sc->sc_fid;
504 			break;
505 		default:
506 			return (EINVAL);
507 		}
508 		r = usbd_get_report(sc->sc_iface, re->report, id,
509 				    re->data, size);
510 		if (r != USBD_NORMAL_COMPLETION)
511 			return (EIO);
512 		break;
513 
514 	default:
515 		return (EINVAL);
516 	}
517 	return (0);
518 }
519 
520 int
521 uhidpoll(dev, events, p)
522 	dev_t dev;
523 	int events;
524 	struct proc *p;
525 {
526 	int revents = 0;
527 	int s;
528 	USB_GET_SC(uhid, UHIDUNIT(dev), sc);
529 
530 	if (sc->sc_disconnected)
531 		return (EIO);
532 
533 	s = splusb();
534 	if (events & (POLLOUT | POLLWRNORM))
535 		revents |= events & (POLLOUT | POLLWRNORM);
536 	if (events & (POLLIN | POLLRDNORM)) {
537 		if (sc->sc_q.c_cc > 0)
538 			revents |= events & (POLLIN | POLLRDNORM);
539 		else
540 			selrecord(p, &sc->sc_rsel);
541 	}
542 
543 	splx(s);
544 	return (revents);
545 }
546 
547 #if defined(__FreeBSD__)
548 DRIVER_MODULE(uhid, usb, uhid_driver, uhid_devclass, usbd_driver_load, 0);
549 #endif
550