According to wsconctl(8), I re-map my Caps Lock key to be a Control: keyboard.map+='keysym Caps_Lock = Control_L'
After I suspend my machine I lose my modified keyboard layout and it reverts to the default layout of the chosen encoding. Looking for a solution I came across a discussion from 2014 where a similar problem was solved for keyboard encodings [1]. My initial approach was to do just the same for keymaps -- storing them in parent wsmux on every change. But because of the intricate interplay between encodings, keymaps and actual hardware keys I had to look for a simpler solution. Also, it did not feel right to store keyboard settings in wsmux since it works with abstract event sources. Here is what I came up with: consolidate configurable keyboard settings in a data structure which survives attaching/detaching of wskbd. The behavior described in [1] posed an additional challenge. All instances of wskbd devices have a shared state which they read and write -- the default settings for the new keyboards. I am not sure why that shared encoding (kbd_t) was not protected by a synchronization primitive previously, but I believe the complex state of encoding+keymap certainly needs a lock. I believe in my patch I have preserved the useful behavior described in [1] regarding interpretation of KB_DEFAULT and carrying last set encoding as a default encoding for new keyboards. Although, I have no Sun keyboard to test it. Resulting behavior: 1. wskbdX devices preserve their individual configs, including custom keymaps, repeat and bell settings on re-attaching. 2. New keyboards inherit encoding (working since [1]) and custom keymaps (new) from the last _explicit_ change. Except for keyboards which have an encoding defined by hardware. Bell and keyrepeat are excluded from this inheritance because a) it is how it was and b) there are ioctls to set those defaults explicitly. Does it look sensible? Best regards, Sergii Rudchenko [1]: https://marc.info/?l=openbsd-misc&m=139051083212925&w=2 diff --git a/sys/dev/wscons/wskbd.c b/sys/dev/wscons/wskbd.c index ab3bbd3b6f2..12789d72d9d 100644 --- a/sys/dev/wscons/wskbd.c +++ b/sys/dev/wscons/wskbd.c @@ -94,6 +94,7 @@ #include <sys/errno.h> #include <sys/fcntl.h> #include <sys/vnode.h> +#include <sys/queue.h> #include <sys/poll.h> #include <ddb/db_var.h> @@ -122,6 +123,8 @@ int wskbddebug = 0; #include <dev/wscons/wsmuxvar.h> struct wskbd_internal { + const struct wskbd_mapdata *t_keymap; + const struct wskbd_consops *t_consops; void *t_consaccesscookie; @@ -136,15 +139,13 @@ struct wskbd_internal { keysym_t t_symbols[MAXKEYSYMSPERKEY]; struct wskbd_softc *t_sc; /* back pointer */ - - struct wskbd_mapdata t_keymap; /* translation map table and - current layout */ }; struct wskbd_softc { struct wsevsrc sc_base; struct wskbd_internal *id; + struct wskbd_config *sc_config; const struct wskbd_accessops *sc_accessops; void *sc_accesscookie; @@ -164,13 +165,29 @@ struct wskbd_softc { int sc_translating; /* xlate to chars for emulation */ - int sc_maplen; /* number of entries in sc_map */ - struct wscons_keymap *sc_map; /* current translation map */ - int sc_refcnt; u_char sc_dying; /* device is being detached */ }; +/* + * Keyboard configuration which can be tuned via wsconsctl and is + * persisted across attach/detach cycles (e.g. unplugging and + * plugging back a USB keyboard, suspend/resume). + */ +struct wskbd_config { + struct wskbd_bell_data c_bell_data; + struct wskbd_keyrepeat_data c_keyrepeat_data; + + kbd_t c_layout; /* current layout */ + int c_maplen; /* number of entries in sc_map */ + struct wscons_keymap *c_map; /* current translation map */ + const struct wscons_keydesc *c_keydesc; /* map compatibility */ + + int c_unit; /* device unit number of the owner */ + int c_dirty; /* any settings changed since initialization */ + SLIST_ENTRY(wskbd_config) c_next; +}; + #define MOD_SHIFT_L (1 << 0) #define MOD_SHIFT_R (1 << 1) #define MOD_SHIFTLOCK (1 << 2) @@ -203,6 +220,11 @@ int wskbd_match(struct device *, void *, void *); void wskbd_attach(struct device *, struct device *, void *); int wskbd_detach(struct device *, int); int wskbd_activate(struct device *, int); +static struct wskbd_config * wskbd_acquire_config(int); +static void wskbd_release_config(struct wskbd_config *); +static void wskbd_config_copy_defaults(struct wskbd_config *, int); +static void wskbd_update_default_keymap(struct wskbd_config *); +static void wskbd_config_free_keymap(struct wskbd_config *); int wskbd_displayioctl(struct device *, u_long, caddr_t, int, struct proc *); @@ -257,13 +279,6 @@ extern int kbd_reset; #define WSKBD_DEFAULT_BELL_VOLUME 50 /* 50% volume */ #endif -struct wskbd_bell_data wskbd_default_bell_data = { - WSKBD_BELL_DOALL, - WSKBD_DEFAULT_BELL_PITCH, - WSKBD_DEFAULT_BELL_PERIOD, - WSKBD_DEFAULT_BELL_VOLUME, -}; - #ifndef WSKBD_DEFAULT_KEYREPEAT_DEL1 #define WSKBD_DEFAULT_KEYREPEAT_DEL1 400 /* 400ms to start repeating */ #endif @@ -271,11 +286,23 @@ struct wskbd_bell_data wskbd_default_bell_data = { #define WSKBD_DEFAULT_KEYREPEAT_DELN 100 /* 100ms to between repeats */ #endif -struct wskbd_keyrepeat_data wskbd_default_keyrepeat_data = { - WSKBD_KEYREPEAT_DOALL, - WSKBD_DEFAULT_KEYREPEAT_DEL1, - WSKBD_DEFAULT_KEYREPEAT_DELN, +/* Default configuration for new devices */ +static struct wskbd_config wskbd_defcfg = { + .c_bell_data = { + WSKBD_BELL_DOALL, + WSKBD_DEFAULT_BELL_PITCH, + WSKBD_DEFAULT_BELL_PERIOD, + WSKBD_DEFAULT_BELL_VOLUME, + }, + .c_keyrepeat_data = { + WSKBD_KEYREPEAT_DOALL, + WSKBD_DEFAULT_KEYREPEAT_DEL1, + WSKBD_DEFAULT_KEYREPEAT_DELN, + }, }; +static struct rwlock wskbd_defcfg_lock = + RWLOCK_INITIALIZER("wskbd_defcfg"); + #if NWSMUX > 0 || NWSDISPLAY > 0 struct wssrcops wskbd_srcops = { @@ -313,8 +340,6 @@ wskbd_update_layout(struct wskbd_internal *id, kbd_t enc) id->t_flags |= WSKFL_METAESC; else id->t_flags &= ~WSKFL_METAESC; - - id->t_keymap.layout = enc; } /* @@ -362,9 +387,9 @@ wskbd_attach(struct device *parent, struct device *self, void *aux) { struct wskbd_softc *sc = (struct wskbd_softc *)self; struct wskbddev_attach_args *ap = aux; - kbd_t layout; + struct wskbd_config *cfg = NULL; + kbd_t hw_layout = KB_NONE; #if NWSMUX > 0 - struct wsmux_softc *wsmux_sc = NULL; int mux, error; #endif @@ -373,18 +398,13 @@ wskbd_attach(struct device *parent, struct device *self, void *aux) #if NWSMUX > 0 || NWSDISPLAY > 0 sc->sc_base.me_ops = &wskbd_srcops; #endif -#if NWSMUX > 0 - mux = sc->sc_base.me_dv.dv_cfdata->wskbddevcf_mux; - if (mux >= 0) - wsmux_sc = wsmux_getmux(mux); -#endif /* NWSMUX > 0 */ if (ap->console) { sc->id = &wskbd_console_data; } else { sc->id = malloc(sizeof(struct wskbd_internal), M_DEVBUF, M_WAITOK | M_ZERO); - bcopy(ap->keymap, &sc->id->t_keymap, sizeof(sc->id->t_keymap)); + sc->id->t_keymap = ap->keymap; } #if NWSDISPLAY > 0 @@ -392,6 +412,7 @@ wskbd_attach(struct device *parent, struct device *self, void *aux) #endif sc->id->t_sc = sc; + hw_layout = sc->id->t_keymap->layout; sc->sc_accessops = ap->accessops; sc->sc_accesscookie = ap->accesscookie; @@ -399,40 +420,48 @@ wskbd_attach(struct device *parent, struct device *self, void *aux) sc->sc_translating = 1; sc->sc_ledstate = -1; /* force update */ + cfg = wskbd_acquire_config(sc->sc_base.me_dv.dv_unit); + sc->sc_config = cfg; + /* - * If this layout is the default choice of the driver (i.e. the - * driver doesn't know better), pick the existing layout of the - * current mux, if any. + * Fill newly allocated configs with defaults. + * If hardware reports a specific layout, then ignore + * layout data from the defaults */ - layout = sc->id->t_keymap.layout; -#if NWSMUX > 0 - if (layout & KB_DEFAULT) { - if (wsmux_sc != NULL && wsmux_get_layout(wsmux_sc) != KB_NONE) - layout = wsmux_get_layout(wsmux_sc); + if (cfg->c_layout == KB_NONE) + wskbd_config_copy_defaults(cfg, !(hw_layout & KB_DEFAULT)); + + /* + * Check if attached hardware is compatible with the + * keymap from cached or default configuration + */ + if (cfg->c_map != NULL && + cfg->c_keydesc != sc->id->t_keymap->keydesc) { + wskbd_config_free_keymap(cfg); + if (cfg->c_layout == KB_USER) + cfg->c_layout = KB_NONE; } -#endif - for (;;) { - if (wskbd_load_keymap(&sc->id->t_keymap, layout, &sc->sc_map, - &sc->sc_maplen) == 0) + + if (cfg->c_layout == KB_NONE) + cfg->c_layout = hw_layout & ~KB_DEFAULT; + + while (cfg->c_map == NULL) { + if (wskbd_load_keymap(sc->id->t_keymap->keydesc, + cfg->c_layout, &cfg->c_map, &cfg->c_maplen) == 0) break; -#if NWSMUX > 0 - if (layout == sc->id->t_keymap.layout) - panic("cannot load keymap"); - if (wsmux_sc != NULL && wsmux_get_layout(wsmux_sc) != KB_NONE) { - printf("\n%s: cannot load keymap, " + + if (cfg->c_layout != (hw_layout & ~KB_DEFAULT)) { + printf("%s: cannot load keymap, " "falling back to default\n%s", sc->sc_base.me_dv.dv_xname, sc->sc_base.me_dv.dv_xname); - layout = wsmux_get_layout(wsmux_sc); + cfg->c_layout = hw_layout & ~KB_DEFAULT; } else -#endif panic("cannot load keymap"); } - wskbd_update_layout(sc->id, layout); - /* set default bell and key repeat data */ - sc->sc_bell_data = wskbd_default_bell_data; - sc->sc_keyrepeat_data = wskbd_default_keyrepeat_data; + wskbd_update_layout(sc->id, cfg->c_layout); + cfg->c_keydesc = sc->id->t_keymap->keydesc; if (ap->console) { KASSERT(wskbd_console_initted); @@ -451,22 +480,13 @@ wskbd_attach(struct device *parent, struct device *self, void *aux) #if NWSMUX > 0 /* Ignore mux for console; it always goes to the console mux. */ - if (wsmux_sc != NULL && ap->console == 0) { + mux = sc->sc_base.me_dv.dv_cfdata->wskbddevcf_mux; + if (mux >= 0 && ap->console == 0) { printf(" mux %d\n", mux); - error = wsmux_attach_sc(wsmux_sc, &sc->sc_base); + error = wsmux_attach_sc(wsmux_getmux(mux), &sc->sc_base); if (error) printf("%s: attach error=%d\n", sc->sc_base.me_dv.dv_xname, error); - - /* - * Try and set this encoding as the mux default if it - * hasn't any yet, and if this is not a driver default - * layout (i.e. parent driver pretends to know better). - * Note that wsmux_set_layout() rejects layouts with - * KB_DEFAULT set. - */ - if (wsmux_get_layout(wsmux_sc) == KB_NONE) - wsmux_set_layout(wsmux_sc, layout); } else #endif printf("\n"); @@ -496,7 +516,7 @@ wskbd_cnattach(const struct wskbd_consops *consops, void *conscookie, KASSERT(!wskbd_console_initted); - bcopy(mapdata, &wskbd_console_data.t_keymap, sizeof(*mapdata)); + wskbd_console_data.t_keymap = mapdata; wskbd_update_layout(&wskbd_console_data, mapdata->layout); wskbd_console_data.t_consops = consops; @@ -514,9 +534,7 @@ wskbd_cndetach(void) { KASSERT(wskbd_console_initted); - wskbd_console_data.t_keymap.keydesc = NULL; - wskbd_console_data.t_keymap.layout = KB_NONE; - + wskbd_console_data.t_keymap = NULL; wskbd_console_data.t_consops = NULL; wskbd_console_data.t_consaccesscookie = NULL; @@ -533,6 +551,7 @@ void wskbd_repeat(void *v) { struct wskbd_softc *sc = (struct wskbd_softc *)v; + struct wskbd_config *cfg = sc->sc_config; int s = spltty(); if (sc->sc_repeating == 0) { @@ -547,15 +566,15 @@ wskbd_repeat(void *v) /* deliver keys */ if (sc->sc_displaydv != NULL) wsdisplay_kbdinput(sc->sc_displaydv, - sc->id->t_keymap.layout, + cfg->c_layout, sc->id->t_symbols, sc->sc_repeating); } else { /* queue event */ wskbd_deliver_event(sc, sc->sc_repeat_type, sc->sc_repeat_value); } - if (sc->sc_keyrepeat_data.delN != 0) - timeout_add_msec(&sc->sc_repeat_ch, sc->sc_keyrepeat_data.delN); + if (cfg->c_keyrepeat_data.delN != 0) + timeout_add_msec(&sc->sc_repeat_ch, cfg->c_keyrepeat_data.delN); splx(s); } #endif @@ -582,6 +601,7 @@ int wskbd_detach(struct device *self, int flags) { struct wskbd_softc *sc = (struct wskbd_softc *)self; + struct wskbd_config *cfg = sc->sc_config; struct wseventvar *evar; int maj, mn; int s; @@ -620,8 +640,11 @@ wskbd_detach(struct device *self, int flags) splx(s); } - free(sc->sc_map, M_DEVBUF, - sc->sc_maplen * sizeof(struct wscons_keymap)); + if (cfg->c_layout != KB_USER) { + /* Standard keymaps we reload on attach */ + wskbd_config_free_keymap(cfg); + } + wskbd_release_config(cfg); /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) @@ -639,6 +662,7 @@ void wskbd_input(struct device *dev, u_int type, int value) { struct wskbd_softc *sc = (struct wskbd_softc *)dev; + struct wskbd_config *cfg = sc->sc_config; #if NWSDISPLAY > 0 int num; #endif @@ -669,14 +693,14 @@ wskbd_input(struct device *dev, u_int type, int value) } #endif wsdisplay_kbdinput(sc->sc_displaydv, - sc->id->t_keymap.layout, + cfg->c_layout, sc->id->t_symbols, num); } - if (sc->sc_keyrepeat_data.del1 != 0) { + if (cfg->c_keyrepeat_data.del1 != 0) { sc->sc_repeating = num; timeout_add_msec(&sc->sc_repeat_ch, - sc->sc_keyrepeat_data.del1); + cfg->c_keyrepeat_data.del1); } } return; @@ -687,11 +711,11 @@ wskbd_input(struct device *dev, u_int type, int value) #if NWSDISPLAY > 0 /* Repeat key presses if enabled. */ - if (type == WSCONS_EVENT_KEY_DOWN && sc->sc_keyrepeat_data.del1 != 0) { + if (type == WSCONS_EVENT_KEY_DOWN && cfg->c_keyrepeat_data.del1 != 0) { sc->sc_repeat_type = type; sc->sc_repeat_value = value; sc->sc_repeating = 1; - timeout_add_msec(&sc->sc_repeat_ch, sc->sc_keyrepeat_data.del1); + timeout_add_msec(&sc->sc_repeat_ch, cfg->c_keyrepeat_data.del1); } #endif } @@ -984,6 +1008,17 @@ wskbd_do_ioctl_sc(struct wskbd_softc *sc, u_long cmd, caddr_t data, int flag, * if it didn't recognize the request. */ error = wskbd_displayioctl(&sc->sc_base.me_dv, cmd, data, flag, p); + if (error == 0) { + switch (cmd) { + case WSKBDIO_SETBELL: + case WSKBDIO_SETKEYREPEAT: + case WSKBDIO_SETMAP: + case WSKBDIO_SETENCODING: + sc->sc_config->c_dirty = 1; + break; + } + + } return (error != -1 ? error : ENOTTY); } @@ -996,6 +1031,7 @@ wskbd_displayioctl(struct device *dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct wskbd_softc *sc = (struct wskbd_softc *)dev; + struct wskbd_config *cfg = sc->sc_config; struct wskbd_bell_data *ubdp, *kbdp; struct wskbd_keyrepeat_data *ukdp, *kkdp; struct wskbd_map_data *umdp; @@ -1032,24 +1068,22 @@ wskbd_displayioctl(struct device *dev, u_long cmd, caddr_t data, int flag, case WSKBDIO_BELL: return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, - WSKBDIO_COMPLEXBELL, (caddr_t)&sc->sc_bell_data, flag, p)); + WSKBDIO_COMPLEXBELL, (caddr_t)&cfg->c_bell_data, flag, p)); case WSKBDIO_COMPLEXBELL: ubdp = (struct wskbd_bell_data *)data; - SETBELL(ubdp, ubdp, &sc->sc_bell_data); + SETBELL(ubdp, ubdp, &cfg->c_bell_data); return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, WSKBDIO_COMPLEXBELL, (caddr_t)ubdp, flag, p)); case WSKBDIO_SETBELL: - kbdp = &sc->sc_bell_data; -setbell: + kbdp = &cfg->c_bell_data; ubdp = (struct wskbd_bell_data *)data; SETBELL(kbdp, ubdp, kbdp); return (0); case WSKBDIO_GETBELL: - kbdp = &sc->sc_bell_data; -getbell: + kbdp = &cfg->c_bell_data; ubdp = (struct wskbd_bell_data *)data; SETBELL(ubdp, kbdp, kbdp); return (0); @@ -1057,13 +1091,21 @@ getbell: case WSKBDIO_SETDEFAULTBELL: if ((error = suser(p)) != 0) return (error); - kbdp = &wskbd_default_bell_data; - goto setbell; + kbdp = &wskbd_defcfg.c_bell_data; + ubdp = (struct wskbd_bell_data *)data; + rw_enter_write(&wskbd_defcfg_lock); + SETBELL(kbdp, ubdp, kbdp); + rw_exit_write(&wskbd_defcfg_lock); + return (0); case WSKBDIO_GETDEFAULTBELL: - kbdp = &wskbd_default_bell_data; - goto getbell; + kbdp = &wskbd_defcfg.c_bell_data; + ubdp = (struct wskbd_bell_data *)data; + rw_enter_read(&wskbd_defcfg_lock); + SETBELL(ubdp, kbdp, kbdp); + rw_exit_read(&wskbd_defcfg_lock); + return (0); #undef SETBELL @@ -1077,15 +1119,13 @@ getbell: } while (0) case WSKBDIO_SETKEYREPEAT: - kkdp = &sc->sc_keyrepeat_data; -setkeyrepeat: + kkdp = &cfg->c_keyrepeat_data; ukdp = (struct wskbd_keyrepeat_data *)data; SETKEYREPEAT(kkdp, ukdp, kkdp); return (0); case WSKBDIO_GETKEYREPEAT: - kkdp = &sc->sc_keyrepeat_data; -getkeyrepeat: + kkdp = &cfg->c_keyrepeat_data; ukdp = (struct wskbd_keyrepeat_data *)data; SETKEYREPEAT(ukdp, kkdp, kkdp); return (0); @@ -1093,13 +1133,21 @@ getkeyrepeat: case WSKBDIO_SETDEFAULTKEYREPEAT: if ((error = suser(p)) != 0) return (error); - kkdp = &wskbd_default_keyrepeat_data; - goto setkeyrepeat; + kkdp = &wskbd_defcfg.c_keyrepeat_data; + ukdp = (struct wskbd_keyrepeat_data *)data; + rw_enter_write(&wskbd_defcfg_lock); + SETKEYREPEAT(kkdp, ukdp, kkdp); + rw_exit_write(&wskbd_defcfg_lock); + return (0); case WSKBDIO_GETDEFAULTKEYREPEAT: - kkdp = &wskbd_default_keyrepeat_data; - goto getkeyrepeat; + kkdp = &wskbd_defcfg.c_keyrepeat_data; + ukdp = (struct wskbd_keyrepeat_data *)data; + rw_enter_read(&wskbd_defcfg_lock); + SETKEYREPEAT(ukdp, kkdp, kkdp); + rw_exit_read(&wskbd_defcfg_lock); + return (0); #undef SETKEYREPEAT @@ -1115,59 +1163,57 @@ getkeyrepeat: error = copyin(umdp->map, buf, len); if (error == 0) { wskbd_init_keymap(umdp->maplen, - &sc->sc_map, &sc->sc_maplen); - memcpy(sc->sc_map, buf, len); + &cfg->c_map, &cfg->c_maplen); + memcpy(cfg->c_map, buf, len); /* drop the variant bits handled by the map */ - enc = KB_USER | (KB_VARIANT(sc->id->t_keymap.layout) & - KB_HANDLEDBYWSKBD); - wskbd_update_layout(sc->id, enc); + cfg->c_layout = KB_USER | + (KB_VARIANT(cfg->c_layout) & KB_HANDLEDBYWSKBD); + wskbd_update_layout(sc->id, cfg->c_layout); + wskbd_update_default_keymap(cfg); } free(buf, M_TEMP, len); return(error); case WSKBDIO_GETMAP: umdp = (struct wskbd_map_data *)data; - if (umdp->maplen > sc->sc_maplen) - umdp->maplen = sc->sc_maplen; - error = copyout(sc->sc_map, umdp->map, + if (umdp->maplen > cfg->c_maplen) + umdp->maplen = cfg->c_maplen; + error = copyout(cfg->c_map, umdp->map, umdp->maplen*sizeof(struct wscons_keymap)); return(error); case WSKBDIO_GETENCODING: - *((kbd_t *)data) = sc->id->t_keymap.layout & ~KB_DEFAULT; + *((kbd_t *) data) = cfg->c_layout; return(0); case WSKBDIO_SETENCODING: enc = *((kbd_t *)data); if (KB_ENCODING(enc) == KB_USER) { /* user map must already be loaded */ - if (KB_ENCODING(sc->id->t_keymap.layout) != KB_USER) + if (KB_ENCODING(cfg->c_layout) != KB_USER) return (EINVAL); /* map variants make no sense */ if (KB_VARIANT(enc) & ~KB_HANDLEDBYWSKBD) return (EINVAL); } else { - error = wskbd_load_keymap(&sc->id->t_keymap, enc, - &sc->sc_map, &sc->sc_maplen); + error = wskbd_load_keymap(sc->id->t_keymap->keydesc, + enc, &cfg->c_map, &cfg->c_maplen); if (error) return (error); } + cfg->c_layout = enc; wskbd_update_layout(sc->id, enc); -#if NWSMUX > 0 - /* Update mux default layout */ - if (sc->sc_base.me_parent != NULL) - wsmux_set_layout(sc->sc_base.me_parent, enc); -#endif + wskbd_update_default_keymap(cfg); return (0); case WSKBDIO_GETENCODINGS: uedp = (struct wskbd_encoding_data *)data; - for (count = 0; sc->id->t_keymap.keydesc[count].name; count++) + for (count = 0; sc->id->t_keymap->keydesc[count].name; count++) ; if (uedp->nencodings > count) uedp->nencodings = count; for (i = 0; i < uedp->nencodings; i++) { - error = copyout(&sc->id->t_keymap.keydesc[i].name, + error = copyout(&sc->id->t_keymap->keydesc[i].name, &uedp->encodings[i], sizeof(kbd_t)); if (error) return (error); @@ -1586,6 +1632,7 @@ int wskbd_translate(struct wskbd_internal *id, u_int type, int value) { struct wskbd_softc *sc = id->t_sc; + struct wskbd_config *cfg = sc != NULL ? sc->sc_config : NULL; keysym_t ksym, res, *group; struct wscons_keymap kpbuf, *kp; int gindex, iscommand = 0; @@ -1605,18 +1652,18 @@ wskbd_translate(struct wskbd_internal *id, u_int type, int value) return (0); } - if (sc != NULL) { - if (value < 0 || value >= sc->sc_maplen) { + if (cfg != NULL) { + if (value < 0 || value >= cfg->c_maplen) { #ifdef DEBUG printf("wskbd_translate: keycode %d out of range\n", value); #endif return (0); } - kp = sc->sc_map + value; + kp = cfg->c_map + value; } else { kp = &kpbuf; - wskbd_get_mapentry(&id->t_keymap, value, kp); + wskbd_get_mapentry(id->t_keymap, value, kp); } /* if this key has a command, process it first */ @@ -1836,3 +1883,102 @@ wskbd_debugger(struct wskbd_softc *sc) } #endif } + +/* Config instances which are stashed for future re-use */ +static SLIST_HEAD(, wskbd_config) cached_configs = + SLIST_HEAD_INITIALIZER(cached_configs); + +/* + * Get a cached instance of config, or create one if necessary. + * This relies on stable device numbering, but it is ok + * since wsconsctl.conf also does. For example, two USB keyboards + * unplugged and plugged back in a different order will swap + * their wskbd unit numbers. + */ +static struct wskbd_config * +wskbd_acquire_config(int unit) { + struct wskbd_config *cfg = NULL; + struct wskbd_config *item = NULL; + + SLIST_FOREACH(item, &cached_configs, c_next) { + if (item->c_unit == unit) { + cfg = item; + SLIST_REMOVE(&cached_configs, item, wskbd_config, + c_next); + break; + } + } + + if (cfg == NULL) { + cfg = malloc(sizeof(struct wskbd_config), + M_DEVBUF, M_WAITOK | M_ZERO); + cfg->c_unit = unit; + } + return (cfg); +} + +static void +wskbd_release_config(struct wskbd_config *cfg) { + if (cfg->c_dirty) { + SLIST_INSERT_HEAD(&cached_configs, cfg, c_next); + return; + } + /* Nothing changed from the defaults, destroying */ + wskbd_config_free_keymap(cfg); + free(cfg, M_DEVBUF, sizeof(struct wskbd_config)); +} + +/* + * Copy current default configuration into the provided object + * DO NOT call it on released configs, it does not preserve c_next + */ +static void +wskbd_config_copy_defaults(struct wskbd_config *cfg, int ignore_layout) { + int unit = cfg->c_unit; + struct wskbd_config* def = &wskbd_defcfg; + + rw_enter_read(&wskbd_defcfg_lock); + memcpy(cfg, def, sizeof(struct wskbd_config)); + + /* keymap is dynamic memory, has to be copied separately */ + cfg->c_map = NULL; + cfg->c_maplen = 0; + + if (ignore_layout) { + cfg->c_layout = KB_NONE; + } else if (wskbd_defcfg.c_map != NULL) { + wskbd_init_keymap(def->c_maplen, &cfg->c_map, &cfg->c_maplen); + memcpy(cfg->c_map, def->c_map, + def->c_maplen * sizeof(struct wscons_keymap)); + } + + rw_exit_read(&wskbd_defcfg_lock); + cfg->c_unit = unit; +} + +/* + * Copy the default keymap and encoding for new keyboards from the + * provided config instance + */ +static void +wskbd_update_default_keymap(struct wskbd_config *cfg) { + struct wskbd_config* def = &wskbd_defcfg; + + rw_enter_write(&wskbd_defcfg_lock); + + def->c_layout = cfg->c_layout; + def->c_keydesc = cfg->c_keydesc; + wskbd_init_keymap(cfg->c_maplen, &def->c_map, &def->c_maplen); + memcpy(def->c_map, cfg->c_map, + cfg->c_maplen * sizeof(struct wscons_keymap)); + + rw_exit_write(&wskbd_defcfg_lock); +} + +static void +wskbd_config_free_keymap(struct wskbd_config *cfg) { + free(cfg->c_map, M_DEVBUF, + cfg->c_maplen * sizeof(struct wscons_keymap)); + cfg->c_map = NULL; + cfg->c_maplen = 0; +} diff --git a/sys/dev/wscons/wskbdutil.c b/sys/dev/wscons/wskbdutil.c index 9cca6eea7a9..3f1e7a77497 100644 --- a/sys/dev/wscons/wskbdutil.c +++ b/sys/dev/wscons/wskbdutil.c @@ -399,7 +399,7 @@ wskbd_init_keymap(int newlen, struct wscons_keymap **map, int *maplen) } int -wskbd_load_keymap(const struct wskbd_mapdata *mapdata, kbd_t layout, +wskbd_load_keymap(const struct wscons_keydesc *keydesc, kbd_t layout, struct wscons_keymap **map, int *maplen) { int i, s, kc, stack_ptr; @@ -410,7 +410,7 @@ wskbd_load_keymap(const struct wskbd_mapdata *mapdata, kbd_t layout, for (cur = layout & ~KB_HANDLEDBYWSKBD, stack_ptr = 0; cur != 0; stack_ptr++) { - mp = mapdata->keydesc; + mp = keydesc; while (mp->map_size > 0) { if (cur == 0 || mp->name == cur) { break; @@ -420,7 +420,7 @@ wskbd_load_keymap(const struct wskbd_mapdata *mapdata, kbd_t layout, if (stack_ptr == nitems(stack)) panic("wskbd_load_keymap: %d: recursion too deep", - mapdata->layout); + layout); if (mp->map_size <= 0) return(EINVAL); diff --git a/sys/dev/wscons/wsksymvar.h b/sys/dev/wscons/wsksymvar.h index e40889dd81c..9c43b341326 100644 --- a/sys/dev/wscons/wsksymvar.h +++ b/sys/dev/wscons/wsksymvar.h @@ -68,7 +68,7 @@ struct wskbd_mapdata { void wskbd_get_mapentry(const struct wskbd_mapdata *, int, struct wscons_keymap *); void wskbd_init_keymap(int, struct wscons_keymap **, int *); -int wskbd_load_keymap(const struct wskbd_mapdata *, kbd_t, +int wskbd_load_keymap(const struct wscons_keydesc *, kbd_t, struct wscons_keymap **, int *); keysym_t wskbd_compose_value(keysym_t *); diff --git a/sys/dev/wscons/wsmux.c b/sys/dev/wscons/wsmux.c index b3080573836..d99890291f8 100644 --- a/sys/dev/wscons/wsmux.c +++ b/sys/dev/wscons/wsmux.c @@ -654,7 +654,6 @@ wsmux_create(const char *name, int unit) "%s%d", name, unit); sc->sc_base.me_dv.dv_unit = unit; sc->sc_base.me_ops = &wsmux_srcops; - sc->sc_kbd_layout = KB_NONE; return (sc); } @@ -904,19 +903,6 @@ wsmux_set_display(struct wsmux_softc *sc, struct device *displaydv) } #endif /* NWSDISPLAY > 0 */ -uint32_t -wsmux_get_layout(struct wsmux_softc *sc) -{ - return sc->sc_kbd_layout; -} - -void -wsmux_set_layout(struct wsmux_softc *sc, uint32_t layout) -{ - if ((layout & KB_DEFAULT) == 0) - sc->sc_kbd_layout = layout; -} - /* * Returns the depth of the longest chain of nested wsmux devices starting * from sc. diff --git a/sys/dev/wscons/wsmuxvar.h b/sys/dev/wscons/wsmuxvar.h index 20627191afb..c87eca4c834 100644 --- a/sys/dev/wscons/wsmuxvar.h +++ b/sys/dev/wscons/wsmuxvar.h @@ -78,7 +78,6 @@ struct wsmux_softc { struct proc *sc_p; /* open proc */ TAILQ_HEAD(, wsevsrc) sc_cld; /* list of children */ struct rwlock sc_lock; /* lock for sc_cld */ - u_int32_t sc_kbd_layout; /* current layout of keyboard */ #ifdef WSDISPLAY_COMPAT_RAWKBD int sc_rawkbd; /* A hack to remember the kbd mode */ #endif @@ -94,8 +93,6 @@ struct wsmux_softc *wsmux_create(const char *, int); int wsmux_attach_sc(struct wsmux_softc *, struct wsevsrc *); void wsmux_detach_sc(struct wsevsrc *); int wsmux_set_display(struct wsmux_softc *, struct device *); -uint32_t wsmux_get_layout(struct wsmux_softc *); -void wsmux_set_layout(struct wsmux_softc *, uint32_t); int wskbd_add_mux(int, struct wsmux_softc *); int wsmouse_add_mux(int, struct wsmux_softc *);