Home | History | Annotate | Line # | Download | only in usb
ukbd.c revision 1.2
      1 /*	$NetBSD: ukbd.c,v 1.2 1998/07/25 15:36:30 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 
    155 #define	UKBDUNIT(dev)	(minor(dev))
    156 #define	UKBD_CHUNK	128	/* chunk size for read */
    157 #define	UKBD_BSIZE	1020	/* buffer size */
    158 
    159 int	ukbd_match __P((struct device *, struct cfdata *, void *));
    160 void	ukbd_attach __P((struct device *, struct device *, void *));
    161 
    162 void	ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
    163 void	ukbd_disco __P((void *));
    164 
    165 void	ukbd_set_leds __P((void *, int));
    166 int	ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *));
    167 
    168 extern struct cfdriver ukbd_cd;
    169 
    170 struct cfattach ukbd_ca = {
    171 	sizeof(struct ukbd_softc), ukbd_match, ukbd_attach
    172 };
    173 
    174 int
    175 ukbd_match(parent, match, aux)
    176 	struct device *parent;
    177 	struct cfdata *match;
    178 	void *aux;
    179 {
    180 	struct usb_attach_arg *uaa = (struct usb_attach_arg *)aux;
    181 	usb_interface_descriptor_t *id;
    182 
    183 	/* Check that this is a keyboard that speaks the boot protocol. */
    184 	if (!uaa->iface)
    185 		return (UMATCH_NONE);
    186 	id = usbd_get_interface_descriptor(uaa->iface);
    187 	if (id->bInterfaceClass != UCLASS_HID ||
    188 	    id->bInterfaceSubClass != USUBCLASS_BOOT ||
    189 	    id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD)
    190 		return (UMATCH_NONE);
    191 	return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
    192 }
    193 
    194 void
    195 ukbd_attach(parent, self, aux)
    196 	struct device *parent;
    197 	struct device *self;
    198 	void *aux;
    199 {
    200 	struct ukbd_softc *sc = (struct ukbd_softc *)self;
    201 	struct usb_attach_arg *uaa = aux;
    202 	usbd_interface_handle iface = uaa->iface;
    203 	usb_interface_descriptor_t *id;
    204 	usb_endpoint_descriptor_t *ed;
    205 	usbd_status r;
    206 	char devinfo[1024];
    207 	struct wskbddev_attach_args a;
    208 
    209 	sc->sc_disconnected = 1;
    210 	sc->sc_iface = iface;
    211 	id = usbd_get_interface_descriptor(iface);
    212 	usbd_devinfo(uaa->device, 0, devinfo);
    213 	printf(": %s (interface class %d/%d)\n", devinfo,
    214 	       id->bInterfaceClass, id->bInterfaceSubClass);
    215 	ed = usbd_interface2endpoint_descriptor(iface, 0);
    216 	if (!ed) {
    217 		printf("%s: could not read endpoint descriptor\n",
    218 		       sc->sc_dev.dv_xname);
    219 		return;
    220 	}
    221 
    222 	DPRINTFN(10,("ukbd_attach: \
    223 bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
    224 	       ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
    225 	       ed->bEndpointAddress & UE_IN ? "in" : "out",
    226 	       ed->bmAttributes & UE_XFERTYPE,
    227 	       UGETW(ed->wMaxPacketSize), ed->bInterval));
    228 
    229 	if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
    230 	    (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
    231 		printf("%s: unexpected endpoint\n",
    232 		       sc->sc_dev.dv_xname);
    233 		return;
    234 	}
    235 
    236 	if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) {
    237 		r = usbd_set_protocol(iface, 0);
    238 		DPRINTFN(5, ("ukbd_attach: protocol set\n"));
    239 		if (r != USBD_NORMAL_COMPLETION) {
    240 			printf("%s: set protocol failed\n",
    241 			       sc->sc_dev.dv_xname);
    242 			return;
    243 		}
    244 	}
    245 
    246 	sc->sc_ep_addr = ed->bEndpointAddress;
    247 	sc->sc_disconnected = 0;
    248 
    249 	a.console = 0;	/* XXX */
    250 #ifdef PCKBD_LAYOUT
    251 	a.layout = PCKBD_LAYOUT;
    252 #else
    253 	a.layout = KB_US;
    254 #endif
    255 	a.keydesc = pckbd_keydesctab;
    256 	a.num_keydescs = sizeof(pckbd_keydesctab)/sizeof(pckbd_keydesctab[0]);
    257 	a.getc = NULL;
    258 	a.pollc = NULL;
    259 	a.set_leds = ukbd_set_leds;
    260 	a.ioctl = ukbd_ioctl;
    261 	a.accesscookie = sc;
    262 	sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
    263 
    264 	/* Set up interrupt pipe. */
    265 	r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
    266 				USBD_SHORT_XFER_OK,
    267 				&sc->sc_intrpipe, sc, &sc->sc_ndata,
    268 				sizeof(sc->sc_ndata), ukbd_intr);
    269 	if (r != USBD_NORMAL_COMPLETION) {
    270 		printf("%s: usbd_open_pipe_intr failed, error=%d\n",
    271 		       sc->sc_dev.dv_xname, r);
    272 		return;
    273 	}
    274 	usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc);
    275 }
    276 
    277 void
    278 ukbd_disco(p)
    279 	void *p;
    280 {
    281 	struct ukbd_softc *sc = p;
    282 	sc->sc_disconnected = 1;
    283 }
    284 
    285 void
    286 ukbd_intr(reqh, addr, status)
    287 	usbd_request_handle reqh;
    288 	usbd_private_handle addr;
    289 	usbd_status status;
    290 {
    291 	struct ukbd_softc *sc = addr;
    292 	struct ukbd_data *ud = &sc->sc_ndata;
    293 	int mod, omod;
    294 	char ibuf[NMOD+2*NKEYCODE];	/* chars events */
    295 	int nkeys, i, j;
    296 	int key, c;
    297 #define ADDKEY(c) ibuf[nkeys++] = (c)
    298 
    299 	DPRINTFN(5, ("ukbd_intr: status=%d\n", status));
    300 	if (status == USBD_CANCELLED)
    301 		return;
    302 
    303 	if (status != USBD_NORMAL_COMPLETION) {
    304 		DPRINTF(("ukbd_intr: status=%d\n", status));
    305 		sc->sc_state |= UKBD_NEEDCLEAR;
    306 		return;
    307 	}
    308 
    309 	DPRINTFN(5, ("          mod=0x%02x key0=0x%02x key1=0x%02x\n",
    310 		     ud->modifiers, ud->keycode[0], ud->keycode[1]));
    311 
    312 	if (ud->keycode[0] == KEY_ERROR)
    313 		return;		/* ignore  */
    314 	nkeys = 0;
    315 	mod = ud->modifiers;
    316 	omod = sc->sc_odata.modifiers;
    317 	if (mod != omod)
    318 		for (i = 0; i < NMOD; i++)
    319 			if (( mod & ukbd_mods[i].mask) !=
    320 			    (omod & ukbd_mods[i].mask))
    321 				ADDKEY(ukbd_mods[i].key |
    322 				       (mod & ukbd_mods[i].mask
    323 					  ? PRESS : RELEASE));
    324 	if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) {
    325 		/* Check for released keys. */
    326 		for (i = 0; i < NKEYCODE; i++) {
    327 			key = sc->sc_odata.keycode[i];
    328 			if (key == 0)
    329 				continue;
    330 			for (j = 0; j < NKEYCODE; j++)
    331 				if (key == ud->keycode[j])
    332 					goto rfound;
    333 			c = ukbd_trtab[key];
    334 			if (c)
    335 				ADDKEY(c | RELEASE);
    336 		rfound:
    337 			;
    338 		}
    339 
    340 		/* Check for pressed keys. */
    341 		for (i = 0; i < NKEYCODE; i++) {
    342 			key = ud->keycode[i];
    343 			if (key == 0)
    344 				continue;
    345 			for (j = 0; j < NKEYCODE; j++)
    346 				if (key == sc->sc_odata.keycode[j])
    347 					goto pfound;
    348 			c = ukbd_trtab[key];
    349 			if (c)
    350 				ADDKEY(c | PRESS);
    351 		pfound:
    352 			;
    353 		}
    354 	}
    355 	sc->sc_odata = *ud;
    356 
    357 	for (i = 0; i < nkeys; i++) {
    358 		c = ibuf[i];
    359 		wskbd_input(sc->sc_wskbddev,
    360 			    c&0x80 ? WSCONS_EVENT_KEY_UP:WSCONS_EVENT_KEY_DOWN,
    361 			    c & 0x7f);
    362 	}
    363 }
    364 
    365 void
    366 ukbd_set_leds(v, leds)
    367 	void *v;
    368 	int leds;
    369 {
    370 	struct ukbd_softc *sc = v;
    371 	u_int8_t res;
    372 
    373 	DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds));
    374 
    375 	sc->sc_leds = leds;
    376 	res = 0;
    377 	if (leds & WSKBD_LED_SCROLL)
    378 		res |= SCROLL_LOCK;
    379 	if (leds & WSKBD_LED_NUM)
    380 		res |= NUM_LOCK;
    381 	if (leds & WSKBD_LED_CAPS)
    382 		res |= CAPS_LOCK;
    383 	usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1);
    384 }
    385 
    386 int
    387 ukbd_ioctl(v, cmd, data, flag, p)
    388 	void *v;
    389 	u_long cmd;
    390 	caddr_t data;
    391 	int flag;
    392 	struct proc *p;
    393 {
    394 	struct ukbd_softc *sc = v;
    395 
    396 	switch (cmd) {
    397 	case WSKBDIO_GTYPE:
    398 		*(int *)data = WSKBD_TYPE_PC_XT;
    399 		return 0;
    400 	case WSKBDIO_SETLEDS:
    401 		ukbd_set_leds(v, *(int *)data);
    402 		return 0;
    403 	case WSKBDIO_GETLEDS:
    404 		*(int *)data = sc->sc_leds;
    405 		return (0);
    406 #ifdef WSDISPLAY_COMPAT_RAWKBD
    407 	case WSKBDIO_SETMODE:
    408 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
    409 		return (0);
    410 #endif
    411 	}
    412 	return -1;
    413 }
    414 
    415