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 *);

Reply via email to