Home | History | Annotate | Line # | Download | only in usb
ukbd.c revision 1.3
      1 /*	$NetBSD: ukbd.c,v 1.3 1998/07/26 17:42:49 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 #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 #include <dev/wscons/wsconsio.h>
     63 #include <dev/wscons/wskbdvar.h>
     64 #include <dev/wscons/wsksymdef.h>
     65 #include <dev/wscons/wsksymvar.h>
     66 #include <dev/wscons/wskbdmap_mfii.h>
     67 
     68 #include "opt_pckbd_layout.h"
     69 #include "opt_wsdisplay_compat.h"
     70 
     71 #ifdef USB_DEBUG
     72 #define DPRINTF(x)	if (ukbddebug) printf x
     73 #define DPRINTFN(n,x)	if (ukbddebug>(n)) printf x
     74 int	ukbddebug = 0;
     75 #else
     76 #define DPRINTF(x)
     77 #define DPRINTFN(n,x)
     78 #endif
     79 
     80 #define UPROTO_BOOT_KEYBOARD 1
     81 
     82 #define NKEYCODE 6
     83 
     84 #define NUM_LOCK 0x01
     85 #define CAPS_LOCK 0x02
     86 #define SCROLL_LOCK 0x04
     87 
     88 struct ukbd_data {
     89 	u_int8_t	modifiers;
     90 #define MOD_CONTROL_L	0x01
     91 #define MOD_CONTROL_R	0x10
     92 #define MOD_SHIFT_L	0x02
     93 #define MOD_SHIFT_R	0x20
     94 #define MOD_ALT_L	0x04
     95 #define MOD_ALT_R	0x40
     96 #define MOD_WIN_L	0x08
     97 #define MOD_WIN_R	0x80
     98 	u_int8_t	reserved;
     99 	u_int8_t	keycode[NKEYCODE];
    100 };
    101 
    102 #define PRESS 0
    103 #define RELEASE 0x80
    104 
    105 #define NMOD 6
    106 static struct {
    107 	int mask, key;
    108 } ukbd_mods[NMOD] = {
    109 	{ MOD_CONTROL_L, 29 },
    110 	{ MOD_CONTROL_R, 58 },
    111 	{ MOD_SHIFT_L,   42 },
    112 	{ MOD_SHIFT_R,   54 },
    113 	{ MOD_ALT_L,     56 },
    114 	{ MOD_ALT_R,     56 },
    115 };
    116 
    117 #define XX 0
    118 /* Translate USB keycodes to US keyboard scancodes. */
    119 /* XXX very incomplete */
    120 static char ukbd_trtab[256] = {
    121 	   0,   0,   0,   0,  30,  48,  46,  32,
    122 	  18,  33,  34,  35,  23,  36,  37,  38,
    123 	  50,  49,  24,  25,  16,  19,  31,  20,
    124 	  22,  47,  17,  45,  21,  44,   2,   3,
    125 	   4,   5,   6,   7,   8,   9,  10,  11,
    126 	  28,   1,  14,  15,  57,  12,  13,  26,
    127 	  27,  43,  XX,  39,  40,  41,  51,  52,
    128 	  53,  58,  59,  60,  61,  62,  63,  64,
    129 	  65,  66,  67,  68,  87,  88,  XX,  70,
    130 	  XX,  XX,  XX,  XX,  XX,
    131 };
    132 
    133 #define KEY_ERROR 0x01
    134 
    135 struct ukbd_softc {
    136 	struct device sc_dev;		/* base device */
    137 	usbd_interface_handle sc_iface;	/* interface */
    138 	usbd_pipe_handle sc_intrpipe;	/* interrupt pipe */
    139 	int sc_ep_addr;
    140 
    141 	struct ukbd_data sc_ndata;
    142 	struct ukbd_data sc_odata;
    143 
    144 	int sc_state;
    145 #define UKBD_NEEDCLEAR	0x01		/* needs clearing endpoint stall */
    146 	int sc_disconnected;		/* device is gone */
    147 
    148 	int sc_leds;
    149 	struct device *sc_wskbddev;
    150 #ifdef WSDISPLAY_COMPAT_RAWKBD
    151 	int sc_rawkbd;
    152 #endif
    153 
    154 	int sc_polling;
    155 	int sc_pollchar;
    156 };
    157 
    158 #define	UKBDUNIT(dev)	(minor(dev))
    159 #define	UKBD_CHUNK	128	/* chunk size for read */
    160 #define	UKBD_BSIZE	1020	/* buffer size */
    161 
    162 int	ukbd_match __P((struct device *, struct cfdata *, void *));
    163 void	ukbd_attach __P((struct device *, struct device *, void *));
    164 
    165 void	ukbd_cngetc __P((void *, u_int *, int *));
    166 void	ukbd_cnpollc __P((void *, int));
    167 
    168 void	ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
    169 void	ukbd_disco __P((void *));
    170 
    171 void	ukbd_set_leds __P((void *, int));
    172 int	ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
    173 
    174 extern struct cfdriver ukbd_cd;
    175 
    176 struct cfattach ukbd_ca = {
    177 	sizeof(struct ukbd_softc), ukbd_match, ukbd_attach
    178 };
    179 
    180 int
    181 ukbd_match(parent, match, aux)
    182 	struct device *parent;
    183 	struct cfdata *match;
    184 	void *aux;
    185 {
    186 	struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
    187 	usb_interface_descriptor_t *id;
    188 
    189 	/* Check that this is a keyboard that speaks the boot protocol. */
    190 	if (!uaa->iface)
    191 		return (UMATCH_NONE);
    192 	id = usbd_get_interface_descriptor(uaa->iface);
    193 	if (id->bInterfaceClass != UCLASS_HID ||
    194 	    id->bInterfaceSubClass != USUBCLASS_BOOT ||
    195 	    id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD)
    196 		return (UMATCH_NONE);
    197 	return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
    198 }
    199 
    200 void
    201 ukbd_attach(parent, self, aux)
    202 	struct device *parent;
    203 	struct device *self;
    204 	void *aux;
    205 {
    206 	struct ukbd_softc *sc = (struct ukbd_softc *)self;
    207 	struct usb_attach_arg *uaa = aux;
    208 	usbd_interface_handle iface = uaa->iface;
    209 	usb_interface_descriptor_t *id;
    210 	usb_endpoint_descriptor_t *ed;
    211 	usbd_status r;
    212 	char devinfo[1024];
    213 	struct wskbddev_attach_args a;
    214 
    215 	sc->sc_disconnected = 1;
    216 	sc->sc_iface = iface;
    217 	id = usbd_get_interface_descriptor(iface);
    218 	usbd_devinfo(uaa->device, 0, devinfo);
    219 	printf(": %s (interface class %d/%d)\n", devinfo,
    220 	       id->bInterfaceClass, id->bInterfaceSubClass);
    221 	ed = usbd_interface2endpoint_descriptor(iface, 0);
    222 	if (!ed) {
    223 		printf("%s: could not read endpoint descriptor\n",
    224 		       sc->sc_dev.dv_xname);
    225 		return;
    226 	}
    227 
    228 	DPRINTFN(10,("ukbd_attach: \
    229 bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
    230 	       ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
    231 	       ed->bEndpointAddress & UE_IN ? "in" : "out",
    232 	       ed->bmAttributes & UE_XFERTYPE,
    233 	       UGETW(ed->wMaxPacketSize), ed->bInterval));
    234 
    235 	if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
    236 	    (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
    237 		printf("%s: unexpected endpoint\n",
    238 		       sc->sc_dev.dv_xname);
    239 		return;
    240 	}
    241 
    242 	if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
    243 		r = usbd_set_protocol(iface, 0);
    244 		DPRINTFN(5, ("ukbd_attach: protocol set\n"));
    245 		if (r != USBD_NORMAL_COMPLETION) {
    246 			printf("%s: set protocol failed\n",
    247 			       sc->sc_dev.dv_xname);
    248 			return;
    249 		}
    250 	}
    251 
    252 	sc->sc_ep_addr = ed->bEndpointAddress;
    253 	sc->sc_disconnected = 0;
    254 
    255 	a.console = 0;	/* XXX */
    256 #ifdef PCKBD_LAYOUT
    257 	a.layout = PCKBD_LAYOUT;
    258 #else
    259 	a.layout = KB_US;
    260 #endif
    261 	a.keydesc = pckbd_keydesctab;
    262 	a.num_keydescs = sizeof(pckbd_keydesctab)/sizeof(pckbd_keydesctab[0]);
    263 	a.getc = ukbd_cngetc;
    264 	a.pollc = ukbd_cnpollc;
    265 	a.set_leds = ukbd_set_leds;
    266 	a.ioctl = ukbd_ioctl;
    267 	a.accesscookie = sc;
    268 	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
    269 
    270 	/* Set up interrupt pipe. */
    271 	r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
    272 				USBD_SHORT_XFER_OK,
    273 				&sc->sc_intrpipe, sc, &sc->sc_ndata,
    274 				sizeof(sc->sc_ndata), ukbd_intr);
    275 	if (r != USBD_NORMAL_COMPLETION) {
    276 		printf("%s: usbd_open_pipe_intr failed, error=%d\n",
    277 		       sc->sc_dev.dv_xname, r);
    278 		return;
    279 	}
    280 	usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc);
    281 }
    282 
    283 void
    284 ukbd_disco(p)
    285 	void *p;
    286 {
    287 	struct ukbd_softc *sc = p;
    288 	sc->sc_disconnected = 1;
    289 }
    290 
    291 void
    292 ukbd_intr(reqh, addr, status)
    293 	usbd_request_handle reqh;
    294 	usbd_private_handle addr;
    295 	usbd_status status;
    296 {
    297 	struct ukbd_softc *sc = addr;
    298 	struct ukbd_data *ud = &sc->sc_ndata;
    299 	int mod, omod;
    300 	char ibuf[NMOD+2*NKEYCODE];	/* chars events */
    301 	int nkeys, i, j;
    302 	int key, c;
    303 #define ADDKEY(c) ibuf[nkeys++] = (c)
    304 
    305 	DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
    306 	if (status == USBD_CANCELLED)
    307 		return;
    308 
    309 	if (status != USBD_NORMAL_COMPLETION) {
    310 		DPRINTF(("ukbd_intr: status=%d\n", status));
    311 		sc->sc_state |= UKBD_NEEDCLEAR;
    312 		return;
    313 	}
    314 
    315 	DPRINTFN(5, ("          mod=0x%02x key0=0x%02x key1=0x%02x\n",
    316 		     ud->modifiers, ud->keycode[0], ud->keycode[1]));
    317 
    318 	if (ud->keycode[0] == KEY_ERROR)
    319 		return;		/* ignore  */
    320 	nkeys = 0;
    321 	mod = ud->modifiers;
    322 	omod = sc->sc_odata.modifiers;
    323 	if (mod != omod)
    324 		for (i = 0; i < NMOD; i++)
    325 			if (( mod & ukbd_mods[i].mask) !=
    326 			    (omod & ukbd_mods[i].mask))
    327 				ADDKEY(ukbd_mods[i].key |
    328 				       (mod & ukbd_mods[i].mask
    329 					  ? PRESS : RELEASE));
    330 	if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) {
    331 		/* Check for released keys. */
    332 		for (i = 0; i < NKEYCODE; i++) {
    333 			key = sc->sc_odata.keycode[i];
    334 			if (key == 0)
    335 				continue;
    336 			for (j = 0; j < NKEYCODE; j++)
    337 				if (key == ud->keycode[j])
    338 					goto rfound;
    339 			c = ukbd_trtab[key];
    340 			if (c)
    341 				ADDKEY(c | RELEASE);
    342 		rfound:
    343 			;
    344 		}
    345 
    346 		/* Check for pressed keys. */
    347 		for (i = 0; i < NKEYCODE; i++) {
    348 			key = ud->keycode[i];
    349 			if (key == 0)
    350 				continue;
    351 			for (j = 0; j < NKEYCODE; j++)
    352 				if (key == sc->sc_odata.keycode[j])
    353 					goto pfound;
    354 			c = ukbd_trtab[key];
    355 			if (c)
    356 				ADDKEY(c | PRESS);
    357 		pfound:
    358 			;
    359 		}
    360 	}
    361 	sc->sc_odata = *ud;
    362 
    363 	if (sc->sc_polling) {
    364 		if (nkeys > 0)
    365 			sc->sc_pollchar = ibuf[0];
    366 		return;
    367 	}
    368 	for (i = 0; i < nkeys; i++) {
    369 		c = ibuf[i];
    370 		wskbd_input(sc->sc_wskbddev,
    371 			    c&0x80 ? WSCONS_EVENT_KEY_UP:WSCONS_EVENT_KEY_DOWN,
    372 			    c & 0x7f);
    373 	}
    374 }
    375 
    376 void
    377 ukbd_set_leds(v, leds)
    378 	void *v;
    379 	int leds;
    380 {
    381 	struct ukbd_softc *sc = v;
    382 	u_int8_t res;
    383 
    384 	DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds));
    385 
    386 	sc->sc_leds = leds;
    387 	res = 0;
    388 	if (leds & WSKBD_LED_SCROLL)
    389 		res |= SCROLL_LOCK;
    390 	if (leds & WSKBD_LED_NUM)
    391 		res |= NUM_LOCK;
    392 	if (leds & WSKBD_LED_CAPS)
    393 		res |= CAPS_LOCK;
    394 	usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1);
    395 }
    396 
    397 int
    398 ukbd_ioctl(v, cmd, data, flag, p)
    399 	void *v;
    400 	u_long cmd;
    401 	caddr_t data;
    402 	int flag;
    403 	struct proc *p;
    404 {
    405 	struct ukbd_softc *sc = v;
    406 
    407 	switch (cmd) {
    408 	case WSKBDIO_GTYPE:
    409 		*(int *)data = WSKBD_TYPE_PC_XT;
    410 		return 0;
    411 	case WSKBDIO_SETLEDS:
    412 		ukbd_set_leds(v, *(int *)data);
    413 		return 0;
    414 	case WSKBDIO_GETLEDS:
    415 		*(int *)data = sc->sc_leds;
    416 		return (0);
    417 #ifdef WSDISPLAY_COMPAT_RAWKBD
    418 	case WSKBDIO_SETMODE:
    419 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
    420 		return (0);
    421 #endif
    422 	}
    423 	return -1;
    424 }
    425 
    426 void
    427 ukbd_cngetc(v, type, data)
    428 	void *v;
    429 	u_int *type;
    430 	int *data;
    431 {
    432 	struct ukbd_softc *sc = v;
    433 	usbd_lock_token s;
    434 	extern int usbd_use_polling;
    435 
    436 	s = usbd_lock();
    437 	usbd_use_polling = 1;
    438 	sc->sc_polling = 1;
    439 	sc->sc_pollchar = -1;
    440 	while(sc->sc_pollchar == -1)
    441 		usbd_dopoll(sc->sc_iface);
    442 	sc->sc_polling = 0;
    443 	usbd_use_polling = 0;
    444 	*type = sc->sc_pollchar&0x80?WSCONS_EVENT_KEY_UP:WSCONS_EVENT_KEY_DOWN;
    445 	*data = sc->sc_pollchar & 0x7f;
    446 	usbd_unlock(s);
    447 }
    448 
    449 void
    450 ukbd_cnpollc(v, on)
    451 	void *v;
    452         int on;
    453 {
    454 	DPRINTF(("ukbd_cnpollc: sc=%p on=%d\n", v, on));
    455 }
    456