Home | History | Annotate | Line # | Download | only in usb
uhid.c revision 1.1
      1 /*	$NetBSD: uhid.c,v 1.1 1998/07/12 19:51:59 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 	u_int8_t sc_iid;
     80 	u_int8_t sc_oid;
     81 
     82 	char *sc_ibuf;
     83 	char *sc_obuf;
     84 
     85 	void *sc_repdesc;
     86 	int sc_repdesc_size;
     87 
     88 	struct clist sc_q;
     89 	struct selinfo sc_rsel;
     90 	u_char sc_state;	/* driver state */
     91 #define	UHID_OPEN	0x01	/* device is open */
     92 #define	UHID_ASLP	0x02	/* waiting for mouse data */
     93 #define UHID_NEEDCLEAR	0x04	/* needs clearing endpoint stall */
     94 	int sc_disconnected;	/* device is gone */
     95 };
     96 
     97 #define	UHIDUNIT(dev)	(minor(dev))
     98 #define	UHID_CHUNK	128	/* chunk size for read */
     99 #define	UHID_BSIZE	1020	/* buffer size */
    100 
    101 int uhid_match __P((struct device *, struct cfdata *, void *));
    102 void uhid_attach __P((struct device *, struct device *, void *));
    103 
    104 int uhidopen __P((dev_t, int, int, struct proc *));
    105 int uhidclose __P((dev_t, int, int, struct proc *p));
    106 int uhidread __P((dev_t, struct uio *uio, int));
    107 int uhidwrite __P((dev_t, struct uio *uio, int));
    108 int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
    109 int uhidpoll __P((dev_t, int, struct proc *));
    110 void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
    111 void uhid_disco __P((void *));
    112 
    113 extern struct cfdriver uhid_cd;
    114 
    115 struct cfattach uhid_ca = {
    116 	sizeof(struct uhid_softc), uhid_match, uhid_attach
    117 };
    118 
    119 int
    120 uhid_match(parent, match, aux)
    121 	struct device *parent;
    122 	struct cfdata *match;
    123 	void *aux;
    124 {
    125 	struct usb_attach_arg *uaa = aux;
    126 	usb_interface_descriptor_t *id;
    127 
    128 	if (!uaa->iface)
    129 		return (UMATCH_NONE);
    130 	id = usbd_get_interface_descriptor(uaa->iface);
    131 	if (!id || id->bInterfaceClass != UCLASS_HID)
    132 		return (UMATCH_NONE);
    133 	return (UMATCH_IFACECLASS_GENERIC);
    134 }
    135 
    136 void
    137 uhid_attach(parent, self, aux)
    138 	struct device *parent;
    139 	struct device *self;
    140 	void *aux;
    141 {
    142 	struct uhid_softc *sc = (struct uhid_softc *)self;
    143 	struct usb_attach_arg *uaa = aux;
    144 	usbd_interface_handle iface = uaa->iface;
    145 	usb_interface_descriptor_t *id;
    146 	usb_endpoint_descriptor_t *ed;
    147 	int size;
    148 	void *desc;
    149 	usbd_status r;
    150 	char devinfo[1024];
    151 
    152 	sc->sc_disconnected = 1;
    153 	sc->sc_iface = iface;
    154 	id = usbd_get_interface_descriptor(iface);
    155 	usbd_devinfo(uaa->device, 0, devinfo);
    156 	printf(": %s (interface class %d/%d)\n", devinfo,
    157 	       id->bInterfaceClass, id->bInterfaceSubClass);
    158 	ed = usbd_interface2endpoint_descriptor(iface, 0);
    159 	if (!ed) {
    160 		printf("%s: could not read endpoint descriptor\n",
    161 		       sc->sc_dev.dv_xname);
    162 		return;
    163 	}
    164 
    165 	DPRINTFN(10,("uhid_attach: \
    166 bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
    167 	       ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
    168 	       ed->bEndpointAddress & UE_IN ? "in" : "out",
    169 	       ed->bmAttributes & UE_XFERTYPE,
    170 	       UGETW(ed->wMaxPacketSize), ed->bInterval));
    171 
    172 	if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
    173 	    (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
    174 		printf("%s: unexpected endpoint\n",
    175 		       sc->sc_dev.dv_xname);
    176 		return;
    177 	}
    178 
    179 	sc->sc_ep_addr = ed->bEndpointAddress;
    180 	sc->sc_disconnected = 0;
    181 
    182 	r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB);
    183 	if (r != USBD_NORMAL_COMPLETION) {
    184 		printf("%s: no report descriptor\n", sc->sc_dev.dv_xname);
    185 		return;
    186 	}
    187 
    188 	(void)usbd_set_idle(iface, 0, 0);
    189 
    190 	sc->sc_isize = hid_report_size(desc, size, hid_input,  &sc->sc_iid);
    191 	sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid);
    192 
    193 	sc->sc_repdesc = desc;
    194 	sc->sc_repdesc_size = size;
    195 }
    196 
    197 void
    198 uhid_disco(p)
    199 	void *p;
    200 {
    201 	struct uhid_softc *sc = p;
    202 	sc->sc_disconnected = 1;
    203 }
    204 
    205 void
    206 uhid_intr(reqh, addr, status)
    207 	usbd_request_handle reqh;
    208 	usbd_private_handle addr;
    209 	usbd_status status;
    210 {
    211 	struct uhid_softc *sc = addr;
    212 
    213 	DPRINTFN(5, ("uhid_intr: status=%d\n", status));
    214 	DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
    215 		     sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
    216 
    217 	if (status == USBD_CANCELLED)
    218 		return;
    219 
    220 	if (status != USBD_NORMAL_COMPLETION) {
    221 		DPRINTF(("uhid_intr: status=%d\n", status));
    222 		sc->sc_state |= UHID_NEEDCLEAR;
    223 		return;
    224 	}
    225 
    226 	(void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
    227 
    228 	if (sc->sc_state & UHID_ASLP) {
    229 		sc->sc_state &= ~UHID_ASLP;
    230 		DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
    231 		wakeup((caddr_t)sc);
    232 	}
    233 	selwakeup(&sc->sc_rsel);
    234 }
    235 
    236 int
    237 uhidopen(dev, flag, mode, p)
    238 	dev_t dev;
    239 	int flag;
    240 	int mode;
    241 	struct proc *p;
    242 {
    243 	int unit = UHIDUNIT(dev);
    244 	struct uhid_softc *sc;
    245 	usbd_status r;
    246 
    247 	if (unit >= uhid_cd.cd_ndevs)
    248 		return ENXIO;
    249 	sc = uhid_cd.cd_devs[unit];
    250 	if (!sc)
    251 		return ENXIO;
    252 
    253 	DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
    254 
    255 	if (sc->sc_disconnected)
    256 		return (EIO);
    257 
    258 	if (sc->sc_state & UHID_OPEN)
    259 		return EBUSY;
    260 
    261 	if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1)
    262 		return ENOMEM;
    263 
    264 	sc->sc_state |= UHID_OPEN;
    265 
    266 	sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK);
    267 	sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK);
    268 
    269 	/* Set up interrupt pipe. */
    270 	r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
    271 				USBD_SHORT_XFER_OK,
    272 				&sc->sc_intrpipe, sc, sc->sc_ibuf,
    273 				sc->sc_isize, uhid_intr);
    274 	if (r != USBD_NORMAL_COMPLETION) {
    275 		DPRINTF(("uhidopen: usbd_open_pipe_intr failed, error=%d\n",r));
    276 		sc->sc_state &= ~UHID_OPEN;
    277 		return (EIO);
    278 	}
    279 	usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc);
    280 
    281 	return 0;
    282 }
    283 
    284 int
    285 uhidclose(dev, flag, mode, p)
    286 	dev_t dev;
    287 	int flag;
    288 	int mode;
    289 	struct proc *p;
    290 {
    291 	struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
    292 
    293 	if (sc->sc_disconnected)
    294 		return (EIO);
    295 
    296 	DPRINTF(("uhidclose: sc=%p\n", sc));
    297 
    298 	/* Disable interrupts. */
    299 	usbd_abort_pipe(sc->sc_intrpipe);
    300 	usbd_close_pipe(sc->sc_intrpipe);
    301 
    302 	sc->sc_state &= ~UHID_OPEN;
    303 
    304 	clfree(&sc->sc_q);
    305 
    306 	free(sc->sc_ibuf, M_USB);
    307 	free(sc->sc_obuf, M_USB);
    308 
    309 	return 0;
    310 }
    311 
    312 int
    313 uhidread(dev, uio, flag)
    314 	dev_t dev;
    315 	struct uio *uio;
    316 	int flag;
    317 {
    318 	struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
    319 	int s;
    320 	int error = 0;
    321 	size_t length;
    322 	u_char buffer[UHID_CHUNK];
    323 
    324 	if (sc->sc_disconnected)
    325 		return (EIO);
    326 
    327 	DPRINTFN(1, ("uhidread\n"));
    328 	s = spltty();
    329 	while (sc->sc_q.c_cc == 0) {
    330 		if (flag & IO_NDELAY) {
    331 			splx(s);
    332 			return EWOULDBLOCK;
    333 		}
    334 		sc->sc_state |= UHID_ASLP;
    335 		DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
    336 		error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0);
    337 		DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
    338 		if (error) {
    339 			sc->sc_state &= ~UHID_ASLP;
    340 			splx(s);
    341 			return (error);
    342 		}
    343 		if (sc->sc_state & UHID_NEEDCLEAR) {
    344 			DPRINTFN(-1,("uhidread: clearing stall\n"));
    345 			sc->sc_state &= ~UHID_NEEDCLEAR;
    346 			usbd_clear_endpoint_stall(sc->sc_intrpipe);
    347 		}
    348 	}
    349 	splx(s);
    350 
    351 	/* Transfer as many chunks as possible. */
    352 	while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
    353 		length = min(sc->sc_q.c_cc, uio->uio_resid);
    354 		if (length > sizeof(buffer))
    355 			length = sizeof(buffer);
    356 
    357 		/* Remove a small chunk from the input queue. */
    358 		(void) q_to_b(&sc->sc_q, buffer, length);
    359 		DPRINTFN(5, ("uhidread: got %d chars\n", length));
    360 
    361 		/* Copy the data to the user process. */
    362 		if ((error = uiomove(buffer, length, uio)) != 0)
    363 			break;
    364 	}
    365 
    366 	return (error);
    367 }
    368 
    369 int
    370 uhidwrite(dev, uio, flag)
    371 	dev_t dev;
    372 	struct uio *uio;
    373 	int flag;
    374 {
    375 	struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
    376 	int error;
    377 	int size;
    378 	usbd_status r;
    379 
    380 	if (sc->sc_disconnected)
    381 		return (EIO);
    382 
    383 	DPRINTFN(1, ("uhidwrite\n"));
    384 
    385 	size = sc->sc_osize;
    386 	error = 0;
    387 	while (uio->uio_resid > 0) {
    388 		if (uio->uio_resid != size)
    389 			return (0);
    390 		if ((error = uiomove(sc->sc_obuf, size, uio)) != 0)
    391 			break;
    392 		if (sc->sc_oid)
    393 			r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
    394 					    sc->sc_obuf[0],
    395 					    sc->sc_obuf+1, size-1);
    396 		else
    397 			r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
    398 					    0, sc->sc_obuf, size);
    399 		if (r != USBD_NORMAL_COMPLETION) {
    400 			error = EIO;
    401 			break;
    402 		}
    403 	}
    404 	return (error);
    405 }
    406 
    407 int
    408 uhidioctl(dev, cmd, addr, flag, p)
    409 	dev_t dev;
    410 	u_long cmd;
    411 	caddr_t addr;
    412 	int flag;
    413 	struct proc *p;
    414 {
    415 	struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
    416 	struct usb_ctl_report_desc *rd;
    417 	int size;
    418 
    419 	if (sc->sc_disconnected)
    420 		return (EIO);
    421 
    422 	DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
    423 	switch (cmd) {
    424 	case USB_GET_REPORT_DESC:
    425 		rd = (struct usb_ctl_report_desc *)addr;
    426 		size = min(sc->sc_repdesc_size, sizeof rd->data);
    427 		rd->size = size;
    428 		memcpy(rd->data, sc->sc_repdesc, size);
    429 		break;
    430 	default:
    431 		return (EINVAL);
    432 	}
    433 	return (0);
    434 }
    435 
    436 int
    437 uhidpoll(dev, events, p)
    438 	dev_t dev;
    439 	int events;
    440 	struct proc *p;
    441 {
    442 	struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
    443 	int revents = 0;
    444 	int s;
    445 
    446 	if (sc->sc_disconnected)
    447 		return (EIO);
    448 
    449 	s = spltty();
    450 	if (events & (POLLOUT | POLLWRNORM))
    451 		revents |= events & (POLLOUT | POLLWRNORM);
    452 	if (events & (POLLIN | POLLRDNORM))
    453 		if (sc->sc_q.c_cc > 0)
    454 			revents |= events & (POLLIN | POLLRDNORM);
    455 		else
    456 			selrecord(p, &sc->sc_rsel);
    457 
    458 	splx(s);
    459 	return (revents);
    460 }
    461