Index: head/sys/dev/kbd/kbd.c =================================================================== --- head/sys/dev/kbd/kbd.c (revision 197329) +++ head/sys/dev/kbd/kbd.c (revision 197330) @@ -1,1451 +1,1463 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer as * the first lines of this file unmodified. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KBD_INDEX(dev) dev2unit(dev) #define KB_QSIZE 512 #define KB_BUFSIZE 64 typedef struct genkbd_softc { int gkb_flags; /* flag/status bits */ #define KB_ASLEEP (1 << 0) struct selinfo gkb_rsel; char gkb_q[KB_QSIZE]; /* input queue */ unsigned int gkb_q_start; unsigned int gkb_q_length; } genkbd_softc_t; static SLIST_HEAD(, keyboard_driver) keyboard_drivers = SLIST_HEAD_INITIALIZER(keyboard_drivers); SET_DECLARE(kbddriver_set, const keyboard_driver_t); /* local arrays */ /* * We need at least one entry each in order to initialize a keyboard * for the kernel console. The arrays will be increased dynamically * when necessary. */ static int keyboards = 1; static keyboard_t *kbd_ini; static keyboard_t **keyboard = &kbd_ini; static keyboard_switch_t *kbdsw_ini; keyboard_switch_t **kbdsw = &kbdsw_ini; static int keymap_restrict_change; SYSCTL_NODE(_hw, OID_AUTO, kbd, CTLFLAG_RD, 0, "kbd"); SYSCTL_INT(_hw_kbd, OID_AUTO, keymap_restrict_change, CTLFLAG_RW, &keymap_restrict_change, 0, "restrict ability to change keymap"); #define ARRAY_DELTA 4 static int kbd_realloc_array(void) { keyboard_t **new_kbd; keyboard_switch_t **new_kbdsw; int newsize; int s; s = spltty(); newsize = ((keyboards + ARRAY_DELTA)/ARRAY_DELTA)*ARRAY_DELTA; new_kbd = malloc(sizeof(*new_kbd)*newsize, M_DEVBUF, M_NOWAIT|M_ZERO); if (new_kbd == NULL) { splx(s); return (ENOMEM); } new_kbdsw = malloc(sizeof(*new_kbdsw)*newsize, M_DEVBUF, M_NOWAIT|M_ZERO); if (new_kbdsw == NULL) { free(new_kbd, M_DEVBUF); splx(s); return (ENOMEM); } bcopy(keyboard, new_kbd, sizeof(*keyboard)*keyboards); bcopy(kbdsw, new_kbdsw, sizeof(*kbdsw)*keyboards); if (keyboards > 1) { free(keyboard, M_DEVBUF); free(kbdsw, M_DEVBUF); } keyboard = new_kbd; kbdsw = new_kbdsw; keyboards = newsize; splx(s); if (bootverbose) printf("kbd: new array size %d\n", keyboards); return (0); } /* * Low-level keyboard driver functions * Keyboard subdrivers, such as the AT keyboard driver and the USB keyboard * driver, call these functions to initialize the keyboard_t structure * and register it to the virtual keyboard driver `kbd'. */ /* initialize the keyboard_t structure */ void kbd_init_struct(keyboard_t *kbd, char *name, int type, int unit, int config, int port, int port_size) { kbd->kb_flags = KB_NO_DEVICE; /* device has not been found */ kbd->kb_name = name; kbd->kb_type = type; kbd->kb_unit = unit; kbd->kb_config = config & ~KB_CONF_PROBE_ONLY; kbd->kb_led = 0; /* unknown */ kbd->kb_io_base = port; kbd->kb_io_size = port_size; kbd->kb_data = NULL; kbd->kb_keymap = NULL; kbd->kb_accentmap = NULL; kbd->kb_fkeytab = NULL; kbd->kb_fkeytab_size = 0; kbd->kb_delay1 = KB_DELAY1; /* these values are advisory only */ kbd->kb_delay2 = KB_DELAY2; kbd->kb_count = 0L; bzero(kbd->kb_lastact, sizeof(kbd->kb_lastact)); } void kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap, fkeytab_t *fkeymap, int fkeymap_size) { kbd->kb_keymap = keymap; kbd->kb_accentmap = accmap; kbd->kb_fkeytab = fkeymap; kbd->kb_fkeytab_size = fkeymap_size; } /* declare a new keyboard driver */ int kbd_add_driver(keyboard_driver_t *driver) { if (SLIST_NEXT(driver, link)) return (EINVAL); SLIST_INSERT_HEAD(&keyboard_drivers, driver, link); return (0); } int kbd_delete_driver(keyboard_driver_t *driver) { SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link); SLIST_NEXT(driver, link) = NULL; return (0); } /* register a keyboard and associate it with a function table */ int kbd_register(keyboard_t *kbd) { const keyboard_driver_t **list; const keyboard_driver_t *p; keyboard_t *mux; keyboard_info_t ki; int index; mux = kbd_get_keyboard(kbd_find_keyboard("kbdmux", -1)); for (index = 0; index < keyboards; ++index) { if (keyboard[index] == NULL) break; } if (index >= keyboards) { if (kbd_realloc_array()) return (-1); } kbd->kb_index = index; KBD_UNBUSY(kbd); KBD_VALID(kbd); kbd->kb_active = 0; /* disabled until someone calls kbd_enable() */ kbd->kb_token = NULL; kbd->kb_callback.kc_func = NULL; kbd->kb_callback.kc_arg = NULL; SLIST_FOREACH(p, &keyboard_drivers, link) { if (strcmp(p->name, kbd->kb_name) == 0) { keyboard[index] = kbd; kbdsw[index] = p->kbdsw; if (mux != NULL) { bzero(&ki, sizeof(ki)); strcpy(ki.kb_name, kbd->kb_name); ki.kb_unit = kbd->kb_unit; kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki); } return (index); } } SET_FOREACH(list, kbddriver_set) { p = *list; if (strcmp(p->name, kbd->kb_name) == 0) { keyboard[index] = kbd; kbdsw[index] = p->kbdsw; if (mux != NULL) { bzero(&ki, sizeof(ki)); strcpy(ki.kb_name, kbd->kb_name); ki.kb_unit = kbd->kb_unit; kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki); } return (index); } } return (-1); } int kbd_unregister(keyboard_t *kbd) { int error; int s; if ((kbd->kb_index < 0) || (kbd->kb_index >= keyboards)) return (ENOENT); if (keyboard[kbd->kb_index] != kbd) return (ENOENT); s = spltty(); if (KBD_IS_BUSY(kbd)) { error = (*kbd->kb_callback.kc_func)(kbd, KBDIO_UNLOADING, kbd->kb_callback.kc_arg); if (error) { splx(s); return (error); } if (KBD_IS_BUSY(kbd)) { splx(s); return (EBUSY); } } KBD_INVALID(kbd); keyboard[kbd->kb_index] = NULL; kbdsw[kbd->kb_index] = NULL; splx(s); return (0); } /* find a funciton table by the driver name */ keyboard_switch_t *kbd_get_switch(char *driver) { const keyboard_driver_t **list; const keyboard_driver_t *p; SLIST_FOREACH(p, &keyboard_drivers, link) { if (strcmp(p->name, driver) == 0) return (p->kbdsw); } SET_FOREACH(list, kbddriver_set) { p = *list; if (strcmp(p->name, driver) == 0) return (p->kbdsw); } return (NULL); } /* * Keyboard client functions * Keyboard clients, such as the console driver `syscons' and the keyboard * cdev driver, use these functions to claim and release a keyboard for * exclusive use. */ /* * find the keyboard specified by a driver name and a unit number * starting at given index */ int kbd_find_keyboard2(char *driver, int unit, int index) { int i; if ((index < 0) || (index >= keyboards)) return (-1); for (i = index; i < keyboards; ++i) { if (keyboard[i] == NULL) continue; if (!KBD_IS_VALID(keyboard[i])) continue; if (strcmp("*", driver) && strcmp(keyboard[i]->kb_name, driver)) continue; if ((unit != -1) && (keyboard[i]->kb_unit != unit)) continue; return (i); } return (-1); } /* find the keyboard specified by a driver name and a unit number */ int kbd_find_keyboard(char *driver, int unit) { return (kbd_find_keyboard2(driver, unit, 0)); } /* allocate a keyboard */ int kbd_allocate(char *driver, int unit, void *id, kbd_callback_func_t *func, void *arg) { int index; int s; if (func == NULL) return (-1); s = spltty(); index = kbd_find_keyboard(driver, unit); if (index >= 0) { if (KBD_IS_BUSY(keyboard[index])) { splx(s); return (-1); } keyboard[index]->kb_token = id; KBD_BUSY(keyboard[index]); keyboard[index]->kb_callback.kc_func = func; keyboard[index]->kb_callback.kc_arg = arg; kbdd_clear_state(keyboard[index]); } splx(s); return (index); } int kbd_release(keyboard_t *kbd, void *id) { int error; int s; s = spltty(); if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) { error = EINVAL; } else if (kbd->kb_token != id) { error = EPERM; } else { kbd->kb_token = NULL; KBD_UNBUSY(kbd); kbd->kb_callback.kc_func = NULL; kbd->kb_callback.kc_arg = NULL; kbdd_clear_state(kbd); error = 0; } splx(s); return (error); } int kbd_change_callback(keyboard_t *kbd, void *id, kbd_callback_func_t *func, void *arg) { int error; int s; s = spltty(); if (!KBD_IS_VALID(kbd) || !KBD_IS_BUSY(kbd)) { error = EINVAL; } else if (kbd->kb_token != id) { error = EPERM; } else if (func == NULL) { error = EINVAL; } else { kbd->kb_callback.kc_func = func; kbd->kb_callback.kc_arg = arg; error = 0; } splx(s); return (error); } /* get a keyboard structure */ keyboard_t *kbd_get_keyboard(int index) { if ((index < 0) || (index >= keyboards)) return (NULL); if (keyboard[index] == NULL) return (NULL); if (!KBD_IS_VALID(keyboard[index])) return (NULL); return (keyboard[index]); } /* * The back door for the console driver; configure keyboards * This function is for the kernel console to initialize keyboards * at very early stage. */ int kbd_configure(int flags) { const keyboard_driver_t **list; const keyboard_driver_t *p; SLIST_FOREACH(p, &keyboard_drivers, link) { if (p->configure != NULL) (*p->configure)(flags); } SET_FOREACH(list, kbddriver_set) { p = *list; if (p->configure != NULL) (*p->configure)(flags); } return (0); } #ifdef KBD_INSTALL_CDEV /* * Virtual keyboard cdev driver functions * The virtual keyboard driver dispatches driver functions to * appropriate subdrivers. */ #define KBD_UNIT(dev) dev2unit(dev) static d_open_t genkbdopen; static d_close_t genkbdclose; static d_read_t genkbdread; static d_write_t genkbdwrite; static d_ioctl_t genkbdioctl; static d_poll_t genkbdpoll; static struct cdevsw kbd_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = genkbdopen, .d_close = genkbdclose, .d_read = genkbdread, .d_write = genkbdwrite, .d_ioctl = genkbdioctl, .d_poll = genkbdpoll, .d_name = "kbd", }; int kbd_attach(keyboard_t *kbd) { if (kbd->kb_index >= keyboards) return (EINVAL); if (keyboard[kbd->kb_index] != kbd) return (EINVAL); kbd->kb_dev = make_dev(&kbd_cdevsw, kbd->kb_index, UID_ROOT, GID_WHEEL, 0600, "%s%r", kbd->kb_name, kbd->kb_unit); make_dev_alias(kbd->kb_dev, "kbd%r", kbd->kb_index); kbd->kb_dev->si_drv1 = malloc(sizeof(genkbd_softc_t), M_DEVBUF, M_WAITOK | M_ZERO); printf("kbd%d at %s%d\n", kbd->kb_index, kbd->kb_name, kbd->kb_unit); return (0); } int kbd_detach(keyboard_t *kbd) { if (kbd->kb_index >= keyboards) return (EINVAL); if (keyboard[kbd->kb_index] != kbd) return (EINVAL); free(kbd->kb_dev->si_drv1, M_DEVBUF); destroy_dev(kbd->kb_dev); return (0); } /* * Generic keyboard cdev driver functions * Keyboard subdrivers may call these functions to implement common * driver functions. */ static void genkbd_putc(genkbd_softc_t *sc, char c) { unsigned int p; if (sc->gkb_q_length == KB_QSIZE) return; p = (sc->gkb_q_start + sc->gkb_q_length) % KB_QSIZE; sc->gkb_q[p] = c; sc->gkb_q_length++; } static size_t genkbd_getc(genkbd_softc_t *sc, char *buf, size_t len) { /* Determine copy size. */ if (sc->gkb_q_length == 0) return (0); if (len >= sc->gkb_q_length) len = sc->gkb_q_length; if (len >= KB_QSIZE - sc->gkb_q_start) len = KB_QSIZE - sc->gkb_q_start; /* Copy out data and progress offset. */ memcpy(buf, sc->gkb_q + sc->gkb_q_start, len); sc->gkb_q_start = (sc->gkb_q_start + len) % KB_QSIZE; sc->gkb_q_length -= len; return (len); } static kbd_callback_func_t genkbd_event; static int genkbdopen(struct cdev *dev, int mode, int flag, struct thread *td) { keyboard_t *kbd; genkbd_softc_t *sc; int s; int i; s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { splx(s); return (ENXIO); } i = kbd_allocate(kbd->kb_name, kbd->kb_unit, sc, genkbd_event, (void *)sc); if (i < 0) { splx(s); return (EBUSY); } /* assert(i == kbd->kb_index) */ /* assert(kbd == kbd_get_keyboard(i)) */ /* * NOTE: even when we have successfully claimed a keyboard, * the device may still be missing (!KBD_HAS_DEVICE(kbd)). */ sc->gkb_q_length = 0; splx(s); return (0); } static int genkbdclose(struct cdev *dev, int mode, int flag, struct thread *td) { keyboard_t *kbd; genkbd_softc_t *sc; int s; /* * NOTE: the device may have already become invalid. * kbd == NULL || !KBD_IS_VALID(kbd) */ s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { /* XXX: we shall be forgiving and don't report error... */ } else { kbd_release(kbd, (void *)sc); } splx(s); return (0); } static int genkbdread(struct cdev *dev, struct uio *uio, int flag) { keyboard_t *kbd; genkbd_softc_t *sc; u_char buffer[KB_BUFSIZE]; int len; int error; int s; /* wait for input */ s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { splx(s); return (ENXIO); } while (sc->gkb_q_length == 0) { if (flag & O_NONBLOCK) { splx(s); return (EWOULDBLOCK); } sc->gkb_flags |= KB_ASLEEP; error = tsleep(sc, PZERO | PCATCH, "kbdrea", 0); kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) { splx(s); return (ENXIO); /* our keyboard has gone... */ } if (error) { sc->gkb_flags &= ~KB_ASLEEP; splx(s); return (error); } } splx(s); /* copy as much input as possible */ error = 0; while (uio->uio_resid > 0) { len = imin(uio->uio_resid, sizeof(buffer)); len = genkbd_getc(sc, buffer, len); if (len <= 0) break; error = uiomove(buffer, len, uio); if (error) break; } return (error); } static int genkbdwrite(struct cdev *dev, struct uio *uio, int flag) { keyboard_t *kbd; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) return (ENXIO); return (ENODEV); } static int genkbdioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { keyboard_t *kbd; int error; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((kbd == NULL) || !KBD_IS_VALID(kbd)) return (ENXIO); error = kbdd_ioctl(kbd, cmd, arg); if (error == ENOIOCTL) error = ENODEV; return (error); } static int genkbdpoll(struct cdev *dev, int events, struct thread *td) { keyboard_t *kbd; genkbd_softc_t *sc; int revents; int s; revents = 0; s = spltty(); sc = dev->si_drv1; kbd = kbd_get_keyboard(KBD_INDEX(dev)); if ((sc == NULL) || (kbd == NULL) || !KBD_IS_VALID(kbd)) { revents = POLLHUP; /* the keyboard has gone */ } else if (events & (POLLIN | POLLRDNORM)) { if (sc->gkb_q_length > 0) revents = events & (POLLIN | POLLRDNORM); else selrecord(td, &sc->gkb_rsel); } splx(s); return (revents); } static int genkbd_event(keyboard_t *kbd, int event, void *arg) { genkbd_softc_t *sc; size_t len; u_char *cp; int mode; int c; /* assert(KBD_IS_VALID(kbd)) */ sc = (genkbd_softc_t *)arg; switch (event) { case KBDIO_KEYINPUT: break; case KBDIO_UNLOADING: /* the keyboard is going... */ kbd_release(kbd, (void *)sc); if (sc->gkb_flags & KB_ASLEEP) { sc->gkb_flags &= ~KB_ASLEEP; wakeup(sc); } selwakeuppri(&sc->gkb_rsel, PZERO); return (0); default: return (EINVAL); } /* obtain the current key input mode */ if (kbdd_ioctl(kbd, KDGKBMODE, (caddr_t)&mode)) mode = K_XLATE; /* read all pending input */ while (kbdd_check_char(kbd)) { c = kbdd_read_char(kbd, FALSE); if (c == NOKEY) continue; if (c == ERRKEY) /* XXX: ring bell? */ continue; if (!KBD_IS_BUSY(kbd)) /* the device is not open, discard the input */ continue; /* store the byte as is for K_RAW and K_CODE modes */ if (mode != K_XLATE) { genkbd_putc(sc, KEYCHAR(c)); continue; } /* K_XLATE */ if (c & RELKEY) /* key release is ignored */ continue; /* process special keys; most of them are just ignored... */ if (c & SPCLKEY) { switch (KEYCHAR(c)) { default: /* ignore them... */ continue; case BTAB: /* a backtab: ESC [ Z */ genkbd_putc(sc, 0x1b); genkbd_putc(sc, '['); genkbd_putc(sc, 'Z'); continue; } } /* normal chars, normal chars with the META, function keys */ switch (KEYFLAGS(c)) { case 0: /* a normal char */ genkbd_putc(sc, KEYCHAR(c)); break; case MKEY: /* the META flag: prepend ESC */ genkbd_putc(sc, 0x1b); genkbd_putc(sc, KEYCHAR(c)); break; case FKEY | SPCLKEY: /* a function key, return string */ cp = kbdd_get_fkeystr(kbd, KEYCHAR(c), &len); if (cp != NULL) { while (len-- > 0) genkbd_putc(sc, *cp++); } break; } } /* wake up sleeping/polling processes */ if (sc->gkb_q_length > 0) { if (sc->gkb_flags & KB_ASLEEP) { sc->gkb_flags &= ~KB_ASLEEP; wakeup(sc); } selwakeuppri(&sc->gkb_rsel, PZERO); } return (0); } #endif /* KBD_INSTALL_CDEV */ /* * Generic low-level keyboard functions * The low-level functions in the keyboard subdriver may use these * functions. */ #ifndef KBD_DISABLE_KEYMAP_LOAD static int key_change_ok(struct keyent_t *, struct keyent_t *, struct thread *); static int keymap_change_ok(keymap_t *, keymap_t *, struct thread *); static int accent_change_ok(accentmap_t *, accentmap_t *, struct thread *); static int fkey_change_ok(fkeytab_t *, fkeyarg_t *, struct thread *); #endif int genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg) { +#ifndef KBD_DISABLE_KEYMAP_LOAD + keymap_t *mapp; +#endif keyarg_t *keyp; fkeyarg_t *fkeyp; int s; int i; -#ifndef KBD_DISABLE_KEYMAP_LOAD int error; -#endif s = spltty(); switch (cmd) { case KDGKBINFO: /* get keyboard information */ ((keyboard_info_t *)arg)->kb_index = kbd->kb_index; i = imin(strlen(kbd->kb_name) + 1, sizeof(((keyboard_info_t *)arg)->kb_name)); bcopy(kbd->kb_name, ((keyboard_info_t *)arg)->kb_name, i); ((keyboard_info_t *)arg)->kb_unit = kbd->kb_unit; ((keyboard_info_t *)arg)->kb_type = kbd->kb_type; ((keyboard_info_t *)arg)->kb_config = kbd->kb_config; ((keyboard_info_t *)arg)->kb_flags = kbd->kb_flags; break; case KDGKBTYPE: /* get keyboard type */ *(int *)arg = kbd->kb_type; break; case KDGETREPEAT: /* get keyboard repeat rate */ ((int *)arg)[0] = kbd->kb_delay1; ((int *)arg)[1] = kbd->kb_delay2; break; case GIO_KEYMAP: /* get keyboard translation table */ - bcopy(kbd->kb_keymap, arg, sizeof(*kbd->kb_keymap)); - break; + error = copyout(kbd->kb_keymap, *(void **)arg, + sizeof(keymap_t)); + splx(s); + return (error); case PIO_KEYMAP: /* set keyboard translation table */ #ifndef KBD_DISABLE_KEYMAP_LOAD - error = keymap_change_ok(kbd->kb_keymap, (keymap_t *)arg, - curthread); + mapp = malloc(sizeof *mapp, M_TEMP, M_NOWAIT); + error = copyin(*(void **)arg, mapp, sizeof *mapp); if (error != 0) { splx(s); + free(mapp, M_TEMP); return (error); } + + error = keymap_change_ok(kbd->kb_keymap, mapp, curthread); + if (error != 0) { + splx(s); + free(mapp, M_TEMP); + return (error); + } bzero(kbd->kb_accentmap, sizeof(*kbd->kb_accentmap)); - bcopy(arg, kbd->kb_keymap, sizeof(*kbd->kb_keymap)); + bcopy(mapp, kbd->kb_keymap, sizeof(*kbd->kb_keymap)); + free(mapp, M_TEMP); break; #else splx(s); return (ENODEV); #endif case GIO_KEYMAPENT: /* get keyboard translation table entry */ keyp = (keyarg_t *)arg; if (keyp->keynum >= sizeof(kbd->kb_keymap->key) / sizeof(kbd->kb_keymap->key[0])) { splx(s); return (EINVAL); } bcopy(&kbd->kb_keymap->key[keyp->keynum], &keyp->key, sizeof(keyp->key)); break; case PIO_KEYMAPENT: /* set keyboard translation table entry */ #ifndef KBD_DISABLE_KEYMAP_LOAD keyp = (keyarg_t *)arg; if (keyp->keynum >= sizeof(kbd->kb_keymap->key) / sizeof(kbd->kb_keymap->key[0])) { splx(s); return (EINVAL); } error = key_change_ok(&kbd->kb_keymap->key[keyp->keynum], &keyp->key, curthread); if (error != 0) { splx(s); return (error); } bcopy(&keyp->key, &kbd->kb_keymap->key[keyp->keynum], sizeof(keyp->key)); break; #else splx(s); return (ENODEV); #endif case GIO_DEADKEYMAP: /* get accent key translation table */ bcopy(kbd->kb_accentmap, arg, sizeof(*kbd->kb_accentmap)); break; case PIO_DEADKEYMAP: /* set accent key translation table */ #ifndef KBD_DISABLE_KEYMAP_LOAD error = accent_change_ok(kbd->kb_accentmap, (accentmap_t *)arg, curthread); if (error != 0) { splx(s); return (error); } bcopy(arg, kbd->kb_accentmap, sizeof(*kbd->kb_accentmap)); break; #else splx(s); return (ENODEV); #endif case GETFKEY: /* get functionkey string */ fkeyp = (fkeyarg_t *)arg; if (fkeyp->keynum >= kbd->kb_fkeytab_size) { splx(s); return (EINVAL); } bcopy(kbd->kb_fkeytab[fkeyp->keynum].str, fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].len); fkeyp->flen = kbd->kb_fkeytab[fkeyp->keynum].len; break; case SETFKEY: /* set functionkey string */ #ifndef KBD_DISABLE_KEYMAP_LOAD fkeyp = (fkeyarg_t *)arg; if (fkeyp->keynum >= kbd->kb_fkeytab_size) { splx(s); return (EINVAL); } error = fkey_change_ok(&kbd->kb_fkeytab[fkeyp->keynum], fkeyp, curthread); if (error != 0) { splx(s); return (error); } kbd->kb_fkeytab[fkeyp->keynum].len = imin(fkeyp->flen, MAXFK); bcopy(fkeyp->keydef, kbd->kb_fkeytab[fkeyp->keynum].str, kbd->kb_fkeytab[fkeyp->keynum].len); break; #else splx(s); return (ENODEV); #endif default: splx(s); return (ENOIOCTL); } splx(s); return (0); } #ifndef KBD_DISABLE_KEYMAP_LOAD #define RESTRICTED_KEY(key, i) \ ((key->spcl & (0x80 >> i)) && \ (key->map[i] == RBT || key->map[i] == SUSP || \ key->map[i] == STBY || key->map[i] == DBG || \ key->map[i] == PNC || key->map[i] == HALT || \ key->map[i] == PDWN)) static int key_change_ok(struct keyent_t *oldkey, struct keyent_t *newkey, struct thread *td) { int i; /* Low keymap_restrict_change means any changes are OK. */ if (keymap_restrict_change <= 0) return (0); /* High keymap_restrict_change means only root can change the keymap. */ if (keymap_restrict_change >= 2) { for (i = 0; i < NUM_STATES; i++) if (oldkey->map[i] != newkey->map[i]) return priv_check(td, PRIV_KEYBOARD); if (oldkey->spcl != newkey->spcl) return priv_check(td, PRIV_KEYBOARD); if (oldkey->flgs != newkey->flgs) return priv_check(td, PRIV_KEYBOARD); return (0); } /* Otherwise we have to see if any special keys are being changed. */ for (i = 0; i < NUM_STATES; i++) { /* * If either the oldkey or the newkey action is restricted * then we must make sure that the action doesn't change. */ if (!RESTRICTED_KEY(oldkey, i) && !RESTRICTED_KEY(newkey, i)) continue; if ((oldkey->spcl & (0x80 >> i)) == (newkey->spcl & (0x80 >> i)) && oldkey->map[i] == newkey->map[i]) continue; return priv_check(td, PRIV_KEYBOARD); } return (0); } static int keymap_change_ok(keymap_t *oldmap, keymap_t *newmap, struct thread *td) { int keycode, error; for (keycode = 0; keycode < NUM_KEYS; keycode++) { if ((error = key_change_ok(&oldmap->key[keycode], &newmap->key[keycode], td)) != 0) return (error); } return (0); } static int accent_change_ok(accentmap_t *oldmap, accentmap_t *newmap, struct thread *td) { struct acc_t *oldacc, *newacc; int accent, i; if (keymap_restrict_change <= 2) return (0); if (oldmap->n_accs != newmap->n_accs) return priv_check(td, PRIV_KEYBOARD); for (accent = 0; accent < oldmap->n_accs; accent++) { oldacc = &oldmap->acc[accent]; newacc = &newmap->acc[accent]; if (oldacc->accchar != newacc->accchar) return priv_check(td, PRIV_KEYBOARD); for (i = 0; i < NUM_ACCENTCHARS; ++i) { if (oldacc->map[i][0] != newacc->map[i][0]) return priv_check(td, PRIV_KEYBOARD); if (oldacc->map[i][0] == 0) /* end of table */ break; if (oldacc->map[i][1] != newacc->map[i][1]) return priv_check(td, PRIV_KEYBOARD); } } return (0); } static int fkey_change_ok(fkeytab_t *oldkey, fkeyarg_t *newkey, struct thread *td) { if (keymap_restrict_change <= 3) return (0); if (oldkey->len != newkey->flen || bcmp(oldkey->str, newkey->keydef, oldkey->len) != 0) return priv_check(td, PRIV_KEYBOARD); return (0); } #endif /* get a pointer to the string associated with the given function key */ u_char *genkbd_get_fkeystr(keyboard_t *kbd, int fkey, size_t *len) { if (kbd == NULL) return (NULL); fkey -= F_FN; if (fkey > kbd->kb_fkeytab_size) return (NULL); *len = kbd->kb_fkeytab[fkey].len; return (kbd->kb_fkeytab[fkey].str); } /* diagnostic dump */ static char *get_kbd_type_name(int type) { static struct { int type; char *name; } name_table[] = { { KB_84, "AT 84" }, { KB_101, "AT 101/102" }, { KB_OTHER, "generic" }, }; int i; for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) { if (type == name_table[i].type) return (name_table[i].name); } return ("unknown"); } void genkbd_diag(keyboard_t *kbd, int level) { if (level > 0) { printf("kbd%d: %s%d, %s (%d), config:0x%x, flags:0x%x", kbd->kb_index, kbd->kb_name, kbd->kb_unit, get_kbd_type_name(kbd->kb_type), kbd->kb_type, kbd->kb_config, kbd->kb_flags); if (kbd->kb_io_base > 0) printf(", port:0x%x-0x%x", kbd->kb_io_base, kbd->kb_io_base + kbd->kb_io_size - 1); printf("\n"); } } #define set_lockkey_state(k, s, l) \ if (!((s) & l ## DOWN)) { \ int i; \ (s) |= l ## DOWN; \ (s) ^= l ## ED; \ i = (s) & LOCK_MASK; \ kbdd_ioctl((k), KDSETLED, (caddr_t)&i); \ } static u_int save_accent_key(keyboard_t *kbd, u_int key, int *accents) { int i; /* make an index into the accent map */ i = key - F_ACC + 1; if ((i > kbd->kb_accentmap->n_accs) || (kbd->kb_accentmap->acc[i - 1].accchar == 0)) { /* the index is out of range or pointing to an empty entry */ *accents = 0; return (ERRKEY); } /* * If the same accent key has been hit twice, produce the accent * char itself. */ if (i == *accents) { key = kbd->kb_accentmap->acc[i - 1].accchar; *accents = 0; return (key); } /* remember the index and wait for the next key */ *accents = i; return (NOKEY); } static u_int make_accent_char(keyboard_t *kbd, u_int ch, int *accents) { struct acc_t *acc; int i; acc = &kbd->kb_accentmap->acc[*accents - 1]; *accents = 0; /* * If the accent key is followed by the space key, * produce the accent char itself. */ if (ch == ' ') return (acc->accchar); /* scan the accent map */ for (i = 0; i < NUM_ACCENTCHARS; ++i) { if (acc->map[i][0] == 0) /* end of table */ break; if (acc->map[i][0] == ch) return (acc->map[i][1]); } /* this char cannot be accented... */ return (ERRKEY); } int genkbd_keyaction(keyboard_t *kbd, int keycode, int up, int *shiftstate, int *accents) { struct keyent_t *key; int state = *shiftstate; int action; int f; int i; i = keycode; f = state & (AGRS | ALKED); if ((f == AGRS1) || (f == AGRS2) || (f == ALKED)) i += ALTGR_OFFSET; key = &kbd->kb_keymap->key[i]; i = ((state & SHIFTS) ? 1 : 0) | ((state & CTLS) ? 2 : 0) | ((state & ALTS) ? 4 : 0); if (((key->flgs & FLAG_LOCK_C) && (state & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (state & NLKED)) ) i ^= 1; if (up) { /* break: key released */ action = kbd->kb_lastact[keycode]; kbd->kb_lastact[keycode] = NOP; switch (action) { case LSHA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = LSH; /* FALL THROUGH */ case LSH: state &= ~SHIFTS1; break; case RSHA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = RSH; /* FALL THROUGH */ case RSH: state &= ~SHIFTS2; break; case LCTRA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = LCTR; /* FALL THROUGH */ case LCTR: state &= ~CTLS1; break; case RCTRA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = RCTR; /* FALL THROUGH */ case RCTR: state &= ~CTLS2; break; case LALTA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = LALT; /* FALL THROUGH */ case LALT: state &= ~ALTS1; break; case RALTA: if (state & SHIFTAON) { set_lockkey_state(kbd, state, ALK); state &= ~ALKDOWN; } action = RALT; /* FALL THROUGH */ case RALT: state &= ~ALTS2; break; case ASH: state &= ~AGRS1; break; case META: state &= ~METAS1; break; case NLK: state &= ~NLKDOWN; break; case CLK: #ifndef PC98 state &= ~CLKDOWN; #else state &= ~CLKED; i = state & LOCK_MASK; kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i); #endif break; case SLK: state &= ~SLKDOWN; break; case ALK: state &= ~ALKDOWN; break; case NOP: /* release events of regular keys are not reported */ *shiftstate &= ~SHIFTAON; return (NOKEY); } *shiftstate = state & ~SHIFTAON; return (SPCLKEY | RELKEY | action); } else { /* make: key pressed */ action = key->map[i]; state &= ~SHIFTAON; if (key->spcl & (0x80 >> i)) { /* special keys */ if (kbd->kb_lastact[keycode] == NOP) kbd->kb_lastact[keycode] = action; if (kbd->kb_lastact[keycode] != action) action = NOP; switch (action) { /* LOCKING KEYS */ case NLK: set_lockkey_state(kbd, state, NLK); break; case CLK: #ifndef PC98 set_lockkey_state(kbd, state, CLK); #else state |= CLKED; i = state & LOCK_MASK; kbdd_ioctl(kbd, KDSETLED, (caddr_t)&i); #endif break; case SLK: set_lockkey_state(kbd, state, SLK); break; case ALK: set_lockkey_state(kbd, state, ALK); break; /* NON-LOCKING KEYS */ case SPSC: case RBT: case SUSP: case STBY: case DBG: case NEXT: case PREV: case PNC: case HALT: case PDWN: *accents = 0; break; case BTAB: *accents = 0; action |= BKEY; break; case LSHA: state |= SHIFTAON; action = LSH; /* FALL THROUGH */ case LSH: state |= SHIFTS1; break; case RSHA: state |= SHIFTAON; action = RSH; /* FALL THROUGH */ case RSH: state |= SHIFTS2; break; case LCTRA: state |= SHIFTAON; action = LCTR; /* FALL THROUGH */ case LCTR: state |= CTLS1; break; case RCTRA: state |= SHIFTAON; action = RCTR; /* FALL THROUGH */ case RCTR: state |= CTLS2; break; case LALTA: state |= SHIFTAON; action = LALT; /* FALL THROUGH */ case LALT: state |= ALTS1; break; case RALTA: state |= SHIFTAON; action = RALT; /* FALL THROUGH */ case RALT: state |= ALTS2; break; case ASH: state |= AGRS1; break; case META: state |= METAS1; break; case NOP: *shiftstate = state; return (NOKEY); default: /* is this an accent (dead) key? */ *shiftstate = state; if (action >= F_ACC && action <= L_ACC) { action = save_accent_key(kbd, action, accents); switch (action) { case NOKEY: case ERRKEY: return (action); default: if (state & METAS) return (action | MKEY); else return (action); } /* NOT REACHED */ } /* other special keys */ if (*accents > 0) { *accents = 0; return (ERRKEY); } if (action >= F_FN && action <= L_FN) action |= FKEY; /* XXX: return fkey string for the FKEY? */ return (SPCLKEY | action); } *shiftstate = state; return (SPCLKEY | action); } else { /* regular keys */ kbd->kb_lastact[keycode] = NOP; *shiftstate = state; if (*accents > 0) { /* make an accented char */ action = make_accent_char(kbd, action, accents); if (action == ERRKEY) return (action); } if (state & METAS) action |= MKEY; return (action); } } /* NOT REACHED */ } Index: head/sys/sys/kbio.h =================================================================== --- head/sys/sys/kbio.h (revision 197329) +++ head/sys/sys/kbio.h (revision 197330) @@ -1,250 +1,251 @@ /*- * $FreeBSD$ */ #ifndef _SYS_KBIO_H_ #define _SYS_KBIO_H_ #ifndef _KERNEL #include #endif #include /* get/set keyboard I/O mode */ #define K_RAW 0 /* keyboard returns scancodes */ #define K_XLATE 1 /* keyboard returns ascii */ #define K_CODE 2 /* keyboard returns keycodes */ #define KDGKBMODE _IOR('K', 6, int) #define KDSKBMODE _IOWINT('K', 7) /* make tone */ #define KDMKTONE _IOWINT('K', 8) /* see console.h for the definitions of the following ioctls */ #ifdef notdef #define KDGETMODE _IOR('K', 9, int) #define KDSETMODE _IOWINT('K', 10) #define KDSBORDER _IOWINT('K', 13) #endif /* get/set keyboard lock state */ #define CLKED 1 /* Caps locked */ #define NLKED 2 /* Num locked */ #define SLKED 4 /* Scroll locked */ #define ALKED 8 /* AltGr locked */ #define LOCK_MASK (CLKED | NLKED | SLKED | ALKED) #define KDGKBSTATE _IOR('K', 19, int) #define KDSKBSTATE _IOWINT('K', 20) /* enable/disable I/O access */ #define KDENABIO _IO('K', 60) #define KDDISABIO _IO('K', 61) /* make sound */ #define KIOCSOUND _IOWINT('K', 63) /* get keyboard model */ #define KB_OTHER 0 /* keyboard not known */ #define KB_84 1 /* 'old' 84 key AT-keyboard */ #define KB_101 2 /* MF-101 or MF-102 keyboard */ #define KDGKBTYPE _IOR('K', 64, int) /* get/set keyboard LED state */ #define LED_CAP 1 /* Caps lock LED */ #define LED_NUM 2 /* Num lock LED */ #define LED_SCR 4 /* Scroll lock LED */ #define LED_MASK (LED_CAP | LED_NUM | LED_SCR) #define KDGETLED _IOR('K', 65, int) #define KDSETLED _IOWINT('K', 66) /* set keyboard repeat rate (obsolete, use KDSETREPEAT below) */ #define KDSETRAD _IOWINT('K', 67) struct keyboard_info { int kb_index; /* kbdio index# */ char kb_name[16]; /* driver name */ int kb_unit; /* unit# */ int kb_type; /* KB_84, KB_101, KB_OTHER,... */ int kb_config; /* device configuration flags */ int kb_flags; /* internal flags */ }; typedef struct keyboard_info keyboard_info_t; /* add/remove keyboard to/from mux */ #define KBADDKBD _IOW('K', 68, keyboard_info_t) /* add keyboard */ #define KBRELKBD _IOW('K', 69, keyboard_info_t) /* release keyboard */ /* see console.h for the definition of the following ioctl */ #ifdef notdef #define KDRASTER _IOW('K', 100, scr_size_t) #endif /* get keyboard information */ #define KDGKBINFO _IOR('K', 101, keyboard_info_t) /* set/get keyboard repeat rate (new interface) */ struct keyboard_repeat { int kb_repeat[2]; }; typedef struct keyboard_repeat keyboard_repeat_t; #define KDSETREPEAT _IOW('K', 102, keyboard_repeat_t) #define KDGETREPEAT _IOR('K', 103, keyboard_repeat_t) /* get/set key map/accent map/function key strings */ #define NUM_KEYS 256 /* number of keys in table */ #define NUM_STATES 8 /* states per key */ #define ALTGR_OFFSET 128 /* offset for altlock keys */ #define NUM_DEADKEYS 15 /* number of accent keys */ #define NUM_ACCENTCHARS 52 /* max number of accent chars */ #define NUM_FKEYS 96 /* max number of function keys */ #define MAXFK 16 /* max length of a function key str */ #ifndef _KEYMAP_DECLARED #define _KEYMAP_DECLARED struct keyent_t { - u_char map[NUM_STATES]; + u_int map[NUM_STATES]; u_char spcl; u_char flgs; #define FLAG_LOCK_O 0 #define FLAG_LOCK_C 1 #define FLAG_LOCK_N 2 }; struct keymap { u_short n_keys; struct keyent_t key[NUM_KEYS]; }; typedef struct keymap keymap_t; #endif /* !_KEYMAP_DECLARED */ /* defines for "special" keys (spcl bit set in keymap) */ #define NOP 0x00 /* nothing (dead key) */ #define LSH 0x02 /* left shift key */ #define RSH 0x03 /* right shift key */ #define CLK 0x04 /* caps lock key */ #define NLK 0x05 /* num lock key */ #define SLK 0x06 /* scroll lock key */ #define LALT 0x07 /* left alt key */ #define BTAB 0x08 /* backwards tab */ #define LCTR 0x09 /* left control key */ #define NEXT 0x0a /* switch to next screen */ #define F_SCR 0x0b /* switch to first screen */ #define L_SCR 0x1a /* switch to last screen */ #define F_FN 0x1b /* first function key */ #define L_FN 0x7a /* last function key */ /* 0x7b-0x7f reserved do not use ! */ #define RCTR 0x80 /* right control key */ #define RALT 0x81 /* right alt (altgr) key */ #define ALK 0x82 /* alt lock key */ #define ASH 0x83 /* alt shift key */ #define META 0x84 /* meta key */ #define RBT 0x85 /* boot machine */ #define DBG 0x86 /* call debugger */ #define SUSP 0x87 /* suspend power (APM) */ #define SPSC 0x88 /* toggle splash/text screen */ #define F_ACC DGRA /* first accent key */ #define DGRA 0x89 /* grave */ #define DACU 0x8a /* acute */ #define DCIR 0x8b /* circumflex */ #define DTIL 0x8c /* tilde */ #define DMAC 0x8d /* macron */ #define DBRE 0x8e /* breve */ #define DDOT 0x8f /* dot */ #define DUML 0x90 /* umlaut/diaresis */ #define DDIA 0x90 /* diaresis */ #define DSLA 0x91 /* slash */ #define DRIN 0x92 /* ring */ #define DCED 0x93 /* cedilla */ #define DAPO 0x94 /* apostrophe */ #define DDAC 0x95 /* double acute */ #define DOGO 0x96 /* ogonek */ #define DCAR 0x97 /* caron */ #define L_ACC DCAR /* last accent key */ #define STBY 0x98 /* Go into standby mode (apm) */ #define PREV 0x99 /* switch to previous screen */ #define PNC 0x9a /* force system panic */ #define LSHA 0x9b /* left shift key / alt lock */ #define RSHA 0x9c /* right shift key / alt lock */ #define LCTRA 0x9d /* left ctrl key / alt lock */ #define RCTRA 0x9e /* right ctrl key / alt lock */ #define LALTA 0x9f /* left alt key / alt lock */ #define RALTA 0xa0 /* right alt key / alt lock */ #define HALT 0xa1 /* halt machine */ #define PDWN 0xa2 /* halt machine and power down */ #define PASTE 0xa3 /* paste from cut-paste buffer */ #define F(x) ((x)+F_FN-1) #define S(x) ((x)+F_SCR-1) #define ACC(x) ((x)+F_ACC) struct acc_t { u_char accchar; u_char map[NUM_ACCENTCHARS][2]; }; struct accentmap { u_short n_accs; struct acc_t acc[NUM_DEADKEYS]; }; typedef struct accentmap accentmap_t; struct keyarg { u_short keynum; struct keyent_t key; }; typedef struct keyarg keyarg_t; struct fkeytab { u_char str[MAXFK]; u_char len; }; typedef struct fkeytab fkeytab_t; struct fkeyarg { u_short keynum; char keydef[MAXFK]; char flen; }; typedef struct fkeyarg fkeyarg_t; #define GETFKEY _IOWR('k', 0, fkeyarg_t) #define SETFKEY _IOWR('k', 1, fkeyarg_t) #ifdef notdef /* see console.h */ #define GIO_SCRNMAP _IOR('k', 2, scrmap_t) #define PIO_SCRNMAP _IOW('k', 3, scrmap_t) #endif -#define GIO_KEYMAP _IOR('k', 6, keymap_t) -#define PIO_KEYMAP _IOW('k', 7, keymap_t) +/* XXX: Should have keymap_t as an argument, but that's too big for ioctl()! */ +#define GIO_KEYMAP _IO('k', 6) +#define PIO_KEYMAP _IO('k', 7) #define GIO_DEADKEYMAP _IOR('k', 8, accentmap_t) #define PIO_DEADKEYMAP _IOW('k', 9, accentmap_t) #define GIO_KEYMAPENT _IOWR('k', 10, keyarg_t) #define PIO_KEYMAPENT _IOW('k', 11, keyarg_t) /* flags set to the return value in the KD_XLATE mode */ #define NOKEY 0x01000000 /* no key pressed marker */ #define FKEY 0x02000000 /* function key marker */ #define MKEY 0x04000000 /* meta key marker (prepend ESC)*/ #define BKEY 0x08000000 /* backtab (ESC [ Z) */ #define SPCLKEY 0x80000000 /* special key */ #define RELKEY 0x40000000 /* key released */ #define ERRKEY 0x20000000 /* error */ /* * The top byte is used to store the flags. This means there are 24 * bits left to store the actual character. Because UTF-8 can encode * 2^21 different characters, this is good enough to get Unicode * working. */ #define KEYCHAR(c) ((c) & 0x00ffffff) #define KEYFLAGS(c) ((c) & ~0x00ffffff) #endif /* !_SYS_KBIO_H_ */ Index: head/usr.sbin/kbdcontrol/kbdcontrol.c =================================================================== --- head/usr.sbin/kbdcontrol/kbdcontrol.c (revision 197329) +++ head/usr.sbin/kbdcontrol/kbdcontrol.c (revision 197330) @@ -1,1212 +1,1212 @@ /*- * Copyright (c) 1994-1995 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "path.h" #include "lex.h" /* * HALT, PDWN, and PASTE aren't defined in 4.x, but we need them to bridge * to 5.0-current so define them here as a stop gap transition measure. */ #ifndef HALT #define HALT 0xa1 /* halt machine */ #endif #ifndef PDWN #define PDWN 0xa2 /* halt machine and power down */ #endif #ifndef PASTE #define PASTE 0xa3 /* paste from cut-paste buffer */ #endif #define SPECIAL 0x80000000 char ctrl_names[32][4] = { "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs ", "ht ", "nl ", "vt ", "ff ", "cr ", "so ", "si ", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em ", "sub", "esc", "fs ", "gs ", "rs ", "us " }; char acc_names[15][5] = { "dgra", "dacu", "dcir", "dtil", "dmac", "dbre", "ddot", "duml", "dsla", "drin", "dced", "dapo", "ddac", "dogo", "dcar", }; char acc_names_u[15][5] = { "DGRA", "DACU", "DCIR", "DTIL", "DMAC", "DBRE", "DDOT", "DUML", "DSLA", "DRIN", "DCED", "DAPO", "DDAC", "DOGO", "DCAR", }; char fkey_table[96][MAXFK] = { /* 01-04 */ "\033[M", "\033[N", "\033[O", "\033[P", /* 05-08 */ "\033[Q", "\033[R", "\033[S", "\033[T", /* 09-12 */ "\033[U", "\033[V", "\033[W", "\033[X", /* 13-16 */ "\033[Y", "\033[Z", "\033[a", "\033[b", /* 17-20 */ "\033[c", "\033[d", "\033[e", "\033[f", /* 21-24 */ "\033[g", "\033[h", "\033[i", "\033[j", /* 25-28 */ "\033[k", "\033[l", "\033[m", "\033[n", /* 29-32 */ "\033[o", "\033[p", "\033[q", "\033[r", /* 33-36 */ "\033[s", "\033[t", "\033[u", "\033[v", /* 37-40 */ "\033[w", "\033[x", "\033[y", "\033[z", /* 41-44 */ "\033[@", "\033[[", "\033[\\","\033[]", /* 45-48 */ "\033[^", "\033[_", "\033[`", "\033[{", /* 49-52 */ "\033[H", "\033[A", "\033[I", "-" , /* 53-56 */ "\033[D", "\033[E", "\033[C", "+" , /* 57-60 */ "\033[F", "\033[B", "\033[G", "\033[L", /* 61-64 */ "\177", "\033[J", "\033[~", "\033[}", /* 65-68 */ "" , "" , "" , "" , /* 69-72 */ "" , "" , "" , "" , /* 73-76 */ "" , "" , "" , "" , /* 77-80 */ "" , "" , "" , "" , /* 81-84 */ "" , "" , "" , "" , /* 85-88 */ "" , "" , "" , "" , /* 89-92 */ "" , "" , "" , "" , /* 93-96 */ "" , "" , "" , "" , }; const int delays[] = {250, 500, 750, 1000}; const int repeats[] = { 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}; const int ndelays = (sizeof(delays) / sizeof(int)); const int nrepeats = (sizeof(repeats) / sizeof(int)); int hex = 0; int number; char letter; int token; void dump_accent_definition(char *name, accentmap_t *accentmap); void dump_entry(int value); void dump_key_definition(char *name, keymap_t *keymap); int get_accent_definition_line(accentmap_t *); int get_entry(void); int get_key_definition_line(keymap_t *); void load_keymap(char *opt, int dumponly); void load_default_functionkeys(void); char * nextarg(int ac, char **av, int *indp, int oc); char * mkfullname(const char *s1, const char *s2, const char *s3); void print_accent_definition_line(FILE *fp, int accent, struct acc_t *key); void print_entry(FILE *fp, int value); void print_key_definition_line(FILE *fp, int scancode, struct keyent_t *key); void print_keymap(void); void release_keyboard(void); void mux_keyboard(u_int op, char *kbd); void set_bell_values(char *opt); void set_functionkey(char *keynumstr, char *string); void set_keyboard(char *device); void set_keyrates(char *opt); void show_kbd_info(void); void usage(void) __dead2; char * nextarg(int ac, char **av, int *indp, int oc) { if (*indp < ac) return(av[(*indp)++]); warnx("option requires two arguments -- %c", oc); usage(); } char * mkfullname(const char *s1, const char *s2, const char *s3) { static char *buf = NULL; static int bufl = 0; int f; f = strlen(s1) + strlen(s2) + strlen(s3) + 1; if (f > bufl) { if (buf) buf = (char *)realloc(buf, f); else buf = (char *)malloc(f); } if (!buf) { bufl = 0; return(NULL); } bufl = f; strcpy(buf, s1); strcat(buf, s2); strcat(buf, s3); return(buf); } int get_entry(void) { switch ((token = yylex())) { case TNOP: return NOP | SPECIAL; case TLSH: return LSH | SPECIAL; case TRSH: return RSH | SPECIAL; case TCLK: return CLK | SPECIAL; case TNLK: return NLK | SPECIAL; case TSLK: return SLK | SPECIAL; case TBTAB: return BTAB | SPECIAL; case TLALT: return LALT | SPECIAL; case TLCTR: return LCTR | SPECIAL; case TNEXT: return NEXT | SPECIAL; case TPREV: return PREV | SPECIAL; case TRCTR: return RCTR | SPECIAL; case TRALT: return RALT | SPECIAL; case TALK: return ALK | SPECIAL; case TASH: return ASH | SPECIAL; case TMETA: return META | SPECIAL; case TRBT: return RBT | SPECIAL; case TDBG: return DBG | SPECIAL; case TSUSP: return SUSP | SPECIAL; case TSPSC: return SPSC | SPECIAL; case TPANIC: return PNC | SPECIAL; case TLSHA: return LSHA | SPECIAL; case TRSHA: return RSHA | SPECIAL; case TLCTRA: return LCTRA | SPECIAL; case TRCTRA: return RCTRA | SPECIAL; case TLALTA: return LALTA | SPECIAL; case TRALTA: return RALTA | SPECIAL; case THALT: return HALT | SPECIAL; case TPDWN: return PDWN | SPECIAL; case TPASTE: return PASTE | SPECIAL; case TACC: if (ACC(number) > L_ACC) return -1; return ACC(number) | SPECIAL; case TFUNC: if (F(number) > L_FN) return -1; return F(number) | SPECIAL; case TSCRN: if (S(number) > L_SCR) return -1; return S(number) | SPECIAL; case TLET: return (unsigned char)letter; case TNUM: - if (number < 0 || number > 255) + if (number < 0x000000 || number > 0x10FFFF) return -1; return number; default: return -1; } } static int get_definition_line(FILE *fd, keymap_t *keymap, accentmap_t *accentmap) { int c; yyin = fd; if (token < 0) token = yylex(); switch (token) { case TNUM: c = get_key_definition_line(keymap); if (c < 0) errx(1, "invalid key definition"); if (c > keymap->n_keys) keymap->n_keys = c; break; case TACC: c = get_accent_definition_line(accentmap); if (c < 0) errx(1, "invalid accent key definition"); if (c > accentmap->n_accs) accentmap->n_accs = c; break; case 0: /* EOF */ return -1; default: errx(1, "illegal definition line"); } return c; } int get_key_definition_line(keymap_t *map) { int i, def, scancode; /* check scancode number */ if (number < 0 || number >= NUM_KEYS) return -1; scancode = number; /* get key definitions */ map->key[scancode].spcl = 0; for (i=0; ikey[scancode].spcl |= (0x80 >> i); map->key[scancode].map[i] = def & ~SPECIAL; } /* get lock state key def */ if ((token = yylex()) != TFLAG) return -1; map->key[scancode].flgs = number; token = yylex(); return (scancode + 1); } int get_accent_definition_line(accentmap_t *map) { int accent; int c1, c2; int i; if (ACC(number) < F_ACC || ACC(number) > L_ACC) /* number out of range */ return -1; accent = number; if (map->acc[accent].accchar != 0) { /* this entry has already been defined before! */ errx(1, "duplicated accent key definition"); } switch ((token = yylex())) { case TLET: map->acc[accent].accchar = letter; break; case TNUM: map->acc[accent].accchar = number; break; default: return -1; } for (i = 0; (token = yylex()) == '(';) { switch ((token = yylex())) { case TLET: c1 = letter; break; case TNUM: c1 = number; break; default: return -1; } switch ((token = yylex())) { case TLET: c2 = letter; break; case TNUM: c2 = number; break; default: return -1; } if ((token = yylex()) != ')') return -1; if (i >= NUM_ACCENTCHARS) { warnx("too many accented characters, ignored"); continue; } map->acc[accent].map[i][0] = c1; map->acc[accent].map[i][1] = c2; ++i; } return (accent + 1); } void print_entry(FILE *fp, int value) { int val = value & ~SPECIAL; switch (value) { case NOP | SPECIAL: fprintf(fp, " nop "); break; case LSH | SPECIAL: fprintf(fp, " lshift"); break; case RSH | SPECIAL: fprintf(fp, " rshift"); break; case CLK | SPECIAL: fprintf(fp, " clock "); break; case NLK | SPECIAL: fprintf(fp, " nlock "); break; case SLK | SPECIAL: fprintf(fp, " slock "); break; case BTAB | SPECIAL: fprintf(fp, " btab "); break; case LALT | SPECIAL: fprintf(fp, " lalt "); break; case LCTR | SPECIAL: fprintf(fp, " lctrl "); break; case NEXT | SPECIAL: fprintf(fp, " nscr "); break; case PREV | SPECIAL: fprintf(fp, " pscr "); break; case RCTR | SPECIAL: fprintf(fp, " rctrl "); break; case RALT | SPECIAL: fprintf(fp, " ralt "); break; case ALK | SPECIAL: fprintf(fp, " alock "); break; case ASH | SPECIAL: fprintf(fp, " ashift"); break; case META | SPECIAL: fprintf(fp, " meta "); break; case RBT | SPECIAL: fprintf(fp, " boot "); break; case DBG | SPECIAL: fprintf(fp, " debug "); break; case SUSP | SPECIAL: fprintf(fp, " susp "); break; case SPSC | SPECIAL: fprintf(fp, " saver "); break; case PNC | SPECIAL: fprintf(fp, " panic "); break; case LSHA | SPECIAL: fprintf(fp, " lshifta"); break; case RSHA | SPECIAL: fprintf(fp, " rshifta"); break; case LCTRA | SPECIAL: fprintf(fp, " lctrla"); break; case RCTRA | SPECIAL: fprintf(fp, " rctrla"); break; case LALTA | SPECIAL: fprintf(fp, " lalta "); break; case RALTA | SPECIAL: fprintf(fp, " ralta "); break; case HALT | SPECIAL: fprintf(fp, " halt "); break; case PDWN | SPECIAL: fprintf(fp, " pdwn "); break; case PASTE | SPECIAL: fprintf(fp, " paste "); break; default: if (value & SPECIAL) { if (val >= F_FN && val <= L_FN) fprintf(fp, " fkey%02d", val - F_FN + 1); else if (val >= F_SCR && val <= L_SCR) fprintf(fp, " scr%02d ", val - F_SCR + 1); else if (val >= F_ACC && val <= L_ACC) fprintf(fp, " %-6s", acc_names[val - F_ACC]); else if (hex) fprintf(fp, " 0x%02x ", val); else fprintf(fp, " %3d ", val); } else { if (val < ' ') fprintf(fp, " %s ", ctrl_names[val]); else if (val == 127) fprintf(fp, " del "); else if (isascii(val) && isprint(val)) fprintf(fp, " '%c' ", val); else if (hex) fprintf(fp, " 0x%02x ", val); else fprintf(fp, " %3d ", val); } } } void print_key_definition_line(FILE *fp, int scancode, struct keyent_t *key) { int i; /* print scancode number */ if (hex) fprintf(fp, " 0x%02x ", scancode); else fprintf(fp, " %03d ", scancode); /* print key definitions */ for (i=0; ispcl & (0x80 >> i)) print_entry(fp, key->map[i] | SPECIAL); else print_entry(fp, key->map[i]); } /* print lock state key def */ switch (key->flgs) { case 0: fprintf(fp, " O\n"); break; case 1: fprintf(fp, " C\n"); break; case 2: fprintf(fp, " N\n"); break; case 3: fprintf(fp, " B\n"); break; } } void print_accent_definition_line(FILE *fp, int accent, struct acc_t *key) { int c; int i; if (key->accchar == 0) return; /* print accent number */ fprintf(fp, " %-6s", acc_names[accent]); if (isascii(key->accchar) && isprint(key->accchar)) fprintf(fp, "'%c' ", key->accchar); else if (hex) fprintf(fp, "0x%02x ", key->accchar); else fprintf(fp, "%03d ", key->accchar); for (i = 0; i < NUM_ACCENTCHARS; ++i) { c = key->map[i][0]; if (c == 0) break; if ((i > 0) && ((i % 4) == 0)) fprintf(fp, "\n "); if (isascii(c) && isprint(c)) fprintf(fp, "( '%c' ", c); else if (hex) fprintf(fp, "(0x%02x ", c); else fprintf(fp, "( %03d ", c); c = key->map[i][1]; if (isascii(c) && isprint(c)) fprintf(fp, "'%c' ) ", c); else if (hex) fprintf(fp, "0x%02x) ", c); else fprintf(fp, "%03d ) ", c); } fprintf(fp, "\n"); } void dump_entry(int value) { if (value & SPECIAL) { value &= ~SPECIAL; switch (value) { case NOP: printf(" NOP, "); break; case LSH: printf(" LSH, "); break; case RSH: printf(" RSH, "); break; case CLK: printf(" CLK, "); break; case NLK: printf(" NLK, "); break; case SLK: printf(" SLK, "); break; case BTAB: printf(" BTAB, "); break; case LALT: printf(" LALT, "); break; case LCTR: printf(" LCTR, "); break; case NEXT: printf(" NEXT, "); break; case PREV: printf(" PREV, "); break; case RCTR: printf(" RCTR, "); break; case RALT: printf(" RALT, "); break; case ALK: printf(" ALK, "); break; case ASH: printf(" ASH, "); break; case META: printf(" META, "); break; case RBT: printf(" RBT, "); break; case DBG: printf(" DBG, "); break; case SUSP: printf(" SUSP, "); break; case SPSC: printf(" SPSC, "); break; case PNC: printf(" PNC, "); break; case LSHA: printf(" LSHA, "); break; case RSHA: printf(" RSHA, "); break; case LCTRA: printf("LCTRA, "); break; case RCTRA: printf("RCTRA, "); break; case LALTA: printf("LALTA, "); break; case RALTA: printf("RALTA, "); break; case HALT: printf(" HALT, "); break; case PDWN: printf(" PDWN, "); break; case PASTE: printf("PASTE, "); break; default: if (value >= F_FN && value <= L_FN) printf(" F(%2d),", value - F_FN + 1); else if (value >= F_SCR && value <= L_SCR) printf(" S(%2d),", value - F_SCR + 1); else if (value >= F_ACC && value <= L_ACC) printf(" %-4s, ", acc_names_u[value - F_ACC]); else printf(" 0x%02X, ", value); break; } } else if (value == '\'') { printf(" '\\'', "); } else if (value == '\\') { printf(" '\\\\', "); } else if (isascii(value) && isprint(value)) { printf(" '%c', ", value); } else { printf(" 0x%02X, ", value); } } void dump_key_definition(char *name, keymap_t *keymap) { int i, j; printf("static keymap_t keymap_%s = { 0x%02x, {\n", name, (unsigned)keymap->n_keys); printf( "/* alt\n" " * scan cntrl alt alt cntrl\n" " * code base shift cntrl shift alt shift cntrl shift spcl flgs\n" " * ---------------------------------------------------------------------------\n" " */\n"); for (i = 0; i < keymap->n_keys; i++) { printf("/*%02x*/{{", i); for (j = 0; j < NUM_STATES; j++) { if (keymap->key[i].spcl & (0x80 >> j)) dump_entry(keymap->key[i].map[j] | SPECIAL); else dump_entry(keymap->key[i].map[j]); } printf("}, 0x%02X,0x%02X },\n", (unsigned)keymap->key[i].spcl, (unsigned)keymap->key[i].flgs); } printf("} };\n\n"); } void dump_accent_definition(char *name, accentmap_t *accentmap) { int i, j; int c; printf("static accentmap_t accentmap_%s = { %d", name, accentmap->n_accs); if (accentmap->n_accs <= 0) { printf(" };\n\n"); return; } printf(", {\n"); for (i = 0; i < NUM_DEADKEYS; i++) { printf(" /* %s=%d */\n {", acc_names[i], i); c = accentmap->acc[i].accchar; if (c == '\'') printf(" '\\'', {"); else if (c == '\\') printf(" '\\\\', {"); else if (isascii(c) && isprint(c)) printf(" '%c', {", c); else if (c == 0) { printf(" 0x00 }, \n"); continue; } else printf(" 0x%02x, {", c); for (j = 0; j < NUM_ACCENTCHARS; j++) { c = accentmap->acc[i].map[j][0]; if (c == 0) break; if ((j > 0) && ((j % 4) == 0)) printf("\n\t "); if (isascii(c) && isprint(c)) printf(" { '%c',", c); else printf(" { 0x%02x,", c); printf("0x%02x },", accentmap->acc[i].map[j][1]); } printf(" }, },\n"); } printf("} };\n\n"); } void load_keymap(char *opt, int dumponly) { keymap_t keymap; accentmap_t accentmap; FILE *fd; int i, j; char *name, *cp; char blank[] = "", keymap_path[] = KEYMAP_PATH, dotkbd[] = ".kbd"; char *prefix[] = {blank, blank, keymap_path, NULL}; char *postfix[] = {blank, dotkbd, NULL}; cp = getenv("KEYMAP_PATH"); if (cp != NULL) asprintf(&(prefix[0]), "%s/", cp); fd = NULL; for (i=0; prefix[i] && fd == NULL; i++) { for (j=0; postfix[j] && fd == NULL; j++) { name = mkfullname(prefix[i], opt, postfix[j]); fd = fopen(name, "r"); } } if (fd == NULL) { warn("keymap file \"%s\" not found", opt); return; } memset(&keymap, 0, sizeof(keymap)); memset(&accentmap, 0, sizeof(accentmap)); token = -1; while (1) { if (get_definition_line(fd, &keymap, &accentmap) < 0) break; } if (dumponly) { /* fix up the filename to make it a valid C identifier */ for (cp = opt; *cp; cp++) if (!isalpha(*cp) && !isdigit(*cp)) *cp = '_'; printf("/*\n" " * Automatically generated from %s.\n" " * DO NOT EDIT!\n" " */\n", name); dump_key_definition(opt, &keymap); dump_accent_definition(opt, &accentmap); return; } if ((keymap.n_keys > 0) && (ioctl(0, PIO_KEYMAP, &keymap) < 0)) { warn("setting keymap"); fclose(fd); return; } if ((accentmap.n_accs > 0) && (ioctl(0, PIO_DEADKEYMAP, &accentmap) < 0)) { warn("setting accentmap"); fclose(fd); return; } } void print_keymap(void) { keymap_t keymap; accentmap_t accentmap; int i; if (ioctl(0, GIO_KEYMAP, &keymap) < 0) err(1, "getting keymap"); if (ioctl(0, GIO_DEADKEYMAP, &accentmap) < 0) memset(&accentmap, 0, sizeof(accentmap)); printf( "# alt\n" "# scan cntrl alt alt cntrl lock\n" "# code base shift cntrl shift alt shift cntrl shift state\n" "# ------------------------------------------------------------------\n" ); for (i=0; i NUM_FKEYS) { warnx("function key number must be between 1 and %d", NUM_FKEYS); return; } if ((fkey.flen = strlen(string)) > MAXFK) { warnx("function key string too long (%d > %d)", fkey.flen, MAXFK); return; } strncpy(fkey.keydef, string, MAXFK); fkey.keynum -= 1; if (ioctl(0, SETFKEY, &fkey) < 0) warn("setting function key"); } void set_bell_values(char *opt) { int bell, duration, pitch; bell = 0; if (!strncmp(opt, "quiet.", 6)) { bell = CONS_QUIET_BELL; opt += 6; } if (!strcmp(opt, "visual")) bell |= CONS_VISUAL_BELL; else if (!strcmp(opt, "normal")) duration = 5, pitch = 800; else if (!strcmp(opt, "off")) duration = 0, pitch = 0; else { char *v1; bell = 0; duration = strtol(opt, &v1, 0); if ((duration < 0) || (*v1 != '.')) goto badopt; opt = ++v1; pitch = strtol(opt, &v1, 0); if ((pitch < 0) || (*opt == '\0') || (*v1 != '\0')) { badopt: warnx("argument to -b must be duration.pitch or [quiet.]visual|normal|off"); return; } if (pitch != 0) pitch = 1193182 / pitch; /* in Hz */ duration /= 10; /* in 10 m sec */ } ioctl(0, CONS_BELLTYPE, &bell); if (!(bell & CONS_VISUAL_BELL)) fprintf(stderr, "[=%d;%dB", pitch, duration); } void set_keyrates(char *opt) { int arg[2]; int repeat; int delay; int r, d; if (!strcmp(opt, "slow")) { delay = 1000, repeat = 500; d = 3, r = 31; } else if (!strcmp(opt, "normal")) { delay = 500, repeat = 125; d = 1, r = 15; } else if (!strcmp(opt, "fast")) { delay = repeat = 0; d = r = 0; } else { int n; char *v1; delay = strtol(opt, &v1, 0); if ((delay < 0) || (*v1 != '.')) goto badopt; opt = ++v1; repeat = strtol(opt, &v1, 0); if ((repeat < 0) || (*opt == '\0') || (*v1 != '\0')) { badopt: warnx("argument to -r must be delay.repeat or slow|normal|fast"); return; } for (n = 0; n < ndelays - 1; n++) if (delay <= delays[n]) break; d = n; for (n = 0; n < nrepeats - 1; n++) if (repeat <= repeats[n]) break; r = n; } arg[0] = delay; arg[1] = repeat; if (ioctl(0, KDSETREPEAT, arg)) { if (ioctl(0, KDSETRAD, (d << 5) | r)) warn("setting keyboard rate"); } } static const char * get_kbd_type_name(int type) { static struct { int type; const char *name; } name_table[] = { { KB_84, "AT 84" }, { KB_101, "AT 101/102" }, { KB_OTHER, "generic" }, }; unsigned int i; for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) { if (type == name_table[i].type) return name_table[i].name; } return "unknown"; } void show_kbd_info(void) { keyboard_info_t info; if (ioctl(0, KDGKBINFO, &info) == -1) { warn("unable to obtain keyboard information"); return; } printf("kbd%d:\n", info.kb_index); printf(" %.*s%d, type:%s (%d)\n", (int)sizeof(info.kb_name), info.kb_name, info.kb_unit, get_kbd_type_name(info.kb_type), info.kb_type); } void set_keyboard(char *device) { keyboard_info_t info; int fd; fd = open(device, O_RDONLY); if (fd < 0) { warn("cannot open %s", device); return; } if (ioctl(fd, KDGKBINFO, &info) == -1) { warn("unable to obtain keyboard information"); close(fd); return; } /* * The keyboard device driver won't release the keyboard by * the following ioctl, but it automatically will, when the device * is closed. So, we don't check error here. */ ioctl(fd, CONS_RELKBD, 0); close(fd); #if 1 printf("kbd%d\n", info.kb_index); printf(" %.*s%d, type:%s (%d)\n", (int)sizeof(info.kb_name), info.kb_name, info.kb_unit, get_kbd_type_name(info.kb_type), info.kb_type); #endif if (ioctl(0, CONS_SETKBD, info.kb_index) == -1) warn("unable to set keyboard"); } void release_keyboard(void) { keyboard_info_t info; /* * If stdin is not associated with a keyboard, the following ioctl * will fail. */ if (ioctl(0, KDGKBINFO, &info) == -1) { warn("unable to obtain keyboard information"); return; } #if 1 printf("kbd%d\n", info.kb_index); printf(" %.*s%d, type:%s (%d)\n", (int)sizeof(info.kb_name), info.kb_name, info.kb_unit, get_kbd_type_name(info.kb_type), info.kb_type); #endif if (ioctl(0, CONS_RELKBD, 0) == -1) warn("unable to release the keyboard"); } void mux_keyboard(u_int op, char *kbd) { keyboard_info_t info; char *unit, *ep; /* * If stdin is not associated with a keyboard, the following ioctl * will fail. */ if (ioctl(0, KDGKBINFO, &info) == -1) { warn("unable to obtain keyboard information"); return; } #if 1 printf("kbd%d\n", info.kb_index); printf(" %.*s%d, type:%s (%d)\n", (int)sizeof(info.kb_name), info.kb_name, info.kb_unit, get_kbd_type_name(info.kb_type), info.kb_type); #endif /* * split kbd into name and unit. find the right most part of the * kbd string that consist of only digits. */ memset(&info, 0, sizeof(info)); info.kb_unit = -1; ep = kbd - 1; do { unit = strpbrk(ep + 1, "0123456789"); if (unit != NULL) { info.kb_unit = strtol(unit, &ep, 10); if (*ep != '\0') info.kb_unit = -1; } } while (unit != NULL && info.kb_unit == -1); if (info.kb_unit == -1) { warnx("unable to find keyboard driver unit in '%s'", kbd); return; } if (unit == kbd) { warnx("unable to find keyboard driver name in '%s'", kbd); return; } if (unit - kbd >= (int) sizeof(info.kb_name)) { warnx("keyboard name '%s' is too long", kbd); return; } strncpy(info.kb_name, kbd, unit - kbd); /* * If stdin is not associated with a kbdmux(4) keyboard, the following * ioctl will fail. */ if (ioctl(0, op, &info) == -1) warn("unable to (un)mux the keyboard"); } void usage() { fprintf(stderr, "%s\n%s\n%s\n", "usage: kbdcontrol [-dFKix] [-A name] [-a name] [-b duration.pitch | [quiet.]belltype]", " [-r delay.repeat | speed] [-l mapfile] [-f # string]", " [-k device] [-L mapfile]"); exit(1); } int main(int argc, char **argv) { int opt; while((opt = getopt(argc, argv, "A:a:b:df:iKk:Fl:L:r:x")) != -1) switch(opt) { case 'A': case 'a': mux_keyboard((opt == 'A')? KBRELKBD : KBADDKBD, optarg); break; case 'b': set_bell_values(optarg); break; case 'd': print_keymap(); break; case 'l': load_keymap(optarg, 0); break; case 'L': load_keymap(optarg, 1); break; case 'f': set_functionkey(optarg, nextarg(argc, argv, &optind, 'f')); break; case 'F': load_default_functionkeys(); break; case 'i': show_kbd_info(); break; case 'K': release_keyboard(); break; case 'k': set_keyboard(optarg); break; case 'r': set_keyrates(optarg); break; case 'x': hex = 1; break; default: usage(); } if ((optind != argc) || (argc == 1)) usage(); exit(0); } Index: head/usr.sbin/kbdcontrol/kbdmap.5 =================================================================== --- head/usr.sbin/kbdcontrol/kbdmap.5 (revision 197329) +++ head/usr.sbin/kbdcontrol/kbdmap.5 (revision 197330) @@ -1,326 +1,326 @@ .\" Copyright (c) 2000 .\" David Malone .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd January 29, 2008 .Dt KBDMAP 5 .Os .Sh NAME .Nm kbdmap .Nd keyboard map file format for kbdcontrol .Sh SYNOPSIS .Nm .Sh DESCRIPTION A .Nm file describes how the keys on a keyboard should behave. These files can be loaded using .Xr kbdcontrol 1 , or .Xr kbdmap 1 can be used to select one of the default .Nm files interactively. A .Nm file can be specified in .Xr rc.conf 5 , to be loaded at boot time. The current keymap may also be printed using .Xr kbdcontrol 1 . .Pp Each line in the file can describe a key or an accent. A .Ql # character begins a comment, which extends to the end of the line. .Pp The description of a key begins with the scancode for that key. Then the effect of the key under combinations of shift, control and alt are listed in the following order: no modifier, shift, control, control and shift, alt, alt and shift, alt and control, alt and control and shift. The action of the key under each modifier can be: .Bl -tag -width Ar .It ' Ns Ar symbol Ns No ' The symbol the key should produce, in single quotes. .It Ar decnum The -.Tn ASCII +.Tn Unicode value to produce as a decimal number (see .Xr ascii 7 ) . For example, 32 for space. .It 0x Ns Ar hexnum The -.Tn ASCII +.Tn Unicode value to produce as a hexadecimal number. For example, 0x20 for space. .It Ar ctrlname One of the standard names for the .Tn ASCII control characters: nul, soh, stx, etx, eot, enq, ack, bel, bs, ht, nl, vt, np, cr, so, si, dle, dc1, dc2, dc3, dc4, nak, syn, etb, can, em, sub, esc, fs, gs, rs, ns, us, sp, del. .It Ar accentname By giving one of the accent names, the next key pressed will produce an accented character in accordance with that accent. See the description of accents below. The accent names are: dgra, dacu, dcir, dtil, dmac, dbre, ddot, duml, ddia, dsla, drin, dced, dapo, ddac, dogo, dcar. .It fkey Ns Ar N Act as the .Ar N Ns No th function key, where .Ar N is a decimal number in the range from 1 to 96. Refer to the .Xr atkbd 4 manual page for a list of predefined function keys. You can use the .Fl f option of the .Xr kbdcontrol 1 utility to assign arbitrary strings to function keys. .It lshift Act as left shift key. .It rshift Act as right shift key. .It clock Act as caps lock key. .It nlock Act as num lock key. .It slock Act as scroll lock key. .It lalt|alt Act as left alt key. .It btab Act as backwards tab. .It lctrl|ctrl Act as left control key. .It rctrl Act as right control key. .It ralt Act as right alt (altgr) key. .It alock Act as alt lock key. .It ashift Act as alt shift key. .It meta Act as meta key. .It lshifta|shifta Act as left shift key / alt lock. .It rshifta Act as right shift key / alt lock. .It lctrla|ctrla Act as left ctrl key / alt lock. .It rctrla Act as right ctrl key / alt lock. .It lalta|alta Act as left alt key / alt lock. .It ralta Act as right alt key / alt lock. .It nscr Act as switch to next screen. .It pscr Act as switch to previous screen. .It scr Ns Ar N Switch to screen .Ar N , where .Ar N is a decimal number. .It boot Reboot the machine. .It halt Halt the machine. .It pdwn Halt the machine and attempt to power it down. .It debug Call the debugger. .It susp Use APM to suspend power. .It saver Activate screen saver by toggling between splash/text screen. .It panic Panic the system. The .Xr sysctl 8 variable .Va machdep.enable_panic_key must be set to 1 to enable this feature. .It paste Act as mouse buffer paste. .El .Pp Finally, to complete the description of a key, a flag which describes the effect of caps lock and num lock on that key is given. The flag can be .Ql C to indicate that caps lock affects the key, .Ql N to indicate that num lock affects the key, .Ql B to indicate that both caps lock and num lock affects the key, or .Ql O to indicate that neither affects the key. .Pp An accent key works by modifying the behavior of the next key pressed. The description of an accent begins with one of the accent names given above. This is followed by the symbol for the accent, given in single quotes or as a decimal or hexadecimal -.Tn ASCII +.Tn Unicode value. This symbol will be produced if the accent key is pressed and then the space key is pressed. .Pp The description of the accent key continues with a list showing how it modifies various symbols, by giving pairs made up of the normal symbol and the modified symbol enclosed in parentheses. Both symbols in a pair can be given in either single quotes or as decimal or hexadecimal -.Tn ASCII +.Tn Unicode values. .Pp For example, consider the following extract from a .Nm : .Bd -literal -offset indent 041 dgra 172 nop nop '|' '|' nop nop O dgra '`' ( 'a' 224 ) ( 'A' 192 ) ( 'e' 232 ) ( 'E' 200 ) ( 'i' 236 ) ( 'I' 204 ) ( 'o' 242 ) ( 'O' 210 ) ( 'u' 249 ) ( 'U' 217 ) .Ed This extract configures the backtick key on a UK keyboard to act as a grave accent key. Pressing backtick followed by space produces a backtick, and pressing a backtick followed by a vowel produces the ISO-8859-1 symbol for that vowel with a grave accent. .Sh FILES .Bl -tag -width /usr/share/syscons/keymaps/* -compact .It Pa /usr/share/syscons/keymaps/* standard keyboard map files .El .Sh SEE ALSO .Xr kbdcontrol 1 , .Xr kbdmap 1 , .Xr keyboard 4 , .Xr syscons 4 , .Xr ascii 7 .Sh HISTORY This manual page first appeared in .Fx 4.2 .