All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] Add specific vt input's key map
@ 2018-09-11 20:23 Remi Pommarel
  2018-09-11 20:23 ` [PATCH 1/4] drivers/tty/vt/keyboard.c: refactor getting/setting a keymap entry Remi Pommarel
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Remi Pommarel @ 2018-09-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, linux-kernel
  Cc: Alexander Viro, Kees Cook, Elie Roudninski, Remi Pommarel

This patchset adds a way to have a specific keyboard config (i.e.
keycode to keysym map) for a vt attached input.

Because one can have different keyboards with different layouts on the
same VT, it can be useful to be able to associate a different keymap
with a different input. In order to do so this patchset introduces
three new ioctls:

1) KDGKBIENT
	Get an input key map's entry. If the input does not have a
	specific key map the requested entry is fetched from global
	key map.

2) KDSKBIENT
	Set an input key map's entry. If the input does not yet have a
	specific key map, a new one is created with the current global
	key map content copied in.

3) KDSKBIRST
	Reset an input key map. The input does not use a specific key
	map anymore and keycode translations are done with global key
	map.

In order to keep old behavior compatibility and not waste memory, an
input uses the global key_maps[] array by default and KDGKBENT/KDSKBENT
ioctls still get/set entries in this global key map. The specific key
map is only allocated on the first call to KDSKBIENT.

A patch for loadkeys is ready to be sent to the ML if this patchset
seems sane to you.

Here are some questions I had while doing this patchset:

- Is it ok to add new ioctl to old legacy code ? I added those ioctl
  the old way in order to match the header style. Maybe it is better to
  use the __IO* macros ?

- I am not quite sure about the meaning of keymap_count. IIUC, it is a
  counter of used keymap function arrays statically or dynamically
  allocated. If it is not the case, the modificiation in kc_setent() that
  decrements keymap_count even if a statically keymap function is removed
  with "K_NOSUCHMAP" (in PATCH 2/4) may be wrong ?

- kbd_detach_conf is a bit clumsy because it tries to copy a shared sparse
  pointer array without using GFP_ATOMIC.

Remi Pommarel (4):
  drivers/tty/vt/keyboard.c: refactor getting/setting a keymap entry
  drivers/tty/vt/keyboard.c: add keyboard config for each vt's input
  drivers/tty/vt/keyboard.c: Make key_down[] bitmap input dependent
  drivers/tty/vt: add ioctl to manage input specific keyboard configs

 drivers/tty/vt/keyboard.c | 531 ++++++++++++++++++++++++++++++--------
 drivers/tty/vt/vt_ioctl.c |   9 +
 fs/compat_ioctl.c         |   3 +
 include/linux/vt_kern.h   |   4 +
 include/uapi/linux/kd.h   |   9 +
 5 files changed, 452 insertions(+), 104 deletions(-)

-- 
2.18.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/4] drivers/tty/vt/keyboard.c: refactor getting/setting a keymap entry
  2018-09-11 20:23 [PATCH 0/4] Add specific vt input's key map Remi Pommarel
@ 2018-09-11 20:23 ` Remi Pommarel
  2018-09-11 20:23 ` [PATCH 2/4] drivers/tty/vt/keyboard.c: add keyboard config for each vt's input Remi Pommarel
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Remi Pommarel @ 2018-09-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, linux-kernel
  Cc: Alexander Viro, Kees Cook, Elie Roudninski, Remi Pommarel

In order to ease the addition of the ability of an input to use a
different key configuration (keycode to keysym map), the operations of
getting and setting an entry in a key map is moved in respective
functions.

Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Tested-by: Elie Roudninski <xademax@gmail.com>
---
 drivers/tty/vt/keyboard.c | 201 ++++++++++++++++++++++----------------
 1 file changed, 118 insertions(+), 83 deletions(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 88312c6c92cc..89fabb8ae04e 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -1874,115 +1874,150 @@ int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
 	return kc;
 }
 
-#define i (tmp.kb_index)
-#define s (tmp.kb_table)
-#define v (tmp.kb_value)
+#define i (kbe->kb_index)
+#define s (kbe->kb_table)
+#define v (kbe->kb_value)
 
-int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
-						int console)
+/*
+ * Get a keysym value from a key map
+ *
+ * @kb: Virtual console structure
+ * @kmaps: Key map array
+ * @kbe : Entry to find in key map
+ */
+static ushort __kdsk_getent(struct kbd_struct const *kb, ushort **kmaps,
+		struct kbentry const *kbe)
 {
-	struct kbd_struct *kb = kbd_table + console;
-	struct kbentry tmp;
-	ushort *key_map, *new_map, val, ov;
+	ushort *key_map, val;
 	unsigned long flags;
 
-	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
-		return -EFAULT;
+	/* Ensure another thread doesn't free it under us */
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	key_map = kmaps[s];
+	if (key_map) {
+		val = U(key_map[i]);
+		if (kb->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
+			val = K_HOLE;
+	} else
+		val = (i ? K_HOLE : K_NOSUCHMAP);
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
 
-	if (!capable(CAP_SYS_TTY_CONFIG))
-		perm = 0;
+	return val;
+}
 
-	switch (cmd) {
-	case KDGKBENT:
-		/* Ensure another thread doesn't free it under us */
+/*
+ * Set a keysym value in a key map
+ *
+ * @kb: Virtual console structure
+ * @kmaps: Key map array
+ * @kbe : Entry to set in key map
+ */
+static int __kdsk_setent(struct kbd_struct const *kb, ushort **kmaps,
+		struct kbentry const *kbe)
+{
+	ushort *key_map, *new_map, ov;
+	unsigned long flags;
+
+	if (!i && v == K_NOSUCHMAP) {
 		spin_lock_irqsave(&kbd_event_lock, flags);
-		key_map = key_maps[s];
-		if (key_map) {
-		    val = U(key_map[i]);
-		    if (kb->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
-			val = K_HOLE;
-		} else
-		    val = (i ? K_HOLE : K_NOSUCHMAP);
-		spin_unlock_irqrestore(&kbd_event_lock, flags);
-		return put_user(val, &user_kbe->kb_value);
-	case KDSKBENT:
-		if (!perm)
-			return -EPERM;
-		if (!i && v == K_NOSUCHMAP) {
-			spin_lock_irqsave(&kbd_event_lock, flags);
-			/* deallocate map */
-			key_map = key_maps[s];
-			if (s && key_map) {
-			    key_maps[s] = NULL;
-			    if (key_map[0] == U(K_ALLOCATED)) {
-					kfree(key_map);
-					keymap_count--;
-			    }
+		/* deallocate map */
+		key_map = kmaps[s];
+		if (s && key_map) {
+			kmaps[s] = NULL;
+			if (key_map[0] == U(K_ALLOCATED)) {
+				kfree(key_map);
+				keymap_count--;
 			}
-			spin_unlock_irqrestore(&kbd_event_lock, flags);
-			break;
 		}
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+		return 0;
+	}
 
-		if (KTYP(v) < NR_TYPES) {
-		    if (KVAL(v) > max_vals[KTYP(v)])
-				return -EINVAL;
-		} else
-		    if (kb->kbdmode != VC_UNICODE)
-				return -EINVAL;
+	if (KTYP(v) < NR_TYPES) {
+		if (KVAL(v) > max_vals[KTYP(v)])
+			return -EINVAL;
+	} else if (kb->kbdmode != VC_UNICODE)
+		return -EINVAL;
 
-		/* ++Geert: non-PC keyboards may generate keycode zero */
+	/* ++Geert: non-PC keyboards may generate keycode zero */
 #if !defined(__mc68000__) && !defined(__powerpc__)
-		/* assignment to entry 0 only tests validity of args */
-		if (!i)
-			break;
+	/* assignment to entry 0 only tests validity of args */
+	if (!i)
+		return 0;
 #endif
 
-		new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
-		if (!new_map)
-			return -ENOMEM;
-		spin_lock_irqsave(&kbd_event_lock, flags);
-		key_map = key_maps[s];
-		if (key_map == NULL) {
-			int j;
-
-			if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
-			    !capable(CAP_SYS_RESOURCE)) {
-				spin_unlock_irqrestore(&kbd_event_lock, flags);
-				kfree(new_map);
-				return -EPERM;
-			}
-			key_maps[s] = new_map;
-			key_map = new_map;
-			key_map[0] = U(K_ALLOCATED);
-			for (j = 1; j < NR_KEYS; j++)
-				key_map[j] = U(K_HOLE);
-			keymap_count++;
-		} else
-			kfree(new_map);
+	new_map = kmalloc(sizeof(plain_map), GFP_KERNEL);
+	if (!new_map)
+		return -ENOMEM;
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	key_map = kmaps[s];
+	if (key_map == NULL) {
+		int j;
 
-		ov = U(key_map[i]);
-		if (v == ov)
-			goto out;
-		/*
-		 * Attention Key.
-		 */
-		if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) {
+		if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
+				!capable(CAP_SYS_RESOURCE)) {
 			spin_unlock_irqrestore(&kbd_event_lock, flags);
+			kfree(new_map);
 			return -EPERM;
 		}
-		key_map[i] = U(v);
-		if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
-			do_compute_shiftstate();
-out:
+		kmaps[s] = new_map;
+		key_map = new_map;
+		key_map[0] = U(K_ALLOCATED);
+		for (j = 1; j < NR_KEYS; j++)
+			key_map[j] = U(K_HOLE);
+		keymap_count++;
+	} else
+		kfree(new_map);
+
+	ov = U(key_map[i]);
+	if (v == ov)
+		goto out;
+	/*
+	 * Attention Key.
+	 */
+	if (((ov == K_SAK) || (v == K_SAK)) && !capable(CAP_SYS_ADMIN)) {
 		spin_unlock_irqrestore(&kbd_event_lock, flags);
-		break;
+		return -EPERM;
 	}
+	key_map[i] = U(v);
+	if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
+		do_compute_shiftstate();
+out:
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
 	return 0;
 }
 #undef i
 #undef s
 #undef v
 
+int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
+						int console)
+{
+	struct kbd_struct *kb = kbd_table + console;
+	struct kbentry tmp;
+	ushort val;
+	int ret = 0;
+
+	if (copy_from_user(&tmp, user_kbe, sizeof(struct kbentry)))
+		return -EFAULT;
+
+	if (!capable(CAP_SYS_TTY_CONFIG))
+		perm = 0;
+
+	switch (cmd) {
+	case KDGKBENT:
+		val = __kdsk_getent(kb, key_maps, &tmp);
+		ret = put_user(val, &user_kbe->kb_value);
+		break;
+	case KDSKBENT:
+		if (!perm)
+			return -EPERM;
+		ret = __kdsk_setent(kb, key_maps, &tmp);
+		break;
+	}
+	return ret;
+}
+
 /* FIXME: This one needs untangling and locking */
 int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
 {
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] drivers/tty/vt/keyboard.c: add keyboard config for each vt's input
  2018-09-11 20:23 [PATCH 0/4] Add specific vt input's key map Remi Pommarel
  2018-09-11 20:23 ` [PATCH 1/4] drivers/tty/vt/keyboard.c: refactor getting/setting a keymap entry Remi Pommarel
@ 2018-09-11 20:23 ` Remi Pommarel
  2018-09-11 20:23 ` [PATCH 3/4] drivers/tty/vt/keyboard.c: Make key_down[] bitmap input dependent Remi Pommarel
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Remi Pommarel @ 2018-09-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, linux-kernel
  Cc: Alexander Viro, Kees Cook, Elie Roudninski, Remi Pommarel

Each connected kbd_handle holds a keyboard config that contains the
keymap used to translate keycode into keysym. At init and until the
kbd_handle gets detached it uses the global keyboard config
(aka key_maps[]) array to translate keycode into keysym.

For now, it is not possible for the user to detach a kbd_handle from
global config. Some new ioctl will be introduced to do so.

Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Tested-by: Elie Roudninski <xademax@gmail.com>
---
 drivers/tty/vt/keyboard.c | 198 ++++++++++++++++++++++++++++++++------
 1 file changed, 171 insertions(+), 27 deletions(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 89fabb8ae04e..4f09331ad5c3 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -108,6 +108,23 @@ struct vt_spawn_console vt_spawn_con = {
  * Internal Data.
  */
 
+/* Input handle's specific key configuration */
+struct kbd_conf {
+	ushort **maps;
+	unsigned int keymap_count;
+};
+
+static struct kbd_conf _gkeyconf;
+#define KC_IS_GLOBAL(kc) ((kc) == &_gkeyconf)
+
+/* Input handle */
+struct kbd_handle {
+	struct kref ref;
+	struct kbd_conf *conf;
+	struct input_handle handle;
+};
+#define hdl_to_kbd_handle(h) (container_of(h, struct kbd_handle, handle))
+
 static struct kbd_struct kbd_table[MAX_NR_CONSOLES];
 static struct kbd_struct *kbd = kbd_table;
 
@@ -1343,9 +1360,11 @@ static void kbd_rawcode(unsigned char data)
 		put_queue(vc, data);
 }
 
-static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
+static void kbd_keycode(struct kbd_handle *kh, unsigned int keycode, int down,
+		int hw_raw)
 {
 	struct vc_data *vc = vc_cons[fg_console].d;
+	struct kbd_conf *kconf = kh->conf;
 	unsigned short keysym, *key_map;
 	unsigned char type;
 	bool raw_mode;
@@ -1422,7 +1441,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 
 	param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
 	param.ledstate = kbd->ledflagstate;
-	key_map = key_maps[shift_final];
+	key_map = kconf->maps[shift_final];
 
 	rc = atomic_notifier_call_chain(&keyboard_notifier_list,
 					KBD_KEYCODE, &param);
@@ -1458,7 +1477,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 	if (type == KT_LETTER) {
 		type = KT_LATIN;
 		if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
-			key_map = key_maps[shift_final ^ (1 << KG_SHIFT)];
+			key_map = kconf->maps[shift_final ^ (1 << KG_SHIFT)];
 			if (key_map)
 				keysym = key_map[keycode];
 		}
@@ -1485,13 +1504,15 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 static void kbd_event(struct input_handle *handle, unsigned int event_type,
 		      unsigned int event_code, int value)
 {
+	struct kbd_handle *kh = hdl_to_kbd_handle(handle);
+
 	/* We are called with interrupts disabled, just take the lock */
 	spin_lock(&kbd_event_lock);
 
 	if (event_type == EV_MSC && event_code == MSC_RAW && HW_RAW(handle->dev))
 		kbd_rawcode(value);
 	if (event_type == EV_KEY)
-		kbd_keycode(event_code, value, HW_RAW(handle->dev));
+		kbd_keycode(kh, event_code, value, HW_RAW(handle->dev));
 
 	spin_unlock(&kbd_event_lock);
 
@@ -1519,6 +1540,114 @@ static bool kbd_match(struct input_handler *handler, struct input_dev *dev)
 	return false;
 }
 
+/*
+ * Keyconf functions
+ */
+
+static void kbd_init_conf(struct kbd_handle *kh)
+{
+	kh->conf = &_gkeyconf;
+}
+
+/*
+ * Detach an input from global keyboard config. If keyboard config is already
+ * detached, nothing is modified.
+ */
+static int kbd_detach_conf(struct kbd_handle *kh)
+{
+	struct kbd_conf *kconf;
+	ushort **tmp, **maps = NULL;
+	size_t i, tmpcnt = 0;
+	unsigned long flags;
+	unsigned int count = 0;
+	int err = -ENOMEM;
+
+	kconf = kmalloc(sizeof(*kconf), GFP_KERNEL);
+	if (kconf == NULL)
+		goto reterr;
+
+	maps = kcalloc(MAX_NR_KEYMAPS, sizeof(*maps), GFP_KERNEL);
+	if (maps == NULL)
+		goto reterr;
+
+	tmp = kmalloc_array(MAX_NR_KEYMAPS, sizeof(*tmp), GFP_KERNEL);
+	if (tmp == NULL)
+		goto reterr;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+
+	if (!KC_IS_GLOBAL(kh->conf))
+		goto out;  /* We have been raced at keymap creation */
+	count = kh->conf->keymap_count;
+
+	/* Pre-alloc enough tmp buffer to avoid allocating with lock held */
+	while (tmpcnt < count) {
+		spin_unlock_irqrestore(&kbd_event_lock, flags);
+		for (; tmpcnt < count; ++tmpcnt) {
+			tmp[tmpcnt] = kmalloc(sizeof(plain_map), GFP_KERNEL);
+			if (tmp[tmpcnt] == NULL)
+				goto freetmp;
+		}
+		spin_lock_irqsave(&kbd_event_lock, flags);
+
+		if (!KC_IS_GLOBAL(kh->conf))
+			goto out;  /* We have been raced at keymap creation */
+		count = kh->conf->keymap_count;
+	}
+
+	/*
+	 * Here at least enough tmp pointers have been allocated and lock is
+	 * held.
+	 *
+	 * If more than enough pointers have been allocated, the extra ones will
+	 * be freed after (see freetmp).
+	 */
+	if (count >= MAX_NR_OF_USER_KEYMAPS &&
+			!capable(CAP_SYS_RESOURCE)) {
+		err = -EPERM;
+		goto unlock;
+	}
+	for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
+		if (kh->conf->maps[i]) {
+			--tmpcnt;
+			memcpy(tmp[tmpcnt], kh->conf->maps[i],
+					sizeof(plain_map));
+			tmp[tmpcnt][0] = U(K_ALLOCATED);
+			maps[i] = tmp[tmpcnt];
+		}
+	}
+	kconf->maps = maps;
+	kconf->keymap_count = count;
+	kh->conf = kconf;
+	kconf = NULL;
+	maps = NULL;
+out:
+	err = 0;
+unlock:
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+freetmp:
+	for (; tmpcnt > 0; --tmpcnt) /* Free extra pointers */
+		kfree(tmp[tmpcnt - 1]);
+	kfree(tmp);
+reterr:
+	kfree(maps);
+	kfree(kconf);
+	return err;
+}
+
+static void kbd_destroy_conf(struct kbd_handle *kh)
+{
+	size_t i;
+
+	if (KC_IS_GLOBAL(kh->conf))
+		return;
+
+	for (i = 0; i < MAX_NR_KEYMAPS; ++i)
+		kfree(kh->conf->maps[i]);
+	kfree(kh->conf->maps);
+	kfree(kh->conf);
+}
+
 /*
  * When a keyboard (or other input device) is found, the kbd_connect
  * function is called. The function then looks at the device, and if it
@@ -1528,13 +1657,17 @@ static bool kbd_match(struct input_handler *handler, struct input_dev *dev)
 static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
 			const struct input_device_id *id)
 {
+	struct kbd_handle *kh;
 	struct input_handle *handle;
 	int error;
 
-	handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
-	if (!handle)
+	kh = kzalloc(sizeof(struct kbd_handle), GFP_KERNEL);
+	if (!kh)
 		return -ENOMEM;
 
+	kref_init(&kh->ref);
+	kbd_init_conf(kh);
+	handle = &kh->handle;
 	handle->dev = dev;
 	handle->handler = handler;
 	handle->name = "kbd";
@@ -1552,15 +1685,25 @@ static int kbd_connect(struct input_handler *handler, struct input_dev *dev,
  err_unregister_handle:
 	input_unregister_handle(handle);
  err_free_handle:
-	kfree(handle);
+	kfree(kh);
 	return error;
 }
 
+static void kbd_destroy(struct kref *ref)
+{
+	struct kbd_handle *kh = container_of(ref, struct kbd_handle, ref);
+
+	kbd_destroy_conf(kh);
+	kfree(kh);
+}
+
 static void kbd_disconnect(struct input_handle *handle)
 {
+	struct kbd_handle *kh = hdl_to_kbd_handle(handle);
+
 	input_close_device(handle);
 	input_unregister_handle(handle);
-	kfree(handle);
+	kref_put(&kh->ref, kbd_destroy);
 }
 
 /*
@@ -1619,6 +1762,8 @@ int __init kbd_init(void)
 	}
 
 	kbd_init_leds();
+	_gkeyconf.maps = key_maps;
+	_gkeyconf.keymap_count = keymap_count;
 
 	error = input_register_handler(&kbd_handler);
 	if (error)
@@ -1879,21 +2024,21 @@ int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
 #define v (kbe->kb_value)
 
 /*
- * Get a keysym value from a key map
+ * Get a keysym value from a keyconf's keymap
  *
  * @kb: Virtual console structure
- * @kmaps: Key map array
+ * @kconf: Keyboard configuration
  * @kbe : Entry to find in key map
  */
-static ushort __kdsk_getent(struct kbd_struct const *kb, ushort **kmaps,
-		struct kbentry const *kbe)
+static ushort kc_getent(struct kbd_struct const *kb,
+		struct kbd_conf const *kconf, struct kbentry const *kbe)
 {
 	ushort *key_map, val;
 	unsigned long flags;
 
 	/* Ensure another thread doesn't free it under us */
 	spin_lock_irqsave(&kbd_event_lock, flags);
-	key_map = kmaps[s];
+	key_map = kconf->maps[s];
 	if (key_map) {
 		val = U(key_map[i]);
 		if (kb->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
@@ -1906,13 +2051,13 @@ static ushort __kdsk_getent(struct kbd_struct const *kb, ushort **kmaps,
 }
 
 /*
- * Set a keysym value in a key map
+ * Set a keysym value in a keyboard configuration's keymap
  *
  * @kb: Virtual console structure
- * @kmaps: Key map array
+ * @kconf: Keyboard configuration
  * @kbe : Entry to set in key map
  */
-static int __kdsk_setent(struct kbd_struct const *kb, ushort **kmaps,
+static int kc_setent(struct kbd_struct const *kb, struct kbd_conf *kconf,
 		struct kbentry const *kbe)
 {
 	ushort *key_map, *new_map, ov;
@@ -1921,13 +2066,12 @@ static int __kdsk_setent(struct kbd_struct const *kb, ushort **kmaps,
 	if (!i && v == K_NOSUCHMAP) {
 		spin_lock_irqsave(&kbd_event_lock, flags);
 		/* deallocate map */
-		key_map = kmaps[s];
+		key_map = kconf->maps[s];
 		if (s && key_map) {
-			kmaps[s] = NULL;
-			if (key_map[0] == U(K_ALLOCATED)) {
+			kconf->maps[s] = NULL;
+			if (key_map[0] == U(K_ALLOCATED))
 				kfree(key_map);
-				keymap_count--;
-			}
+			kconf->keymap_count--;
 		}
 		spin_unlock_irqrestore(&kbd_event_lock, flags);
 		return 0;
@@ -1950,22 +2094,22 @@ static int __kdsk_setent(struct kbd_struct const *kb, ushort **kmaps,
 	if (!new_map)
 		return -ENOMEM;
 	spin_lock_irqsave(&kbd_event_lock, flags);
-	key_map = kmaps[s];
+	key_map = kconf->maps[s];
 	if (key_map == NULL) {
 		int j;
 
-		if (keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
+		if (kconf->keymap_count >= MAX_NR_OF_USER_KEYMAPS &&
 				!capable(CAP_SYS_RESOURCE)) {
 			spin_unlock_irqrestore(&kbd_event_lock, flags);
 			kfree(new_map);
 			return -EPERM;
 		}
-		kmaps[s] = new_map;
+		kconf->maps[s] = new_map;
 		key_map = new_map;
 		key_map[0] = U(K_ALLOCATED);
 		for (j = 1; j < NR_KEYS; j++)
 			key_map[j] = U(K_HOLE);
-		keymap_count++;
+		kconf->keymap_count++;
 	} else
 		kfree(new_map);
 
@@ -2006,13 +2150,13 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
 
 	switch (cmd) {
 	case KDGKBENT:
-		val = __kdsk_getent(kb, key_maps, &tmp);
+		val = kc_getent(kb, &_gkeyconf, &tmp);
 		ret = put_user(val, &user_kbe->kb_value);
 		break;
 	case KDSKBENT:
 		if (!perm)
 			return -EPERM;
-		ret = __kdsk_setent(kb, key_maps, &tmp);
+		ret = kc_setent(kb, &_gkeyconf, &tmp);
 		break;
 	}
 	return ret;
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] drivers/tty/vt/keyboard.c: Make key_down[] bitmap input dependent
  2018-09-11 20:23 [PATCH 0/4] Add specific vt input's key map Remi Pommarel
  2018-09-11 20:23 ` [PATCH 1/4] drivers/tty/vt/keyboard.c: refactor getting/setting a keymap entry Remi Pommarel
  2018-09-11 20:23 ` [PATCH 2/4] drivers/tty/vt/keyboard.c: add keyboard config for each vt's input Remi Pommarel
@ 2018-09-11 20:23 ` Remi Pommarel
  2018-09-11 20:23 ` [PATCH 4/4] drivers/tty/vt: add ioctl to manage input specific keyboard configs Remi Pommarel
  2018-09-12 14:35 ` [PATCH 0/4] Add specific vt input's key map Alan Cox
  4 siblings, 0 replies; 7+ messages in thread
From: Remi Pommarel @ 2018-09-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, linux-kernel
  Cc: Alexander Viro, Kees Cook, Elie Roudninski, Remi Pommarel

A kbd_handle input can have a specific keyboard config that makes
keycode to keysym mapping array different from another one. Because
key_down[] bitmap stores currently pressed key as keycode, it should be
associated with an input keymap in order to retrieve the associated
keysym.

Allocating this array in each input allows to retrieve its associated
keymap.

Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Tested-by: Elie Roudninski <xademax@gmail.com>
---
 drivers/tty/vt/keyboard.c | 46 +++++++++++++++++++++++++++------------
 1 file changed, 32 insertions(+), 14 deletions(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 4f09331ad5c3..7272e1828838 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -122,6 +122,7 @@ struct kbd_handle {
 	struct kref ref;
 	struct kbd_conf *conf;
 	struct input_handle handle;
+	unsigned long key_down[BITS_TO_LONGS(KEY_CNT)];/* keyboard key bitmap */
 };
 #define hdl_to_kbd_handle(h) (container_of(h, struct kbd_handle, handle))
 
@@ -140,7 +141,6 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
 static struct input_handler kbd_handler;
 static DEFINE_SPINLOCK(kbd_event_lock);
 static DEFINE_SPINLOCK(led_lock);
-static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)];	/* keyboard key bitmap */
 static unsigned char shift_down[NR_SHIFT];		/* shift state counters.. */
 static bool dead_key_next;
 static int npadch = -1;					/* -1 or number assembled on pad */
@@ -378,20 +378,18 @@ static void to_utf8(struct vc_data *vc, uint c)
 
 /*
  * Called after returning from RAW mode or when changing consoles - recompute
- * shift_down[] and shift_state from key_down[] maybe called when keymap is
- * undefined, so that shiftkey release is seen. The caller must hold the
- * kbd_event_lock.
+ * shift_down[] and shift_state from each input's key_down[] maybe called when
+ * keymap is undefined, so that shiftkey release is seen. The caller must hold
+ * the kbd_event_lock.
  */
 
-static void do_compute_shiftstate(void)
+static int do_compute_input_shiftstate(struct input_handle *handle, void *data)
 {
+	struct kbd_handle *kh = hdl_to_kbd_handle(handle);
 	unsigned int k, sym, val;
 
-	shift_state = 0;
-	memset(shift_down, 0, sizeof(shift_down));
-
-	for_each_set_bit(k, key_down, min(NR_KEYS, KEY_CNT)) {
-		sym = U(key_maps[0][k]);
+	for_each_set_bit(k, kh->key_down, min(NR_KEYS, KEY_CNT)) {
+		sym = U(kh->conf->maps[0][k]);
 		if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
 			continue;
 
@@ -402,6 +400,15 @@ static void do_compute_shiftstate(void)
 		shift_down[val]++;
 		shift_state |= BIT(val);
 	}
+	return 0;
+}
+
+static void do_compute_shiftstate(void)
+{
+	shift_state = 0;
+	memset(shift_down, 0, sizeof(shift_down));
+	input_handler_for_each_handle(&kbd_handler, NULL,
+			do_compute_input_shiftstate);
 }
 
 /* We still have to export this method to vt.c */
@@ -1278,6 +1285,17 @@ static int sparc_l1_a_state;
 extern void sun_do_break(void);
 #endif
 
+static int is_alt_down(struct input_handle *handle, void *data)
+{
+	struct kbd_handle *kh = hdl_to_kbd_handle(handle);
+
+	if (test_bit(KEY_LEFTALT, kh->key_down) ||
+			test_bit(KEY_RIGHTALT, kh->key_down))
+		return 1;
+
+	return 0;
+}
+
 static int emulate_raw(struct vc_data *vc, unsigned int keycode,
 		       unsigned char up_flag)
 {
@@ -1308,8 +1326,8 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
 		 * pressing PrtSc/SysRq alone, but simply 0x54
 		 * when pressing Alt+PrtSc/SysRq.
 		 */
-		if (test_bit(KEY_LEFTALT, key_down) ||
-		    test_bit(KEY_RIGHTALT, key_down)) {
+		if (input_handler_for_each_handle(&kbd_handler, NULL,
+					is_alt_down)) {
 			put_queue(vc, 0x54 | up_flag);
 		} else {
 			put_queue(vc, 0xe0);
@@ -1424,9 +1442,9 @@ static void kbd_keycode(struct kbd_handle *kh, unsigned int keycode, int down,
 	}
 
 	if (down)
-		set_bit(keycode, key_down);
+		set_bit(keycode, kh->key_down);
 	else
-		clear_bit(keycode, key_down);
+		clear_bit(keycode, kh->key_down);
 
 	if (rep &&
 	    (!vc_kbd_mode(kbd, VC_REPEAT) ||
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] drivers/tty/vt: add ioctl to manage input specific keyboard configs
  2018-09-11 20:23 [PATCH 0/4] Add specific vt input's key map Remi Pommarel
                   ` (2 preceding siblings ...)
  2018-09-11 20:23 ` [PATCH 3/4] drivers/tty/vt/keyboard.c: Make key_down[] bitmap input dependent Remi Pommarel
@ 2018-09-11 20:23 ` Remi Pommarel
  2018-09-12 14:35 ` [PATCH 0/4] Add specific vt input's key map Alan Cox
  4 siblings, 0 replies; 7+ messages in thread
From: Remi Pommarel @ 2018-09-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, linux-kernel
  Cc: Alexander Viro, Kees Cook, Elie Roudninski, Remi Pommarel

Because user can use different keyboards with different layouts on the
same tty, an input could have a different key map from another one. In
order to use and modify this specific key map the user can call three
new ioctls:

1) KDGKBIENT
	Get an input key map's entry. If the input does not have a
	specific keyboard config the requested entry is fetched from
	global key map.

2) KDSKBIENT
	Set an input key map's entry. If the input does not yet have a
	specific keyboard config, a new one is created with the current
	global config (the one modified with KDSKBENT) content copied
	in.

3) KDSKBIRST
	Reset an input key map. The input does not use a specific
	keyboard config anymore and keycode translations are done with
	global key map.

In order to remain compatible with old behavior, an input uses global
key map by default and KDGKENT/KDSKENT still get/set entries in the
global key map.

Below is an example of this ioctl usage:
------------------------- 8< -----------------------------
struct kbientry kbi = {
	.id = { /* Input is found by its input_id */
		.bustype = 0x0003,
		.product = 0x00B2,
		.vendor = 0x2319,
		.version = 0x0200,
	},
	.entry = { /* Entry to set or get */
		.kb_table = 0,
		.kb_index = 0x10,
		.kb_value = 0x0B61,
	},
};
int ret;

/* First set new entry for input 0003:00B2:2319:0200 */
ret = ioctl(fd, KDSKBIENT, &kbi); /* fd is a tty open file */
if(ret < 0)
	/* Error handling */

/* Get specific entry, on success kbi.entry.kb_value will be set */
ret = ioctl(fd, KDSKBIENT, &kbi);
if(ret < 0)
	/* Error handling */

/* Reset input key map so that global key_maps is used */
ret = ioctl(fd, KDSKBIRST, &kbi.id);
if(ret < 0)
	/* Error handling */
------------------------- 8< -----------------------------

Signed-off-by: Remi Pommarel <repk@triplefau.lt>
Tested-by: Elie Roudninski <xademax@gmail.com>
---
 drivers/tty/vt/keyboard.c | 126 ++++++++++++++++++++++++++++++++++++++
 drivers/tty/vt/vt_ioctl.c |   9 +++
 fs/compat_ioctl.c         |   3 +
 include/linux/vt_kern.h   |   4 ++
 include/uapi/linux/kd.h   |   9 +++
 5 files changed, 151 insertions(+)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 7272e1828838..9707d1ebeab4 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -2180,6 +2180,132 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
 	return ret;
 }
 
+/*
+ * Input keymap helper functions
+ */
+struct kbd_lookup_data {
+	struct input_id *id;
+	struct kbd_handle *kh;
+};
+
+static int _kbd_get_helper(struct input_handle *handle, void *data)
+{
+	struct kbd_handle *kh = hdl_to_kbd_handle(handle);
+	struct kbd_lookup_data *d = data;
+
+	if (memcmp(d->id, &handle->dev->id, sizeof(*d->id)) != 0)
+		return 0;
+
+	d->kh = kh;
+	kref_get(&kh->ref);
+	return 1;
+}
+
+/*
+ * Get an input specific handle.
+ * The caller will hold a reference on the found kbd_handle, if it exists, and
+ * should deref it after usage (with kbd_put).
+ */
+static int kbd_get(struct kbd_handle **kh, struct input_id *id)
+{
+	struct kbd_lookup_data d = {
+		.id = id,
+	};
+	int ret;
+
+	ret = input_handler_for_each_handle(&kbd_handler, &d, _kbd_get_helper);
+	*kh = d.kh;
+
+	return (ret == 0);
+}
+
+static void kbd_put(struct kbd_handle *kh)
+{
+	kref_put(&kh->ref, kbd_destroy);
+}
+
+int vt_do_kdski_ioctl(int cmd, struct kbientry __user *user_kbie, int perm,
+		int console)
+{
+	struct kbd_struct *kb = kbd_table + console;
+	struct kbd_handle *kh;
+	struct kbientry tmp;
+	ushort val;
+	int ret = 0;
+
+	if (copy_from_user(&tmp, user_kbie, sizeof(struct kbientry))) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	if (!capable(CAP_SYS_TTY_CONFIG))
+		perm = 0;
+
+	ret = kbd_get(&kh, &tmp.id);
+	if (ret != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	switch (cmd) {
+	case KDGKBIENT:
+		val = kc_getent(kb, kh->conf, &tmp.entry);
+		ret = put_user(val, &user_kbie->entry.kb_value);
+		break;
+	case KDSKBIENT:
+		if (!perm) {
+			ret = -EPERM;
+			break;
+		}
+		/* A keyconf's keymap is created only if global one is used */
+		ret = kbd_detach_conf(kh);
+		if (ret != 0)
+			break;
+
+		ret = kc_setent(kb, kh->conf, &tmp.entry);
+		break;
+	}
+
+	kbd_put(kh);
+out:
+	return ret;
+}
+
+int vt_do_kdskirst_ioctl(int cmd, struct input_id __user *user_iid, int perm,
+		int console)
+{
+	struct kbd_handle *kh;
+	struct input_id tmp;
+	unsigned long flags;
+	int ret;
+
+	if (copy_from_user(&tmp, user_iid, sizeof(struct input_id)))
+		return -EFAULT;
+
+	if (!capable(CAP_SYS_TTY_CONFIG))
+		perm = 0;
+
+	if (!perm)
+		return -EPERM;
+
+	ret = kbd_get(&kh, &tmp);
+	if (ret != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Restore keyboard's configuration to global one */
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	kbd_destroy_conf(kh);
+	kbd_init_conf(kh);
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+
+	kbd_put(kh);
+
+out:
+	return ret;
+}
+
 /* FIXME: This one needs untangling and locking */
 int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
 {
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index a78ad10a119b..ec688d81bab3 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -542,6 +542,15 @@ int vt_ioctl(struct tty_struct *tty,
 		ret = vt_do_kdsk_ioctl(cmd, up, perm, console);
 		break;
 
+	case KDGKBIENT:
+	case KDSKBIENT:
+		ret = vt_do_kdski_ioctl(cmd, up, perm, console);
+		break;
+
+	case KDSKBIRST:
+		ret = vt_do_kdskirst_ioctl(cmd, up, perm, console);
+		break;
+
 	case KDGKBSENT:
 	case KDSKBSENT:
 		ret = vt_do_kdgkb_ioctl(cmd, up, perm);
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index a9b00942e87d..7863b142234c 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -789,6 +789,9 @@ COMPATIBLE_IOCTL(KDGKBDIACR)
 COMPATIBLE_IOCTL(KDSKBDIACR)
 COMPATIBLE_IOCTL(KDGKBDIACRUC)
 COMPATIBLE_IOCTL(KDSKBDIACRUC)
+COMPATIBLE_IOCTL(KDGKBIENT)
+COMPATIBLE_IOCTL(KDSKBIENT)
+COMPATIBLE_IOCTL(KDSKBIRST)
 COMPATIBLE_IOCTL(KDKBDREP)
 COMPATIBLE_IOCTL(KDGKBLED)
 COMPATIBLE_IOCTL(KDGETLED)
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index 3fd07912909c..0f17d305b840 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -175,6 +175,10 @@ extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
 								int perm);
 extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe,
 					int perm, int console);
+extern int vt_do_kdski_ioctl(int cmd, struct kbientry __user *user_kbie,
+					int perm, int console);
+extern int vt_do_kdskirst_ioctl(int cmd, struct input_id __user *user_iid,
+					int perm, int console);
 extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb,
                                         int perm);
 extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm);
diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h
index 4616b31f84da..be048e9e3f02 100644
--- a/include/uapi/linux/kd.h
+++ b/include/uapi/linux/kd.h
@@ -3,6 +3,7 @@
 #define _UAPI_LINUX_KD_H
 #include <linux/types.h>
 #include <linux/compiler.h>
+#include <linux/input.h>
 
 /* 0x4B is 'K', to avoid collision with termios and vt */
 
@@ -137,6 +138,14 @@ struct kbdiacrsuc {
 #define KDGKBDIACRUC    0x4BFA  /* read kernel accent table - UCS */
 #define KDSKBDIACRUC    0x4BFB  /* write kernel accent table - UCS */
 
+struct kbientry {
+	struct input_id id;
+	struct kbentry entry;
+};
+#define KDGKBIENT 0x4B53 /* Get one entry in input's translation table */
+#define KDSKBIENT 0x4B54 /* Set one entry in input's translation table */
+#define KDSKBIRST 0x4B55 /* Set input to use global translation table */
+
 struct kbkeycode {
 	unsigned int scancode, keycode;
 };
-- 
2.18.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/4] Add specific vt input's key map
  2018-09-11 20:23 [PATCH 0/4] Add specific vt input's key map Remi Pommarel
                   ` (3 preceding siblings ...)
  2018-09-11 20:23 ` [PATCH 4/4] drivers/tty/vt: add ioctl to manage input specific keyboard configs Remi Pommarel
@ 2018-09-12 14:35 ` Alan Cox
  2018-09-12 17:03   ` Remi Pommarel
  4 siblings, 1 reply; 7+ messages in thread
From: Alan Cox @ 2018-09-12 14:35 UTC (permalink / raw)
  To: Remi Pommarel
  Cc: Greg Kroah-Hartman, Jiri Slaby, linux-kernel, Alexander Viro,
	Kees Cook, Elie Roudninski

On Tue, 11 Sep 2018 22:23:55 +0200
Remi Pommarel <repk@triplefau.lt> wrote:

> This patchset adds a way to have a specific keyboard config (i.e.
> keycode to keysym map) for a vt attached input.

Who actually needs this given that you can't even render most
international symbols in text mode and X and friends already deal with
mapping of keyboards at this degree of fine-ness and even more so ?

> - kbd_detach_conf is a bit clumsy because it tries to copy a shared sparse
>   pointer array without using GFP_ATOMIC.

It's not a performance critical path at least. You could look at
switching it to use RCU but firstly I think the question is whether
anyone cares enough to bother putting it in the upstream kernel ?

Alan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 0/4] Add specific vt input's key map
  2018-09-12 14:35 ` [PATCH 0/4] Add specific vt input's key map Alan Cox
@ 2018-09-12 17:03   ` Remi Pommarel
  0 siblings, 0 replies; 7+ messages in thread
From: Remi Pommarel @ 2018-09-12 17:03 UTC (permalink / raw)
  To: Alan Cox
  Cc: Greg Kroah-Hartman, Jiri Slaby, linux-kernel, Alexander Viro,
	Kees Cook, Elie Roudninski

On Wed, Sep 12, 2018 at 03:35:19PM +0100, Alan Cox wrote:
> On Tue, 11 Sep 2018 22:23:55 +0200
> Remi Pommarel <repk@triplefau.lt> wrote:
> 
> > This patchset adds a way to have a specific keyboard config (i.e.
> > keycode to keysym map) for a vt attached input.
> 
> Who actually needs this given that you can't even render most
> international symbols in text mode and X and friends already deal with
> mapping of keyboards at this degree of fine-ness and even more so ?

Let say you share one computer among people used to different keyboard
layouts, so you have plugged in one keyboard for each of those people.
Let say you have encrypted your disk with a long passphrase, the one
that boots up the computer has to enter it. It could be annoying to
learn another layout just to enter a passphrase to boot. This allows to
set an udev rule in initrd to use a different layout per keyboard so
every people using this computer can use their favorite layout to enter
the passphrase.

I am in such a situation and so is @Elie.

I also don't start X server at boot time (or sometimes I switch to a linux
VT) it is not very convenient for me to switch layout to enter my password,
or even to type "startx".

I do agree that it is not an absolute must-have or shiny feature, it is
just a convenient one, at least for me.

-- 
Remi

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-09-12 16:55 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-11 20:23 [PATCH 0/4] Add specific vt input's key map Remi Pommarel
2018-09-11 20:23 ` [PATCH 1/4] drivers/tty/vt/keyboard.c: refactor getting/setting a keymap entry Remi Pommarel
2018-09-11 20:23 ` [PATCH 2/4] drivers/tty/vt/keyboard.c: add keyboard config for each vt's input Remi Pommarel
2018-09-11 20:23 ` [PATCH 3/4] drivers/tty/vt/keyboard.c: Make key_down[] bitmap input dependent Remi Pommarel
2018-09-11 20:23 ` [PATCH 4/4] drivers/tty/vt: add ioctl to manage input specific keyboard configs Remi Pommarel
2018-09-12 14:35 ` [PATCH 0/4] Add specific vt input's key map Alan Cox
2018-09-12 17:03   ` Remi Pommarel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.