From 86df1e04ccf4207c03feb479f160c55217175334 Mon Sep 17 00:00:00 2001 From: Nick Hibma Date: Mon, 22 Mar 1999 19:55:30 +0000 Subject: [PATCH] Implementation of the keyboard driver. Done by: Kazutaka YOKOTA --- sys/dev/usb/ukbd.c | 1735 ++++++++++++++++++++++++++++++++------------ 1 file changed, 1278 insertions(+), 457 deletions(-) diff --git a/sys/dev/usb/ukbd.c b/sys/dev/usb/ukbd.c index 194afb410bb..98b958648a6 100644 --- a/sys/dev/usb/ukbd.c +++ b/sys/dev/usb/ukbd.c @@ -42,18 +42,16 @@ * Information about USB keyboard can be found in the USB HID spec. */ +#include "ukbd.h" +#include "opt_kbd.h" + #include #include #include -#if defined(__NetBSD__) -#include -#include -#elif defined(__FreeBSD__) #include #include #include #include -#endif #include #include #include @@ -69,20 +67,14 @@ #include #include -#if defined(__NetBSD__) -#include -#include -#include -#include -#include +#include +#include -#include "opt_pckbd_layout.h" -#include "opt_wsdisplay_compat.h" +#define UKBD_EMULATE_ATSCANCODE 1 + +#define DRIVER_NAME "ukbd" -#elif defined(__FreeBSD__) -#include #define delay(d) DELAY(d) -#endif #ifdef USB_DEBUG #define DPRINTF(x) if (ukbddebug) printf x @@ -97,10 +89,6 @@ int ukbddebug = 1; #define NKEYCODE 6 -#define NUM_LOCK 0x01 -#define CAPS_LOCK 0x02 -#define SCROLL_LOCK 0x04 - struct ukbd_data { u_int8_t modifiers; #define MOD_CONTROL_L 0x01 @@ -115,25 +103,236 @@ struct ukbd_data { u_int8_t keycode[NKEYCODE]; }; -#define PRESS 0 -#define RELEASE 0x100 +#define MAXKEYS (NMOD+2*NKEYCODE) -#define NMOD 6 +typedef struct ukbd_softc { + bdevice sc_dev; /* base device */ + usbd_interface_handle sc_iface; /* interface */ + + short sc_flags; +#define UKBD_ATTACHED (1 << 0) + keyboard_t *sc_kbd; +#ifdef KBD_INSTALL_CDEV + genkbd_softc_t sc_gensc; +#endif +} ukbd_softc_t; + +#define UKBDUNIT(dev) (minor(dev)) +#define UKBD_CHUNK 128 /* chunk size for read */ +#define UKBD_BSIZE 1020 /* buffer size */ + +typedef void usbd_intr_t(usbd_request_handle, usbd_private_handle, usbd_status); +usbd_intr_t ukbd_intr; + +#ifdef KBD_INSTALL_CDEV + +static d_open_t ukbdopen; +static d_close_t ukbdclose; +static d_read_t ukbdread; +static d_ioctl_t ukbdioctl; +static d_poll_t ukbdpoll; + +static struct cdevsw ukbd_cdevsw = { + ukbdopen, ukbdclose, ukbdread, nowrite, + ukbdioctl, nostop, nullreset, nodevtotty, + ukbdpoll, nommap, NULL, DRIVER_NAME, + NULL, -1, +}; + +#endif /* KBD_INSTALL_CDEV */ + +USB_DECLARE_DRIVER(ukbd); + +USB_MATCH(ukbd) +{ + USB_MATCH_START(ukbd, uaa); + + keyboard_switch_t *sw; + void *arg[2]; + int unit = device_get_unit(device); + + sw = kbd_get_switch(DRIVER_NAME); + if (sw == NULL) + return (UMATCH_NONE); + + arg[0] = (void *)uaa; + arg[1] = (void *)ukbd_intr; + if ((*sw->probe)(unit, (void *)arg, 0)) + return (UMATCH_NONE); + + return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); +} + +USB_ATTACH(ukbd) +{ + USB_ATTACH_START(ukbd, sc, uaa); + usbd_interface_handle iface = uaa->iface; + usb_interface_descriptor_t *id; + char devinfo[1024]; + + keyboard_switch_t *sw; + void *arg[2]; + int unit = device_get_unit(self); + + sw = kbd_get_switch(DRIVER_NAME); + if (sw == NULL) + USB_ATTACH_ERROR_RETURN; + + sc->sc_iface = iface; + id = usbd_get_interface_descriptor(iface); + usbd_devinfo(uaa->device, 0, devinfo); + USB_ATTACH_SETUP; + printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), + devinfo, id->bInterfaceClass, id->bInterfaceSubClass); + + arg[0] = (void *)uaa; + arg[1] = (void *)ukbd_intr; + sc->sc_kbd = NULL; + if ((*sw->probe)(unit, (void *)arg, 0)) + USB_ATTACH_ERROR_RETURN; + if ((*sw->init)(unit, &sc->sc_kbd, (void *)arg, 0)) + USB_ATTACH_ERROR_RETURN; + (*sw->enable)(sc->sc_kbd); + +#ifdef KBD_INSTALL_CDEV + if (kbd_attach(makedev(0,unit), sc->sc_kbd, &ukbd_cdevsw)) + USB_ATTACH_ERROR_RETURN; +#endif + if (bootverbose) + (*sw->diag)(sc->sc_kbd, bootverbose); + sc->sc_flags |= UKBD_ATTACHED; + + USB_ATTACH_SUCCESS_RETURN; +} + +int +ukbd_detach(device_t self) +{ + struct ukbd_softc *sc = device_get_softc(self); + const char *devinfo = device_get_desc(self); + int error; + + DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); + error = (*kbdsw[sc->sc_kbd->kb_index]->term)(sc->sc_kbd); + if (error) + return error; + + if (devinfo) { + device_set_desc(self, NULL); + free((void *)devinfo, M_USB); + } + + return (0); +} + +/* cdev driver functions */ + +#ifdef KBD_INSTALL_CDEV + +static int +ukbdopen(dev_t dev, int flag, int mode, struct proc *p) +{ + USB_GET_SC_OPEN(ukbd, UKBDUNIT(dev), sc); + + /* FIXME: set the initial input mode (K_XLATE?) and lock state? */ + return genkbdopen(&sc->sc_gensc, sc->sc_kbd, flag, mode, p); +} + +static int +ukbdclose(dev_t dev, int flag, int mode, struct proc *p) +{ + USB_GET_SC(ukbd, UKBDUNIT(dev),sc); + + return genkbdclose(&sc->sc_gensc, sc->sc_kbd, flag, mode, p); +} + +static int +ukbdread(dev_t dev, struct uio *uio, int flag) +{ + USB_GET_SC(ukbd, UKBDUNIT(dev),sc); + + return genkbdread(&sc->sc_gensc, sc->sc_kbd, uio, flag); +} + +static int +ukbdioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p) +{ + USB_GET_SC(ukbd, UKBDUNIT(dev),sc); + + return genkbdioctl(&sc->sc_gensc, sc->sc_kbd, cmd, arg, flag, p); +} + +static int +ukbdpoll(dev_t dev, int event, struct proc *p) +{ + USB_GET_SC(ukbd, UKBDUNIT(dev),sc); + + return genkbdpoll(&sc->sc_gensc, sc->sc_kbd, event, p); +} + +#endif /* KBD_INSTALL_CDEV */ + +void +ukbd_intr(usbd_request_handle reqh, usbd_private_handle addr, usbd_status status) +{ + keyboard_t *kbd = (keyboard_t *)addr; + + (*kbdsw[kbd->kb_index]->intr)(kbd, (void *)status); +} + + +DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, usbd_driver_load, 0); + + +#include +#include +#include + +#define UKBD_DEFAULT 0 + +#define KEY_ERROR 0x01 + +#define KEY_PRESS 0 +#define KEY_RELEASE 0x400 +#define KEY_INDEX(c) ((c) & ~KEY_RELEASE) + +#define SCAN_PRESS 0 +#define SCAN_RELEASE 0x80 +#define SCAN_PREFIX_E0 0x100 +#define SCAN_PREFIX_E1 0x200 +#define SCAN_PREFIX_CTL 0x400 +#define SCAN_PREFIX_SHIFT 0x800 +#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL \ + | SCAN_PREFIX_SHIFT) +#define SCAN_CHAR(c) ((c) & 0x7f) + +#define NMOD 8 static struct { int mask, key; } ukbd_mods[NMOD] = { + { MOD_CONTROL_L, 0xe0 }, + { MOD_CONTROL_R, 0xe4 }, + { MOD_SHIFT_L, 0xe1 }, + { MOD_SHIFT_R, 0xe5 }, + { MOD_ALT_L, 0xe2 }, + { MOD_ALT_R, 0xe6 }, + { MOD_WIN_L, 0xe3 }, + { MOD_WIN_R, 0xe7 }, +#if 0 { MOD_CONTROL_L, 29 }, - { MOD_CONTROL_R, 58 }, + { MOD_CONTROL_R, 90 }, { MOD_SHIFT_L, 42 }, { MOD_SHIFT_R, 54 }, { MOD_ALT_L, 56 }, - { MOD_ALT_R, 184 }, + { MOD_ALT_R, 93 }, + { MOD_WIN_L, 105 }, + { MOD_WIN_R, 106 }, +#endif }; #define NN 0 /* no translation */ /* * Translate USB keycodes to US keyboard AT scancodes. - * Scancodes >= 128 represent EXTENDED keycodes. */ static u_int8_t ukbd_trtab[256] = { 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */ @@ -142,18 +341,18 @@ static u_int8_t ukbd_trtab[256] = { 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */ - 27, 43, NN, 39, 40, 41, 51, 52, /* 30 - 37 */ + 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */ - 65, 66, 67, 68, 87, 88, 170, 70, /* 40 - 47 */ - 127, 210, 199, 201, 211, 207, 209, 205, /* 48 - 4F */ - 203, 208, 200, 69, 181, 55, 74, 78, /* 50 - 57 */ - 156, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */ - 72, 73, 82, 83, NN, NN, NN, NN, /* 60 - 67 */ + 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */ + 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */ + 97, 100, 95, 69, 91, 55, 74, 78, /* 50 - 57 */ + 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */ + 72, 73, 82, 83, 86, 107, NN, NN, /* 60 - 67 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */ - NN, NN, NN, NN, NN, NN, 221, NN, /* 70 - 77 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* 70 - 77 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 78 - 7F */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 80 - 87 */ - NN, NN, NN, NN, NN, NN, NN, NN, /* 88 - 8F */ + NN, NN, NN, NN, NN, NN, NN, 115, /* 80 - 87 */ + 112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */ @@ -164,518 +363,1140 @@ static u_int8_t ukbd_trtab[256] = { NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */ - NN, NN, NN, NN, NN, NN, NN, NN, /* E0 - E7 */ - NN, NN, NN, 219, NN, NN, NN, 220, /* E8 - EF */ + 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */ + NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */ }; -#define KEY_ERROR 0x01 +typedef struct ukbd_state { + usbd_interface_handle ks_iface; /* interface */ + usbd_pipe_handle ks_intrpipe; /* interrupt pipe */ + usbd_intr_t *ks_intrfunc; + struct usb_attach_arg *ks_uaa; + int ks_ep_addr; -#define MAXKEYS (NMOD+2*NKEYCODE) + struct ukbd_data ks_ndata; + struct ukbd_data ks_odata; + u_long ks_ntime[NKEYCODE]; + u_long ks_otime[NKEYCODE]; -struct ukbd_softc { - bdevice sc_dev; /* base device */ - usbd_interface_handle sc_iface; /* interface */ - usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ - int sc_ep_addr; +#define INPUTBUFSIZE (NMOD + 2*NKEYCODE) + u_int ks_input[INPUTBUFSIZE]; /* input buffer */ + int ks_inputs; + int ks_inputhead; + int ks_inputtail; - struct ukbd_data sc_ndata; - struct ukbd_data sc_odata; + int ks_ifstate; +#define INTRENABLED (1 << 0) +#define DISCONNECTED (1 << 1) - char sc_enabled; - char sc_disconnected; /* device is gone */ + struct callout_handle ks_timeout_handle; - int sc_leds; -#if defined(__NetBSD__) - struct device *sc_wskbddev; -#ifdef WSDISPLAY_COMPAT_RAWKBD -#define REP_DELAY1 400 -#define REP_DELAYN 100 - int sc_rawkbd; - int sc_nrep; - char sc_rep[MAXKEYS]; + int ks_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */ + int ks_flags; /* flags */ +#define COMPOSE (1 << 0) + int ks_polling; + int ks_state; /* shift/lock key state */ + int ks_accents; /* accent key index (> 0) */ + u_int ks_composed_char; /* composed char code (> 0) */ +#ifdef UKBD_EMULATE_ATSCANCODE + u_int ks_buffered_char[2]; #endif +} ukbd_state_t; - int sc_polling; - int sc_pollchar; -#endif -}; +/* keyboard driver declaration */ +static int ukbd_configure(int flags); +static kbd_probe_t ukbd_probe; +static kbd_init_t ukbd_init; +static kbd_term_t ukbd_term; +static kbd_intr_t ukbd_interrupt; +static kbd_test_if_t ukbd_test_if; +static kbd_enable_t ukbd_enable_kbd; +static kbd_disable_t ukbd_disable; +static kbd_read_t ukbd_read; +static kbd_check_t ukbd_check; +static kbd_read_char_t ukbd_read_char; +static kbd_check_char_t ukbd_check_char; +static kbd_ioctl_t ukbd_ioctl; +static kbd_lock_t ukbd_lock; +static kbd_clear_state_t ukbd_clear_state; +static kbd_get_state_t ukbd_get_state; +static kbd_set_state_t ukbd_set_state; +static kbd_poll_mode_t ukbd_poll; -#define UKBDUNIT(dev) (minor(dev)) -#define UKBD_CHUNK 128 /* chunk size for read */ -#define UKBD_BSIZE 1020 /* buffer size */ - -void ukbd_cngetc __P((void *, u_int *, int *)); -void ukbd_cnpollc __P((void *, int)); - -#if defined(__NetBSD__) -const struct wskbd_consops ukbd_consops = { - ukbd_cngetc, - ukbd_cnpollc, -}; -#endif - -void ukbd_intr __P((usbd_request_handle, usbd_private_handle, usbd_status)); -void ukbd_disco __P((void *)); - -int ukbd_enable __P((void *, int)); -void ukbd_set_leds __P((void *, int)); -#if defined(__NetBSD__) -int ukbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); -int ukbd_cnattach __P((void *v)); -void ukbd_rawrepeat __P((void *v)); - -const struct wskbd_accessops ukbd_accessops = { - ukbd_enable, - ukbd_set_leds, +keyboard_switch_t ukbdsw = { + ukbd_probe, + ukbd_init, + ukbd_term, + ukbd_interrupt, + ukbd_test_if, + ukbd_enable_kbd, + ukbd_disable, + ukbd_read, + ukbd_check, + ukbd_read_char, + ukbd_check_char, ukbd_ioctl, -#if 0 - ukbd_cnattach, -#endif + ukbd_lock, + ukbd_clear_state, + ukbd_get_state, + ukbd_set_state, + genkbd_get_fkeystr, + ukbd_poll, + genkbd_diag, }; -const struct wskbd_mapdata ukbd_keymapdata = { - pckbd_keydesctab, -#ifdef PCKBD_LAYOUT - PCKBD_LAYOUT, -#else - KB_US, -#endif -}; +KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure); + +/* local functions */ +static void ukbd_disconnect(void *p); +static int ukbd_enable_intr(keyboard_t *kbd, int on, + usbd_intr_t *func); +static timeout_t ukbd_timeout; + +static int ukbd_getc(ukbd_state_t *state); +static int probe_keyboard(struct usb_attach_arg *uaa, int flags); +static int init_keyboard(ukbd_state_t *state, int *type, + int flags); +static void set_leds(ukbd_state_t *state, int leds); +static int set_typematic(keyboard_t *kbd, int code); +#ifdef UKBD_EMULATE_ATSCANCODE +static int keycode2scancode(int keycode, int shift, int up); #endif -USB_DECLARE_DRIVER(ukbd); +/* local variables */ -USB_MATCH(ukbd) +/* the initial key map, accent map and fkey strings */ +#ifdef UKBD_DFLT_KEYMAP +#define KBD_DFLT_KEYMAP +#include "ukbdmap.h" +#endif +#include + +/* structures for the default keyboard */ +static keyboard_t default_kbd; +static ukbd_state_t default_kbd_state; +static keymap_t default_keymap; +static accentmap_t default_accentmap; +static fkeytab_t default_fkeytab[NUM_FKEYS]; + +/* + * The back door to the keyboard driver! + * This function is called by the console driver, via the kbdio module, + * to tickle keyboard drivers when the low-level console is being initialized. + * Almost nothing in the kernel has been initialied yet. Try to probe + * keyboards if possible. + * NOTE: because of the way the low-level conole is initialized, this routine + * may be called more than once!! + */ +static int +ukbd_configure(int flags) { - USB_MATCH_START(ukbd, uaa); - usb_interface_descriptor_t *id; - - /* Check that this is a keyboard that speaks the boot protocol. */ - if (!uaa->iface) - return (UMATCH_NONE); - id = usbd_get_interface_descriptor(uaa->iface); - if (!id || - id->bInterfaceClass != UCLASS_HID || - id->bInterfaceSubClass != USUBCLASS_BOOT || - id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD) - return (UMATCH_NONE); - return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); + return 0; + +#if 0 /* not yet */ + keyboard_t *kbd; + device_t device; + struct usb_attach_arg *uaa; + void **arg[2]; + + device = devclass_get_device(ukbd_devclass, UKBD_DEFAULT); + if (device == NULL) + return 0; + uaa = (struct usb_attach_arg *)device_get_ivars(device); + if (uaa == NULL) + return 0; + + /* probe the default keyboard */ + arg[0] = (void *)uaa; + arg[1] = (void *)ukbd_intr; + kbd = NULL; + if (ukbd_probe(UKBD_DEFAULT, arg, flags)) + return 0; + if (ukbd_init(UKBD_DEFAULT, &kbd, arg, flags)) + return 0; + + /* return the number of found keyboards */ + return 1; +#endif } -USB_ATTACH(ukbd) +/* low-level functions */ + +/* detect a keyboard */ +static int +ukbd_probe(int unit, void *arg, int flags) { - USB_ATTACH_START(ukbd, sc, uaa); - usbd_interface_handle iface = uaa->iface; - usb_interface_descriptor_t *id; - usb_endpoint_descriptor_t *ed; - usbd_status r; - char devinfo[1024]; -#if defined(__NetBSD__) - struct wskbddev_attach_args a; -#endif - - sc->sc_disconnected = 1; - sc->sc_iface = iface; - id = usbd_get_interface_descriptor(iface); - usbd_devinfo(uaa->device, 0, devinfo); - USB_ATTACH_SETUP; - printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), - devinfo, id->bInterfaceClass, id->bInterfaceSubClass); + void **data; + struct usb_attach_arg *uaa; - ed = usbd_interface2endpoint_descriptor(iface, 0); - if (!ed) { - printf("%s: could not read endpoint descriptor\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; + data = (void **)arg; + uaa = (struct usb_attach_arg *)data[0]; + + /* XXX */ + if (unit == UKBD_DEFAULT) { + if (KBD_IS_PROBED(&default_kbd)) + return 0; } + if (probe_keyboard(uaa, flags)) + return ENXIO; + return 0; +} - DPRINTFN(10,("ukbd_attach: bLength=%d bDescriptorType=%d " - "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" - " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, - ed->bEndpointAddress & UE_ADDR, - ed->bEndpointAddress & UE_IN ? "in" : "out", - ed->bmAttributes & UE_XFERTYPE, - UGETW(ed->wMaxPacketSize), ed->bInterval)); +/* reset and initialize the device */ +static int +ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags) +{ + keyboard_t *kbd; + ukbd_state_t *state; + keymap_t *keymap; + accentmap_t *accmap; + fkeytab_t *fkeymap; + int fkeymap_size; + void **data = (void **)arg; + struct usb_attach_arg *uaa = (struct usb_attach_arg *)data[0]; + usbd_intr_t *func = (usbd_intr_t *)data[1]; - if ((ed->bEndpointAddress & UE_IN) != UE_IN || - (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { - printf("%s: unexpected endpoint\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; - } - - if ((usbd_get_quirks(uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) { - r = usbd_set_protocol(iface, 0); - DPRINTFN(5, ("ukbd_attach: protocol set\n")); - if (r != USBD_NORMAL_COMPLETION) { - printf("%s: set protocol failed\n", - USBDEVNAME(sc->sc_dev)); - USB_ATTACH_ERROR_RETURN; + /* XXX */ + if (unit == UKBD_DEFAULT) { + *kbdp = kbd = &default_kbd; + if (KBD_IS_INITIALIZED(kbd) && KBD_IS_CONFIGURED(kbd)) + return 0; + state = &default_kbd_state; + keymap = &default_keymap; + accmap = &default_accentmap; + fkeymap = default_fkeytab; + fkeymap_size = + sizeof(default_fkeytab)/sizeof(default_fkeytab[0]); + } else if (*kbdp == NULL) { + *kbdp = kbd = malloc(sizeof(*kbd), M_DEVBUF, M_NOWAIT); + if (kbd == NULL) + return ENOMEM; + bzero(kbd, sizeof(*kbd)); + state = malloc(sizeof(*state), M_DEVBUF, M_NOWAIT); + keymap = malloc(sizeof(key_map), M_DEVBUF, M_NOWAIT); + accmap = malloc(sizeof(accent_map), M_DEVBUF, M_NOWAIT); + fkeymap = malloc(sizeof(fkey_tab), M_DEVBUF, M_NOWAIT); + fkeymap_size = sizeof(fkey_tab)/sizeof(fkey_tab[0]); + if ((state == NULL) || (keymap == NULL) || (accmap == NULL) + || (fkeymap == NULL)) { + if (state != NULL) + free(state, M_DEVBUF); + if (keymap != NULL) + free(keymap, M_DEVBUF); + if (accmap != NULL) + free(accmap, M_DEVBUF); + if (fkeymap != NULL) + free(fkeymap, M_DEVBUF); + free(kbd, M_DEVBUF); + return ENOMEM; } + bzero(state, sizeof(*state)); + } else if (KBD_IS_INITIALIZED(*kbdp) && KBD_IS_CONFIGURED(*kbdp)) { + return 0; + } else { + kbd = *kbdp; + state = (ukbd_state_t *)kbd->kb_data; + bzero(state, sizeof(*state)); + keymap = kbd->kb_keymap; + accmap = kbd->kb_accentmap; + fkeymap = kbd->kb_fkeytab; + fkeymap_size = kbd->kb_fkeytab_size; } - /* Ignore if SETIDLE fails since it is not crucial. */ - usbd_set_idle(iface, 0, 0); + if (!KBD_IS_PROBED(kbd)) { + kbd_init_struct(kbd, DRIVER_NAME, KB_OTHER, unit, flags, 0, 0); + bcopy(&key_map, keymap, sizeof(key_map)); + bcopy(&accent_map, accmap, sizeof(accent_map)); + bcopy(fkey_tab, fkeymap, + imin(fkeymap_size*sizeof(fkeymap[0]), sizeof(fkey_tab))); + kbd_set_maps(kbd, keymap, accmap, fkeymap, fkeymap_size); + kbd->kb_data = (void *)state; - sc->sc_ep_addr = ed->bEndpointAddress; - sc->sc_disconnected = 0; - -#if defined(__NetBSD__) - a.console = 0; - - a.keymap = &ukbd_keymapdata; - - a.accessops = &ukbd_accessops; - a.accesscookie = sc; - - /* Flash the leds; no real purpose, just shows we're alive. */ - ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | WSKBD_LED_CAPS); - usbd_delay_ms(uaa->device, 300); - ukbd_set_leds(sc, 0); - - sc->sc_wskbddev = config_found(self, &a, wskbddevprint); - -#elif defined(__FreeBSD__) - /* XXX why waste CPU in delay() ? */ - /* It's alive! IT'S ALIVE! Do a little song and dance. */ - ukbd_set_leds(sc, NUM_LOCK); - delay(15000); - ukbd_set_leds(sc, CAPS_LOCK); - delay(20000); - ukbd_set_leds(sc, SCROLL_LOCK); - delay(30000); - ukbd_set_leds(sc, CAPS_LOCK); - delay(50000); - ukbd_set_leds(sc, NUM_LOCK); - - ukbd_enable(sc, 1); -#endif - - USB_ATTACH_SUCCESS_RETURN; -} - -#if defined(__FreeBSD__) -int -ukbd_detach(device_t self) -{ - struct ukbd_softc *sc = device_get_softc(self); - const char *devinfo = device_get_desc(self); - - DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); - if (sc->sc_enabled) - return (ENXIO); - - if (devinfo) { - device_set_desc(self, NULL); - free((void *)devinfo, M_USB); + if (probe_keyboard(uaa, flags)) + return ENXIO; + else + KBD_FOUND_DEVICE(kbd); + ukbd_clear_state(kbd); + state->ks_mode = K_XLATE; + state->ks_iface = uaa->iface; + state->ks_uaa = uaa; + state->ks_intrfunc = func; + state->ks_ifstate = 0; + callout_handle_init(&state->ks_timeout_handle); + /* + * FIXME: set the initial value for lock keys in ks_state + * according to the BIOS data? + */ + KBD_PROBE_DONE(kbd); + } + if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) { + if (KBD_HAS_DEVICE(kbd) + && init_keyboard((ukbd_state_t *)kbd->kb_data, + &kbd->kb_type, kbd->kb_flags)) + return ENXIO; + ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(state->ks_state)); + KBD_INIT_DONE(kbd); + } + if (!KBD_IS_CONFIGURED(kbd)) { + if (kbd_register(kbd) < 0) + return ENXIO; + ukbd_enable_intr(kbd, TRUE, state->ks_intrfunc); + ukbd_timeout((void *)kbd); + KBD_CONFIG_DONE(kbd); } - return (0); -} -#endif - -void -ukbd_disco(p) - void *p; -{ - struct ukbd_softc *sc = p; - - DPRINTF(("ukbd_disco: sc=%p\n", sc)); - usbd_abort_pipe(sc->sc_intrpipe); - sc->sc_disconnected = 1; + return 0; } -int -ukbd_enable(v, on) - void *v; - int on; +static void +ukbd_disconnect(void *p) { - struct ukbd_softc *sc = v; + keyboard_t *kbd; + ukbd_state_t *state; + + kbd = p; + state = (ukbd_state_t *)kbd->kb_data; + untimeout(ukbd_timeout, (void *)kbd, state->ks_timeout_handle); + DPRINTF(("ukbd_disconnect: state=%p\n", state)); + usbd_abort_pipe(state->ks_intrpipe); + state->ks_ifstate |= DISCONNECTED; +} + +static int +ukbd_enable_intr(keyboard_t *kbd, int on, usbd_intr_t *func) +{ + ukbd_state_t *state = (ukbd_state_t *)kbd->kb_data; usbd_status r; if (on) { /* Set up interrupt pipe. */ - if (sc->sc_enabled) - return (EBUSY); + if (state->ks_ifstate & INTRENABLED) + return EBUSY; - sc->sc_enabled = 1; - r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, + state->ks_ifstate |= INTRENABLED; + r = usbd_open_pipe_intr(state->ks_iface, state->ks_ep_addr, USBD_SHORT_XFER_OK, - &sc->sc_intrpipe, sc, &sc->sc_ndata, - sizeof(sc->sc_ndata), ukbd_intr); + &state->ks_intrpipe, kbd, + &state->ks_ndata, + sizeof(state->ks_ndata), func); if (r != USBD_NORMAL_COMPLETION) return (EIO); - usbd_set_disco(sc->sc_intrpipe, ukbd_disco, sc); + usbd_set_disco(state->ks_intrpipe, ukbd_disconnect, kbd); } else { /* Disable interrupts. */ - usbd_abort_pipe(sc->sc_intrpipe); - usbd_close_pipe(sc->sc_intrpipe); + usbd_abort_pipe(state->ks_intrpipe); + usbd_close_pipe(state->ks_intrpipe); - sc->sc_enabled = 0; + state->ks_ifstate &= ~INTRENABLED; } return (0); } -void -ukbd_intr(reqh, addr, status) - usbd_request_handle reqh; - usbd_private_handle addr; - usbd_status status; +/* finish using this keyboard */ +static int +ukbd_term(keyboard_t *kbd) { - struct ukbd_softc *sc = addr; - struct ukbd_data *ud = &sc->sc_ndata; + ukbd_state_t *state; + + state = (ukbd_state_t *)kbd->kb_data; + if (state->ks_ifstate & INTRENABLED) + return ENXIO; + +#ifdef USB_DEBUG + /* good bye, and thanks for all the fish */ + set_leds(state, LED_NUM); + DELAY(50000); + set_leds(state, LED_CAP); + DELAY(30000); + set_leds(state, LED_SCR); + DELAY(20000); + set_leds(state, LED_CAP); + DELAY(15000); + set_leds(state, LED_NUM); +#endif /* USB_DEBUG */ + + kbd_unregister(kbd); + + return 0; +} + + +/* keyboard interrupt routine */ + +static void +ukbd_timeout(void *arg) +{ + keyboard_t *kbd; + ukbd_state_t *state; + int s; + + kbd = (keyboard_t *)arg; + state = (ukbd_state_t *)kbd->kb_data; + s = splusb(); + (*kbdsw[kbd->kb_index]->intr)(kbd, (void *)USBD_NORMAL_COMPLETION); + state->ks_timeout_handle = timeout(ukbd_timeout, arg, hz/40); + splx(s); +} + +static int +ukbd_interrupt(keyboard_t *kbd, void *arg) +{ + usbd_status status = (usbd_status)arg; + ukbd_state_t *state = (ukbd_state_t *)kbd->kb_data; + struct ukbd_data *ud = &state->ks_ndata; + struct timeval tv; + u_long now; int mod, omod; - int ibuf[MAXKEYS]; /* chars events */ - int nkeys, i, j; int key, c; -#define ADDKEY(c) ibuf[nkeys++] = (c) + int i, j; + +#define ADDKEY1(c) \ + if (state->ks_inputs < INPUTBUFSIZE) { \ + state->ks_input[state->ks_inputtail] = (c); \ + ++state->ks_inputs; \ + state->ks_inputtail = (state->ks_inputtail + 1)%INPUTBUFSIZE; \ + } DPRINTFN(5, ("ukbd_intr: status=%d\n", status)); if (status == USBD_CANCELLED) - return; + return 0; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ukbd_intr: status=%d\n", status)); - usbd_clear_endpoint_stall_async(sc->sc_intrpipe); - return; + usbd_clear_endpoint_stall_async(state->ks_intrpipe); + return 0; } - DPRINTFN(5, (" mod=0x%02x key0=0x%02x key1=0x%02x\n", - ud->modifiers, ud->keycode[0], ud->keycode[1])); - if (ud->keycode[0] == KEY_ERROR) - return; /* ignore */ - nkeys = 0; + return 0; /* ignore */ + + getmicrouptime(&tv); + now = (u_long)tv.tv_sec*1000 + (u_long)tv.tv_usec/1000; + mod = ud->modifiers; - omod = sc->sc_odata.modifiers; - if (mod != omod) + omod = state->ks_odata.modifiers; + if (mod != omod) { for (i = 0; i < NMOD; i++) if (( mod & ukbd_mods[i].mask) != (omod & ukbd_mods[i].mask)) - ADDKEY(ukbd_mods[i].key | + ADDKEY1(ukbd_mods[i].key | (mod & ukbd_mods[i].mask - ? PRESS : RELEASE)); - if (memcmp(ud->keycode, sc->sc_odata.keycode, NKEYCODE) != 0) { - /* Check for released keys. */ - for (i = 0; i < NKEYCODE; i++) { - key = sc->sc_odata.keycode[i]; - if (key == 0) - continue; - for (j = 0; j < NKEYCODE; j++) - if (key == ud->keycode[j]) - goto rfound; - c = ukbd_trtab[key]; - if (c) - ADDKEY(c | RELEASE); - rfound: - ; + ? KEY_PRESS : KEY_RELEASE)); + } + + /* Check for released keys. */ + for (i = 0; i < NKEYCODE; i++) { + key = state->ks_odata.keycode[i]; + if (key == 0) + break; + for (j = 0; j < NKEYCODE; j++) { + if (ud->keycode[j] == 0) + break; + if (key == ud->keycode[j]) + goto rfound; } + ADDKEY1(key | KEY_RELEASE); + rfound: + ; + } - /* Check for pressed keys. */ - for (i = 0; i < NKEYCODE; i++) { - key = ud->keycode[i]; - if (key == 0) - continue; - for (j = 0; j < NKEYCODE; j++) - if (key == sc->sc_odata.keycode[j]) + /* Check for pressed keys. */ + for (i = 0; i < NKEYCODE; i++) { + key = ud->keycode[i]; + if (key == 0) + break; + state->ks_ntime[i] = now + kbd->kb_delay1; + for (j = 0; j < NKEYCODE; j++) { + if (state->ks_odata.keycode[j] == 0) + break; + if (key == state->ks_odata.keycode[j]) { + state->ks_ntime[i] = state->ks_otime[j]; + if (state->ks_otime[j] > now) goto pfound; - c = ukbd_trtab[key]; - DPRINTFN(2,("ukbd_intr: press key=0x%02x -> 0x%02x\n", - key, c)); - if (c) - ADDKEY(c | PRESS); - pfound: - ; - } - } - sc->sc_odata = *ud; - - if (nkeys == 0) - return; - -#if defined(__NetBSD__) - if (sc->sc_polling) { - DPRINTFN(1,("ukbd_intr: pollchar = 0x%02x\n", ibuf[0])); - if (nkeys > 0) - sc->sc_pollchar = ibuf[0]; /* XXX lost keys? */ - return; - } -#ifdef WSDISPLAY_COMPAT_RAWKBD - if (sc->sc_rawkbd) { - char cbuf[MAXKEYS * 2]; - int npress; - - for (npress = i = j = 0; i < nkeys; i++, j++) { - c = ibuf[i]; - if (c & 0x80) - cbuf[j++] = 0xe0; - cbuf[j] = c & 0x7f; - if (c & RELEASE) - cbuf[j] |= 0x80; - else { - /* remember keys for autorepeat */ - if (c & 0x80) - sc->sc_rep[npress++] = 0xe0; - sc->sc_rep[npress++] = c & 0x7f; + state->ks_ntime[i] = now + kbd->kb_delay2; + break; } } - wskbd_rawinput(sc->sc_wskbddev, cbuf, j); - untimeout(ukbd_rawrepeat, sc); - if (npress != 0) { - sc->sc_nrep = npress; - timeout(ukbd_rawrepeat, sc, hz * REP_DELAY1 / 1000); - } - return; + ADDKEY1(key | KEY_PRESS); + pfound: + ; } + + state->ks_odata = *ud; + bcopy(state->ks_ntime, state->ks_otime, sizeof(state->ks_ntime)); + if (state->ks_inputs <= 0) + return 0; + +#if 0 +#if USB_DEBUG + for (i = state->ks_inputhead, j = 0; j < state->ks_inputs; ++j, + i = (i + 1)%INPUTBUFSIZE) { + c = state->ks_input[i]; + printf("0x%x (%d) %s\n", c, c, + (c & KEY_RELEASE) ? "released":"pressed"); + } + if (ud->modifiers) + printf("mod:0x%04x ", ud->modifiers); + for (i = 0; i < NKEYCODE; i++) { + if (ud->keycode[i]) + printf("%d ", ud->keycode[i]); + } + printf("\n"); +#endif /* USB_DEBUG */ #endif - for (i = 0; i < nkeys; i++) { - c = ibuf[i]; - wskbd_input(sc->sc_wskbddev, - c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, - c & 0xff); + if (state->ks_polling) + return 0; + + if (KBD_IS_ACTIVE(kbd) && KBD_IS_BUSY(kbd)) { + /* let the callback function to process the input */ + (*kbd->kb_callback.kc_func)(kbd, KBDIO_KEYINPUT, + kbd->kb_callback.kc_arg); + } else { + /* read and discard the input; no one is waiting for it */ + do { + c = ukbd_read_char(kbd, FALSE); + } while (c != NOKEY); } -#elif defined(__FreeBSD__) - /* XXX shouldn't the keys be used? */ - for (i = 0; i < nkeys; i++) { - c = ibuf[i]; - printf("%c (%d) %s ", - ((c&0xff) < 32 || (c&0xff) > 126? '.':(c&0xff)), c, - (c&RELEASE? "released":"pressed")); - if (ud->modifiers) - printf("mod = 0x%04x ", ud->modifiers); - for (i = 0; i < NKEYCODE; i++) - if (ud->keycode[i]) - printf("%d ", ud->keycode[i]); - printf("\n"); - } -#endif + + return 0; } -void -ukbd_set_leds(v, leds) - void *v; - int leds; +static int +ukbd_getc(ukbd_state_t *state) { - struct ukbd_softc *sc = v; - u_int8_t res; - - DPRINTF(("ukbd_set_leds: sc=%p leds=%d\n", sc, leds)); - - sc->sc_leds = leds; -#if defined(__NetBSD__) - res = 0; - if (leds & WSKBD_LED_SCROLL) - res |= SCROLL_LOCK; - if (leds & WSKBD_LED_NUM) - res |= NUM_LOCK; - if (leds & WSKBD_LED_CAPS) - res |= CAPS_LOCK; -#elif defined(__FreeBSD__) - res = leds; -#endif - usbd_set_report_async(sc->sc_iface, UHID_OUTPUT_REPORT, 0, &res, 1); -} - -#if defined(__NetBSD__) - -#ifdef WSDISPLAY_COMPAT_RAWKBD -void -ukbd_rawrepeat(v) - void *v; -{ - struct ukbd_softc *sc = v; - - wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep); - timeout(ukbd_rawrepeat, sc, hz * REP_DELAYN / 1000); -} -#endif - -int -ukbd_ioctl(v, cmd, data, flag, p) - void *v; - u_long cmd; - caddr_t data; - int flag; - struct proc *p; -{ - struct ukbd_softc *sc = v; - - switch (cmd) { - case WSKBDIO_GTYPE: - *(int *)data = WSKBD_TYPE_USB; - return (0); - case WSKBDIO_SETLEDS: - ukbd_set_leds(v, *(int *)data); - return (0); - case WSKBDIO_GETLEDS: - *(int *)data = sc->sc_leds; - return (0); -#ifdef WSDISPLAY_COMPAT_RAWKBD - case WSKBDIO_SETMODE: - DPRINTF(("ukbd_ioctl: set raw = %d\n", *(int *)data)); - sc->sc_rawkbd = *(int *)data == WSKBD_RAW; - untimeout(ukbd_rawrepeat, sc); - return (0); -#endif - } - return (-1); -} - -/* Console interface. */ -void -ukbd_cngetc(v, type, data) - void *v; - u_int *type; - int *data; -{ - struct ukbd_softc *sc = v; - usbd_lock_token s; + usbd_lock_token l; int c; + int s; - DPRINTFN(1,("ukbd_cngetc: enter\n")); - s = usbd_lock(); - sc->sc_polling = 1; - sc->sc_pollchar = -1; - while(sc->sc_pollchar == -1) - usbd_dopoll(sc->sc_iface); - sc->sc_polling = 0; - c = sc->sc_pollchar; - *type = c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; - *data = c & 0xff; - usbd_unlock(s); - DPRINTFN(1,("ukbd_cngetc: return 0x%02x\n", c)); + if (state->ks_polling) { + DPRINTFN(1,("ukbd_getc: polling\n")); + l = usbd_lock(); + while (state->ks_inputs <= 0) + usbd_dopoll(state->ks_iface); + usbd_unlock(l); + } + s = splusb(); + if (state->ks_inputs <= 0) { + c = -1; + } else { + c = state->ks_input[state->ks_inputhead]; + --state->ks_inputs; + state->ks_inputhead = (state->ks_inputhead + 1)%INPUTBUFSIZE; + } + splx(s); + return c; } -void -ukbd_cnpollc(v, on) - void *v; - int on; +/* test the interface to the device */ +static int +ukbd_test_if(keyboard_t *kbd) { - struct ukbd_softc *sc = v; - - DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); - - usbd_set_polling(sc->sc_iface, on); + return 0; } -int -ukbd_cnattach(v) - void *v; +/* + * Enable the access to the device; until this function is called, + * the client cannot read from the keyboard. + */ +static int +ukbd_enable_kbd(keyboard_t *kbd) { - struct ukbd_softc *sc = v; + int s; - DPRINTF(("ukbd_cnattach: sc=%p\n", sc)); - wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata); - return (0); + s = splusb(); + KBD_ACTIVATE(kbd); + splx(s); + return 0; } -#endif /* NetBSD */ +/* disallow the access to the device */ +static int +ukbd_disable(keyboard_t *kbd) +{ + int s; -#if defined(__FreeBSD__) -DRIVER_MODULE(ukbd, uhub, ukbd_driver, ukbd_devclass, usbd_driver_load, 0); + s = splusb(); + KBD_DEACTIVATE(kbd); + splx(s); + return 0; +} + +/* read one byte from the keyboard if it's allowed */ +static int +ukbd_read(keyboard_t *kbd, int wait) +{ + ukbd_state_t *state; + int usbcode; +#ifdef UKBD_EMULATE_ATSCANCODE + int keycode; + int scancode; #endif + + state = (ukbd_state_t *)kbd->kb_data; +#ifdef UKBD_EMULATE_ATSCANCODE + if (state->ks_buffered_char[0]) { + scancode = state->ks_buffered_char[0]; + if (scancode & SCAN_PREFIX) { + state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; + return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } else { + state->ks_buffered_char[0] = state->ks_buffered_char[1]; + state->ks_buffered_char[1] = 0; + return scancode; + } + } +#endif /* UKBD_EMULATE_ATSCANCODE */ + + /* XXX */ + usbcode = ukbd_getc(state); + if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) + return -1; +#ifdef UKBD_EMULATE_ATSCANCODE + keycode = ukbd_trtab[KEY_INDEX(usbcode)]; + if (keycode == NN) + return -1; + + scancode = keycode2scancode(keycode, state->ks_ndata.modifiers, + usbcode & KEY_RELEASE); + if (scancode & SCAN_PREFIX) { + if (scancode & SCAN_PREFIX_CTL) { + state->ks_buffered_char[0] = + 0x1d | (scancode & SCAN_RELEASE); /* Ctrl */ + state->ks_buffered_char[1] = scancode & ~SCAN_PREFIX; + } else if (scancode & SCAN_PREFIX_SHIFT) { + state->ks_buffered_char[0] = + 0x2a | (scancode & SCAN_RELEASE); /* Shift */ + state->ks_buffered_char[1] = + scancode & ~SCAN_PREFIX_SHIFT; + } else { + state->ks_buffered_char[0] = scancode & ~SCAN_PREFIX; + state->ks_buffered_char[1] = 0; + } + return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } + return scancode; +#else /* !UKBD_EMULATE_ATSCANCODE */ + return usbcode; +#endif /* UKBD_EMULATE_ATSCANCODE */ +} + +/* check if data is waiting */ +static int +ukbd_check(keyboard_t *kbd) +{ + if (!KBD_IS_ACTIVE(kbd)) + return FALSE; +#ifdef UKBD_EMULATE_ATSCANCODE + if (((ukbd_state_t *)kbd->kb_data)->ks_buffered_char[0]) + return TRUE; +#endif + if (((ukbd_state_t *)kbd->kb_data)->ks_inputs > 0) + return TRUE; + return FALSE; +} + +/* read char from the keyboard */ +static u_int +ukbd_read_char(keyboard_t *kbd, int wait) +{ + ukbd_state_t *state; + u_int action; + int usbcode; + int keycode; +#ifdef UKBD_EMULATE_ATSCANCODE + int scancode; +#endif + + state = (ukbd_state_t *)kbd->kb_data; +next_code: + /* do we have a composed char to return? */ + if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) { + action = state->ks_composed_char; + state->ks_composed_char = 0; + if (action > UCHAR_MAX) + return ERRKEY; + return action; + } + +#ifdef UKBD_EMULATE_ATSCANCODE + /* do we have a pending raw scan code? */ + if (state->ks_mode == K_RAW) { + if (state->ks_buffered_char[0]) { + scancode = state->ks_buffered_char[0]; + if (scancode & SCAN_PREFIX) { + state->ks_buffered_char[0] = + scancode & ~SCAN_PREFIX; + return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } else { + state->ks_buffered_char[0] = + state->ks_buffered_char[1]; + state->ks_buffered_char[1] = 0; + return scancode; + } + } + } +#endif /* UKBD_EMULATE_ATSCANCODE */ + + /* see if there is something in the keyboard port */ + /* XXX */ + usbcode = ukbd_getc(state); + if (usbcode == -1) + return NOKEY; + +#ifdef UKBD_EMULATE_ATSCANCODE + /* USB key index -> key code -> AT scan code */ + keycode = ukbd_trtab[KEY_INDEX(usbcode)]; + if (keycode == NN) + return NOKEY; + + /* return an AT scan code for the K_RAW mode */ + if (state->ks_mode == K_RAW) { + scancode = keycode2scancode(keycode, state->ks_ndata.modifiers, + usbcode & KEY_RELEASE); + if (scancode & SCAN_PREFIX) { + if (scancode & SCAN_PREFIX_CTL) { + state->ks_buffered_char[0] = + 0x1d | (scancode & SCAN_RELEASE); + state->ks_buffered_char[1] = + scancode & ~SCAN_PREFIX; + } else if (scancode & SCAN_PREFIX_SHIFT) { + state->ks_buffered_char[0] = + 0x2a | (scancode & SCAN_RELEASE); + state->ks_buffered_char[1] = + scancode & ~SCAN_PREFIX_SHIFT; + } else { + state->ks_buffered_char[0] = + scancode & ~SCAN_PREFIX; + state->ks_buffered_char[1] = 0; + } + return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1); + } + return scancode; + } +#else /* !UKBD_EMULATE_ATSCANCODE */ + /* return the byte as is for the K_RAW mode */ + if (state->ks_mode == K_RAW) + return usbcode; + + /* USB key index -> key code */ + keycode = ukbd_trtab[KEY_INDEX(usbcode)]; + if (keycode == NN) + return NOKEY; +#endif /* UKBD_EMULATE_ATSCANCODE */ + + switch (keycode) { + case 0x38: /* left alt (compose key) */ + if (usbcode & KEY_RELEASE) { + if (state->ks_flags & COMPOSE) { + state->ks_flags &= ~COMPOSE; + if (state->ks_composed_char > UCHAR_MAX) + state->ks_composed_char = 0; + } + } else { + if (!(state->ks_flags & COMPOSE)) { + state->ks_flags |= COMPOSE; + state->ks_composed_char = 0; + } + } + break; + /* XXX: I don't like these... */ + case 0x5c: /* print screen */ + if (state->ks_flags & ALTS) + keycode = 0x54; /* sysrq */ + break; + case 0x68: /* pause/break */ + if (state->ks_flags & CTLS) + keycode = 0x6c; /* break */ + break; + } + + /* return the key code in the K_CODE mode */ + if (usbcode & KEY_RELEASE) + keycode |= SCAN_RELEASE; + if (state->ks_mode == K_CODE) + return keycode; + + /* compose a character code */ + if (state->ks_flags & COMPOSE) { + switch (keycode) { + /* key pressed, process it */ + case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ + state->ks_composed_char *= 10; + state->ks_composed_char += keycode - 0x40; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ + state->ks_composed_char *= 10; + state->ks_composed_char += keycode - 0x47; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ + state->ks_composed_char *= 10; + state->ks_composed_char += keycode - 0x4E; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + case 0x52: /* keypad 0 */ + state->ks_composed_char *= 10; + if (state->ks_composed_char > UCHAR_MAX) + return ERRKEY; + goto next_code; + + /* key released, no interest here */ + case SCAN_RELEASE | 0x47: + case SCAN_RELEASE | 0x48: + case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */ + case SCAN_RELEASE | 0x4B: + case SCAN_RELEASE | 0x4C: + case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */ + case SCAN_RELEASE | 0x4F: + case SCAN_RELEASE | 0x50: + case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */ + case SCAN_RELEASE | 0x52: /* keypad 0 */ + goto next_code; + + case 0x38: /* left alt key */ + break; + + default: + if (state->ks_composed_char > 0) { + state->ks_flags &= ~COMPOSE; + state->ks_composed_char = 0; + return ERRKEY; + } + break; + } + } + + /* keycode to key action */ + action = genkbd_keyaction(kbd, SCAN_CHAR(keycode), + keycode & SCAN_RELEASE, &state->ks_state, + &state->ks_accents); + if (action == NOKEY) + goto next_code; + else + return action; +} + +/* check if char is waiting */ +static int +ukbd_check_char(keyboard_t *kbd) +{ + ukbd_state_t *state; + + if (!KBD_IS_ACTIVE(kbd)) + return FALSE; + state = (ukbd_state_t *)kbd->kb_data; + if (!(state->ks_flags & COMPOSE) && (state->ks_composed_char > 0)) + return TRUE; + if (state->ks_inputs > 0) + return TRUE; + return FALSE; +} + +/* some useful control functions */ +static int +ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) +{ + /* trasnlate LED_XXX bits into the device specific bits */ + static u_char ledmap[8] = { + 0, 2, 1, 3, 4, 6, 5, 7, + }; + ukbd_state_t *state = kbd->kb_data; + int s; + int i; + + s = splusb(); + switch (cmd) { + + case KDGKBMODE: /* get keyboard mode */ + *(int *)arg = state->ks_mode; + break; + case KDSKBMODE: /* set keyboard mode */ + switch (*(int *)arg) { + case K_XLATE: + if (state->ks_mode != K_XLATE) { + /* make lock key state and LED state match */ + state->ks_state &= ~LOCK_MASK; + state->ks_state |= KBD_LED_VAL(kbd); + } + /* FALL THROUGH */ + case K_RAW: + case K_CODE: + if (state->ks_mode != *(int *)arg) { + ukbd_clear_state(kbd); + state->ks_mode = *(int *)arg; + } + break; + default: + splx(s); + return EINVAL; + } + break; + + case KDGETLED: /* get keyboard LED */ + *(int *)arg = KBD_LED_VAL(kbd); + break; + case KDSETLED: /* set keyboard LED */ + /* NOTE: lock key state in ks_state won't be changed */ + if (*(int *)arg & ~LOCK_MASK) { + splx(s); + return EINVAL; + } + i = *(int *)arg; + /* replace CAPS LED with ALTGR LED for ALTGR keyboards */ + if (kbd->kb_keymap->n_keys > ALTGR_OFFSET) { + if (i & ALKED) + i |= CLKED; + else + i &= ~CLKED; + } + if (KBD_HAS_DEVICE(kbd)) { + set_leds(state, ledmap[i & LED_MASK]); + /* XXX: error check? */ + } + KBD_LED_VAL(kbd) = *(int *)arg; + break; + + case KDGKBSTATE: /* get lock key state */ + *(int *)arg = state->ks_state & LOCK_MASK; + break; + case KDSKBSTATE: /* set lock key state */ + if (*(int *)arg & ~LOCK_MASK) { + splx(s); + return EINVAL; + } + state->ks_state &= ~LOCK_MASK; + state->ks_state |= *(int *)arg; + splx(s); + /* set LEDs and quit */ + return ukbd_ioctl(kbd, KDSETLED, arg); + + case KDSETREPEAT: /* set keyboard repeat rate (new interface) */ + splx(s); + if (!KBD_HAS_DEVICE(kbd)) + return 0; + if (((int *)arg)[1] < 0) + return EINVAL; + if (((int *)arg)[0] < 0) + return EINVAL; + else if (((int *)arg)[0] == 0) /* fastest possible value */ + kbd->kb_delay1 = 200; + else + kbd->kb_delay1 = ((int *)arg)[0]; + kbd->kb_delay2 = ((int *)arg)[1]; + return 0; + + case KDSETRAD: /* set keyboard repeat rate (old interface) */ + splx(s); + return set_typematic(kbd, *(int *)arg); + + case PIO_KEYMAP: /* set keyboard translation table */ + case PIO_KEYMAPENT: /* set keyboard translation table entry */ + case PIO_DEADKEYMAP: /* set accent key translation table */ + state->ks_accents = 0; + /* FALL THROUGH */ + default: + splx(s); + return genkbd_commonioctl(kbd, cmd, arg); + } + + splx(s); + return 0; +} + +/* lock the access to the keyboard */ +static int +ukbd_lock(keyboard_t *kbd, int lock) +{ + /* XXX ? */ + return TRUE; +} + +/* clear the internal state of the keyboard */ +static void +ukbd_clear_state(keyboard_t *kbd) +{ + ukbd_state_t *state; + + state = (ukbd_state_t *)kbd->kb_data; + state->ks_flags = 0; + state->ks_polling = 0; + state->ks_state &= LOCK_MASK; /* preserve locking key state */ + state->ks_accents = 0; + state->ks_composed_char = 0; +#ifdef UKBD_EMULATE_ATSCANCODE + state->ks_buffered_char[0] = 0; + state->ks_buffered_char[1] = 0; +#endif + bzero(&state->ks_ndata, sizeof(state->ks_ndata)); + bzero(&state->ks_odata, sizeof(state->ks_odata)); + bzero(&state->ks_ntime, sizeof(state->ks_ntime)); + bzero(&state->ks_otime, sizeof(state->ks_otime)); +} + +/* save the internal state */ +static int +ukbd_get_state(keyboard_t *kbd, void *buf, size_t len) +{ + if (len == 0) + return sizeof(ukbd_state_t); + if (len < sizeof(ukbd_state_t)) + return -1; + bcopy(kbd->kb_data, buf, sizeof(ukbd_state_t)); + return 0; +} + +/* set the internal state */ +static int +ukbd_set_state(keyboard_t *kbd, void *buf, size_t len) +{ + if (len < sizeof(ukbd_state_t)) + return ENOMEM; + bcopy(buf, kbd->kb_data, sizeof(ukbd_state_t)); + return 0; +} + +static int +ukbd_poll(keyboard_t *kbd, int on) +{ + ukbd_state_t *state; + int s; + + state = (ukbd_state_t *)kbd->kb_data; + + s = splusb(); + if (on) { + if (state->ks_polling == 0) + usbd_set_polling(state->ks_iface, on); + ++state->ks_polling; + } else { + --state->ks_polling; + if (state->ks_polling == 0) + usbd_set_polling(state->ks_iface, on); + } + splx(s); + return 0; +} + +/* local functions */ + +static int +probe_keyboard(struct usb_attach_arg *uaa, int flags) +{ + usb_interface_descriptor_t *id; + + /* Check that this is a keyboard that speaks the boot protocol. */ + if (!uaa->iface) + return EINVAL; + id = usbd_get_interface_descriptor(uaa->iface); + if (id->bInterfaceClass != UCLASS_HID || + id->bInterfaceSubClass != USUBCLASS_BOOT || + id->bInterfaceProtocol != UPROTO_BOOT_KEYBOARD) + return EINVAL; + return 0; +} + +static int +init_keyboard(ukbd_state_t *state, int *type, int flags) +{ + usb_endpoint_descriptor_t *ed; + usbd_status r; + + *type = KB_OTHER; + + state->ks_ifstate |= DISCONNECTED; + + ed = usbd_interface2endpoint_descriptor(state->ks_iface, 0); + if (!ed) { + printf("ukbd: could not read endpoint descriptor\n"); + return EIO; + } + + DPRINTFN(10,("ukbd:init_keyboard: \ +bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n", + ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, + ed->bEndpointAddress & UE_IN ? "in" : "out", + ed->bmAttributes & UE_XFERTYPE, + UGETW(ed->wMaxPacketSize), ed->bInterval)); + + if ((ed->bEndpointAddress & UE_IN) != UE_IN || + (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { + printf("ukbd: unexpected endpoint\n"); + return EINVAL; + } + + if ((usbd_get_quirks(state->ks_uaa->device)->uq_flags & UQ_NO_SET_PROTO) == 0) { + r = usbd_set_protocol(state->ks_iface, 0); + DPRINTFN(5, ("ukbd:init_keyboard: protocol set\n")); + if (r != USBD_NORMAL_COMPLETION) { + printf("ukbd: set protocol failed\n"); + return EIO; + } + } + /* Ignore if SETIDLE fails since it is not crucial. */ + usbd_set_idle(state->ks_iface, 0, 0); + + state->ks_ep_addr = ed->bEndpointAddress; + state->ks_ifstate &= ~DISCONNECTED; + + return 0; +} + +static void +set_leds(ukbd_state_t *state, int leds) +{ + u_int8_t res = leds; + + DPRINTF(("ukbd:set_leds: state=%p leds=%d\n", state, leds)); + + usbd_set_report_async(state->ks_iface, UHID_OUTPUT_REPORT, 0, &res, 1); +} + +static int +set_typematic(keyboard_t *kbd, int code) +{ + static int delays[] = { 250, 500, 750, 1000 }; + static int rates[] = { 34, 38, 42, 46, 50, 55, 59, 63, + 68, 76, 84, 92, 100, 110, 118, 126, + 136, 152, 168, 184, 200, 220, 236, 252, + 272, 304, 336, 368, 400, 440, 472, 504 }; + + if (code & ~0x7f) + return EINVAL; + kbd->kb_delay1 = delays[(code >> 5) & 3]; + kbd->kb_delay2 = rates[code & 0x1f]; + return 0; +} + +#ifdef UKBD_EMULATE_ATSCANCODE +static int +keycode2scancode(int keycode, int shift, int up) +{ + static int scan[] = { + 0x1c, 0x1d, 0x35, + 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */ + 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f, + 0x50, 0x51, 0x52, 0x53, + 0x46, /* XXX Pause/Break */ + 0x5b, 0x5c, 0x5d, + }; + int scancode; + + scancode = keycode; + if ((keycode >= 89) && (keycode < 89 + sizeof(scan)/sizeof(scan[0]))) + scancode = scan[keycode - 89] | SCAN_PREFIX_E0; + /* Pause/Break */ + if ((keycode == 104) && !(shift & (MOD_CONTROL_L | MOD_CONTROL_R))) + scancode = 0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL; + if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) + scancode &= ~SCAN_PREFIX_SHIFT; + return (scancode | (up ? SCAN_RELEASE : SCAN_PRESS)); +} +#endif /* UKBD_EMULATE_ATSCANCODE */