Home | History | Annotate | Line # | Download | only in usb
ukbd.c revision 1.1
      1 /*	$NetBSD: ukbd.c,v 1.1 1998/07/12 19:52:00 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 
     56 #include <dev/usb/usbdi.h>
     57 #include <dev/usb/usbdi_util.h>
     58 #include <dev/usb/usbdevs.h>
     59 #include <dev/usb/usb_quirks.h>
     60 #include <dev/usb/hid.h>
     61 
     62 #ifdef USB_DEBUG
     63 #define DPRINTF(x)	if (ukbddebug) printf x
     64 #define DPRINTFN(n,x)	if (ukbddebug>(n)) printf x
     65 int	ukbddebug = 0;
     66 #else
     67 #define DPRINTF(x)
     68 #define DPRINTFN(n,x)
     69 #endif
     70 
     71 #define UPROTO_BOOT_KEYBOARD 1
     72 
     73 #define NKEYCODE 6
     74 
     75 struct ukbd_data {
     76 	u_int8_t	modifiers;
     77 #define MOD_CONTROL_L	0x01
     78 #define MOD_CONTROL_R	0x10
     79 #define MOD_SHIFT_L	0x02
     80 #define MOD_SHIFT_R	0x20
     81 #define MOD_ALT_L	0x04
     82 #define MOD_ALT_R	0x40
     83 #define MOD_WIN_L	0x08
     84 #define MOD_WIN_R	0x80
     85 	u_int8_t	reserved;
     86 	u_int8_t	keycode[NKEYCODE];
     87 };
     88 
     89 #define PRESS 0
     90 #define RELEASE 0x80
     91 
     92 #define NMOD 6
     93 static struct {
     94 	int mask, key;
     95 } ukbd_mods[NMOD] = {
     96 	{ MOD_CONTROL_L, 29 },
     97 	{ MOD_CONTROL_R, 58 },
     98 	{ MOD_SHIFT_L,   42 },
     99 	{ MOD_SHIFT_R,   54 },
    100 	{ MOD_ALT_L,     56 },
    101 	{ MOD_ALT_R,     56 },
    102 };
    103 
    104 #define XX 0
    105 /* Translate USB keycodes to US keyboard scancodes. */
    106 /* XXX very incomplete */
    107 static char ukbd_trtab[256] = {
    108 	   0,   0,   0,   0,  30,  48,  46,  32,
    109 	  18,  33,  34,  35,  23,  36,  37,  38,
    110 	  50,  49,  24,  25,  16,  19,  31,  20,
    111 	  22,  47,  17,  45,  21,  44,   2,   3,
    112 	   4,   5,   6,   7,   8,   9,  10,  11,
    113 	  28,   1,  14,  15,  57,  12,  13,  26,
    114 	  27,  43,  XX,  39,  40,  41,  51,  52,
    115 	  53,  58,  59,  60,  61,  62,  63,  64,
    116 	  65,  66,  67,  68,  87,  88,  XX,  70,
    117 	  XX,  XX,  XX,  XX,  XX,
    118 };
    119 
    120 #define KEY_ERROR 0x01
    121 
    122 struct ukbd_softc {
    123 	struct device sc_dev;		/* base device */
    124 	usbd_interface_handle sc_iface;	/* interface */
    125 	usbd_pipe_handle sc_intrpipe;	/* interrupt pipe */
    126 	int sc_ep_addr;
    127 
    128 	struct ukbd_data sc_ndata;
    129 	struct ukbd_data sc_odata;
    130 
    131 	struct clist sc_q;
    132 	struct selinfo sc_rsel;
    133 	u_char sc_state;	/* keyboard driver state */
    134 #define	UKBD_OPEN	0x01	/* device is open */
    135 #define	UKBD_ASLP	0x02	/* waiting for keyboard data */
    136 #define UKBD_NEEDCLEAR	0x04	/* needs clearing endpoint stall */
    137 
    138 	int sc_disconnected;		/* device is gone */
    139 };
    140 
    141 #define	UKBDUNIT(dev)	(minor(dev))
    142 #define	UKBD_CHUNK	128	/* chunk size for read */
    143 #define	UKBD_BSIZE	1020	/* buffer size */
    144 
    145 int ukbd_match __P((struct device *, struct cfdata *, void *));
    146 void ukbd_attach __P((struct device *, struct device *, void *));
    147 
    148 int ukbdopen __P((dev_t, int, int, struct proc *));
    149 int ukbdclose __P((dev_t, int, int, struct proc *p));
    150 int ukbdread __P((dev_t, struct uio *uio, int));
    151 int ukbdioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
    152 int ukbdpoll __P((dev_t, int, struct proc *));
    153 void ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
    154 void ukbd_disco __P((void *));
    155 
    156 extern struct cfdriver ukbd_cd;
    157 
    158 struct cfattach ukbd_ca = {
    159 	sizeof(struct ukbd_softc), ukbd_match, ukbd_attach
    160 };
    161 
    162 int
    163 ukbd_match(parent, match, aux)
    164 	struct device *parent;
    165 	struct cfdata *match;
    166 	void *aux;
    167 {
    168 	struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
    169 	usb_interface_descriptor_t *id;
    170 
    171 	/* Check that this is a keyboard that speaks the boot protocol. */
    172 	if (!uaa->iface)
    173 		return (UMATCH_NONE);
    174 	id = usbd_get_interface_descriptor(uaa->iface);
    175 	if (id->bInterfaceClass != UCLASS_HID ||
    176 	    id->bInterfaceSubClass != USUBCLASS_BOOT ||
    177 	    id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD)
    178 		return (UMATCH_NONE);
    179 	return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
    180 }
    181 
    182 void
    183 ukbd_attach(parent, self, aux)
    184 	struct device *parent;
    185 	struct device *self;
    186 	void *aux;
    187 {
    188 	struct ukbd_softc *sc = (struct ukbd_softc *)self;
    189 	struct usb_attach_arg *uaa = aux;
    190 	usbd_interface_handle iface = uaa->iface;
    191 	usb_interface_descriptor_t *id;
    192 	usb_endpoint_descriptor_t *ed;
    193 	usbd_status r;
    194 	char devinfo[1024];
    195 
    196 	sc->sc_disconnected = 1;
    197 	sc->sc_iface = iface;
    198 	id = usbd_get_interface_descriptor(iface);
    199 	usbd_devinfo(uaa->device, 0, devinfo);
    200 	printf(": %s (interface class %d/%d)\n", devinfo,
    201 	       id->bInterfaceClass, id->bInterfaceSubClass);
    202 	ed = usbd_interface2endpoint_descriptor(iface, 0);
    203 	if (!ed) {
    204 		printf("%s: could not read endpoint descriptor\n",
    205 		       sc->sc_dev.dv_xname);
    206 		return;
    207 	}
    208 
    209 	DPRINTFN(10,("ukbd_attach: \
    210 bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
    211 	       ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
    212 	       ed->bEndpointAddress & UE_IN ? "in" : "out",
    213 	       ed->bmAttributes & UE_XFERTYPE,
    214 	       UGETW(ed->wMaxPacketSize), ed->bInterval));
    215 
    216 	if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
    217 	    (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
    218 		printf("%s: unexpected endpoint\n",
    219 		       sc->sc_dev.dv_xname);
    220 		return;
    221 	}
    222 
    223 	if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
    224 		r = usbd_set_protocol(iface, 0);
    225 		DPRINTFN(5, ("ukbd_attach: protocol set\n"));
    226 		if (r != USBD_NORMAL_COMPLETION) {
    227 			printf("%s: set protocol failed\n",
    228 			       sc->sc_dev.dv_xname);
    229 			return;
    230 		}
    231 	}
    232 
    233 	sc->sc_ep_addr = ed->bEndpointAddress;
    234 	sc->sc_disconnected = 0;
    235 }
    236 
    237 void
    238 ukbd_disco(p)
    239 	void *p;
    240 {
    241 	struct ukbd_softc *sc = p;
    242 	sc->sc_disconnected = 1;
    243 }
    244 
    245 void
    246 ukbd_intr(reqh, addr, status)
    247 	usbd_request_handle reqh;
    248 	usbd_private_handle addr;
    249 	usbd_status status;
    250 {
    251 	struct ukbd_softc *sc = addr;
    252 	struct ukbd_data *ud = &sc->sc_ndata;
    253 	int mod, omod;
    254 	char ibuf[NMOD+2*NKEYCODE];	/* chars events */
    255 	int nkeys, i, j;
    256 	int key, ch;
    257 #define ADDKEY(c) ibuf[nkeys++] = (c)
    258 
    259 	DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
    260 	if (status == USBD_CANCELLED)
    261 		return;
    262 
    263 	if (status != USBD_NORMAL_COMPLETION) {
    264 		DPRINTF(("ukbd_intr: status=%d\n", status));
    265 		sc->sc_state |= UKBD_NEEDCLEAR;
    266 		return;
    267 	}
    268 
    269 	DPRINTFN(5, ("          mod=0x%02x key0=0x%02x key1=0x%02x\n",
    270 		     ud->modifiers, ud->keycode[0], ud->keycode[1]));
    271 
    272 	if (ud->keycode[0] == KEY_ERROR)
    273 		return;		/* ignore  */
    274 	nkeys = 0;
    275 	mod = ud->modifiers;
    276 	omod = sc->sc_odata.modifiers;
    277 	if (mod != omod)
    278 		for (i = 0; i < NMOD; i++)
    279 			if (( mod & ukbd_mods[i].mask) !=
    280 			    (omod & ukbd_mods[i].mask))
    281 				ADDKEY(ukbd_mods[i].key |
    282 				       (mod & ukbd_mods[i].mask
    283 					  ? PRESS : RELEASE));
    284 	if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) {
    285 		/* Check for released keys. */
    286 		for (i = 0; i < NKEYCODE; i++) {
    287 			key = sc->sc_odata.keycode[i];
    288 			if (key == 0)
    289 				continue;
    290 			for (j = 0; j < NKEYCODE; j++)
    291 				if (key == ud->keycode[j])
    292 					goto rfound;
    293 			ch = ukbd_trtab[key];
    294 			if (ch)
    295 				ADDKEY(ch | RELEASE);
    296 		rfound:
    297 			;
    298 		}
    299 
    300 		/* Check for pressed keys. */
    301 		for (i = 0; i < NKEYCODE; i++) {
    302 			key = ud->keycode[i];
    303 			if (key == 0)
    304 				continue;
    305 			for (j = 0; j < NKEYCODE; j++)
    306 				if (key == sc->sc_odata.keycode[j])
    307 					goto pfound;
    308 			ch = ukbd_trtab[key];
    309 			if (ch)
    310 				ADDKEY(ch | PRESS);
    311 		pfound:
    312 			;
    313 		}
    314 	}
    315 	sc->sc_odata = *ud;
    316 
    317 	if (nkeys) {
    318 		b_to_q(ibuf, nkeys, &sc->sc_q);
    319 
    320 		if (sc->sc_state & UKBD_ASLP) {
    321 			sc->sc_state &= ~UKBD_ASLP;
    322 			DPRINTFN(5, ("ukbd_intr: waking %p\n", sc));
    323 			wakeup((caddr_t)sc);
    324 		}
    325 		selwakeup(&sc->sc_rsel);
    326 	}
    327 }
    328 
    329 int
    330 ukbdopen(dev, flag, mode, p)
    331 	dev_t dev;
    332 	int flag;
    333 	int mode;
    334 	struct proc *p;
    335 {
    336 	int unit = UKBDUNIT(dev);
    337 	struct ukbd_softc *sc;
    338 	usbd_status r;
    339 
    340 	if (unit >= ukbd_cd.cd_ndevs)
    341 		return ENXIO;
    342 	sc = ukbd_cd.cd_devs[unit];
    343 	if (!sc)
    344 		return ENXIO;
    345 
    346 	DPRINTF(("ukbdopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
    347 
    348 	if (sc->sc_disconnected)
    349 		return (EIO);
    350 
    351 	if (sc->sc_state & UKBD_OPEN)
    352 		return EBUSY;
    353 
    354 	if (clalloc(&sc->sc_q, UKBD_BSIZE, 0) == -1)
    355 		return ENOMEM;
    356 
    357 	sc->sc_state |= UKBD_OPEN;
    358 
    359 	/* Set up interrupt pipe. */
    360 	r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
    361 				USBD_SHORT_XFER_OK,
    362 				&sc->sc_intrpipe, sc, &sc->sc_ndata,
    363 				sizeof(sc->sc_ndata), ukbd_intr);
    364 	if (r != USBD_NORMAL_COMPLETION) {
    365 		DPRINTF(("ukbdopen: usbd_open_pipe_intr failed, error=%d\n",r));
    366 		sc->sc_state &= ~UKBD_OPEN;
    367 		clfree(&sc->sc_q);
    368 		return (EIO);
    369 	}
    370 	usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc);
    371 	return 0;
    372 }
    373 
    374 int
    375 ukbdclose(dev, flag, mode, p)
    376 	dev_t dev;
    377 	int flag;
    378 	int mode;
    379 	struct proc *p;
    380 {
    381 	struct ukbd_softc *sc = ukbd_cd.cd_devs[UKBDUNIT(dev)];
    382 
    383 	if (sc->sc_disconnected)
    384 		return (EIO);
    385 
    386 	/* Disable interrupts. */
    387 	usbd_abort_pipe(sc->sc_intrpipe);
    388 	usbd_close_pipe(sc->sc_intrpipe);
    389 
    390 	sc->sc_state &= ~UKBD_OPEN;
    391 
    392 	clfree(&sc->sc_q);
    393 
    394 	return 0;
    395 }
    396 
    397 int
    398 ukbdread(dev, uio, flag)
    399 	dev_t dev;
    400 	struct uio *uio;
    401 	int flag;
    402 {
    403 	struct ukbd_softc *sc = ukbd_cd.cd_devs[UKBDUNIT(dev)];
    404 	int s;
    405 	int error = 0;
    406 	size_t length;
    407 	u_char buffer[UKBD_CHUNK];
    408 
    409 	if (sc->sc_disconnected)
    410 		return (EIO);
    411 
    412 	/* Block until keyboard activity occured. */
    413 	s = spltty();
    414 	while (sc->sc_q.c_cc == 0) {
    415 		if (flag & IO_NDELAY) {
    416 			splx(s);
    417 			return EWOULDBLOCK;
    418 		}
    419 		sc->sc_state |= UKBD_ASLP;
    420 		DPRINTFN(5, ("ukbdread: sleep on %p\n", sc));
    421 		error = tsleep((caddr_t)sc, PZERO | PCATCH, "ukbdrea", 0);
    422 		DPRINTFN(5, ("ukbdread: woke, error=%d\n", error));
    423 		if (error) {
    424 			sc->sc_state &= ~UKBD_ASLP;
    425 			splx(s);
    426 			return error;
    427 		}
    428 	}
    429 	splx(s);
    430 
    431 	/* Transfer as many chunks as possible. */
    432 	while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
    433 		length = min(sc->sc_q.c_cc, uio->uio_resid);
    434 		if (length > sizeof(buffer))
    435 			length = sizeof(buffer);
    436 
    437 		/* Remove a small chunk from the input queue. */
    438 		(void) q_to_b(&sc->sc_q, buffer, length);
    439 		DPRINTFN(5, ("ukbdread: got %d chars\n", length));
    440 
    441 		/* Copy the data to the user process. */
    442 		if ((error = uiomove(buffer, length, uio)) != 0)
    443 			break;
    444 	}
    445 
    446 	return error;
    447 }
    448 
    449 int
    450 ukbdioctl(dev, cmd, addr, flag, p)
    451 	dev_t dev;
    452 	u_long cmd;
    453 	caddr_t addr;
    454 	int flag;
    455 	struct proc *p;
    456 {
    457 	struct ukbd_softc *sc = ukbd_cd.cd_devs[UKBDUNIT(dev)];
    458 
    459 	if (sc->sc_disconnected)
    460 		return (EIO);
    461 
    462 	return EINVAL;
    463 }
    464 
    465 int
    466 ukbdpoll(dev, events, p)
    467 	dev_t dev;
    468 	int events;
    469 	struct proc *p;
    470 {
    471 	struct ukbd_softc *sc = ukbd_cd.cd_devs[UKBDUNIT(dev)];
    472 	int revents = 0;
    473 	int s;
    474 
    475 	if (sc->sc_disconnected)
    476 		return (EIO);
    477 
    478 	s = spltty();
    479 	if (events & (POLLIN | POLLRDNORM))
    480 		if (sc->sc_q.c_cc > 0)
    481 			revents |= events & (POLLIN | POLLRDNORM);
    482 		else
    483 			selrecord(p, &sc->sc_rsel);
    484 
    485 	splx(s);
    486 	return (revents);
    487 }
    488