* [RFC PATCH] Fix modifier keys for Redragon Asura Keyboard
@ 2018-01-10 9:23 Robert Munteanu
0 siblings, 0 replies; 2+ messages in thread
From: Robert Munteanu @ 2018-01-10 9:23 UTC (permalink / raw)
To: Jiri Kosina
Cc: Benjamin Tissoires, linux-kernel, linux-input, Robert Munteanu
The microdia family of keyboards uses a non-standard way of sending
modifier keys.
The down event always sets the first bit to 0x04 and the second keycode
to a custom value For instance, left shift sends the following bits
04 02 00 00 00 00 00 00
while left control sends
04 01 00 00 00 00 00 00
As a result all modifier keys are mapped to left shift and the keyboard is
non-functional in that respect. To solve the problem, we capture the
raw data in raw_event and manually generate the correct input events.
The keyboard functions mostly as expected now, with only a few minor
issues:
- two USB devices are detected instead of 1
- some key combinations are not triggered - e.g.
left shift + left ctrl + p. However, the same combination is recognized
with the right shift key.
Tested on Kernel 4.14.12
Changelog:
- v2: modifier keys work, some combinations are still troublesome
- v3: style cleanup, rebase on top of 4.14
Signed-off-by: Robert Munteanu <rombert@apache.org>
---
drivers/hid/Kconfig | 9 +++
drivers/hid/Makefile | 2 +-
drivers/hid/hid-core.c | 3 +
drivers/hid/hid-ids.h | 3 +
drivers/hid/hid-microdia.c | 178 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 194 insertions(+), 1 deletion(-)
create mode 100644 drivers/hid/hid-microdia.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 374301fcbc86..0033a81f0ad4 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -548,6 +548,15 @@ config HID_MAYFLASH
Say Y here if you have HJZ Mayflash PS3 game controller adapters
and want to enable force feedback support.
+config HID_MICRODIA
+ tristate "Microdia based keyboards"
+ depends on HID
+ default !EXPERT
+ ---help---
+ Support for Microdia devices that are not compliant with the HID standard.
+
+ One known example if the Redragon Asura Keyboard.
+
config HID_MICROSOFT
tristate "Microsoft non-fully HID-compliant devices"
depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 235bd2a7b333..e66a305876c5 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
+obj-$(CONFIG_HID_MICRODIA) += hid-microdia.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
@@ -121,4 +122,3 @@ obj-$(CONFIG_USB_KBD) += usbhid/
obj-$(CONFIG_I2C_HID) += i2c-hid/
-obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 330ca983828b..eeb325e3ff0d 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -2388,6 +2388,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
#endif
#if IS_ENABLED(CONFIG_HID_ZYDACRON)
{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) },
+#endif
+#if IS_ENABLED(CONFIG_HID_MICRODIA)
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICRODIA, USB_DEVICE_ID_REDRAGON_ASURA) },
#endif
{ }
};
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index be2e005c3c51..2edbf01361f1 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1161,4 +1161,7 @@
#define USB_VENDOR_ID_UGTIZER 0x2179
#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053
+#define USB_VENDOR_ID_MICRODIA 0x0c45
+#define USB_DEVICE_ID_REDRAGON_ASURA 0x760b
+
#endif
diff --git a/drivers/hid/hid-microdia.c b/drivers/hid/hid-microdia.c
new file mode 100644
index 000000000000..a6119786859d
--- /dev/null
+++ b/drivers/hid/hid-microdia.c
@@ -0,0 +1,178 @@
+/*
+ * HID driver for Microdia-based keyboards
+ *
+ * Copyright (c) 2017 Robert Munteanu
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/log2.h>
+#include <linux/input-event-codes.h>
+
+#include "hid-ids.h"
+
+
+// The microdia family of keyboards uses a non-standard way of sending
+// modifier keys
+//
+// The down event always sets the first bit to 0x04 and the second keycode
+// to a custom value. For instance, left shift sends the following bits
+//
+// 04 02 00 00 00 00 00 00
+//
+// while left control sends
+//
+// 04 01 00 00 00 00 00 00
+//
+// Unfortunately these are all mapped to left shift and the keyboard is
+// non-functional in that respect. To solve the problem, we capture the
+// raw data in raw_event and manually generate the correct input events
+//
+// TODO
+//
+// 1. Some modifiers keys still don't work, e.g. Ctrl-Shift-P
+// Interestingly enough, this happens when pressing the physical
+// left shift key, but now when pressing the physical right shift key.
+// hexdump does not show them either.
+// 2. A second USB keyboard is registered, but it should be removed
+// 3. Modifier keys sometimes get stuck, need to re-press to clear
+
+static int microdia_input_configured(struct hid_device *hdev,
+ struct hid_input *hi)
+{
+
+ hid_info(hdev, "Configuring input");
+
+ hid_set_drvdata(hdev, hi);
+
+ return 0;
+}
+
+static int microdia_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ // do not map left shift since we will manually generate input
+ // events in microdia_raw_event
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD)
+ if ((usage->hid & HID_USAGE) == 0xe1)
+ return -1;
+
+ return 0;
+}
+
+// array index of a modifier matches the bit index in the data payload
+// the modifier data is always stored in the second int
+// e.g. for left alt the 1 bit is set - 04 04 ...
+// TODO - second entry should be left shift, but that's not possible
+// since we ignore it in the mapping
+static int microdia_modifiers[7] = {
+ KEY_LEFTCTRL,
+ KEY_RIGHTSHIFT,
+ KEY_LEFTALT,
+ KEY_LEFTMETA,
+ KEY_RIGHTCTRL,
+ KEY_RIGHTSHIFT,
+ KEY_RIGHTALT
+};
+
+static int microdia_last_handled_modifier;
+
+static int microdia_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *data, int size)
+{
+ int i, changed_key, new_value, mapped_key;
+ struct hid_input *hi;
+
+ // 1. validate that we get 8 bits of the for 04 xx 00 00 00 00 00 00
+ if (size != 8) {
+ hid_info(hdev, "size is %d, ignoring\n", size);
+ return 0;
+ }
+
+ if (data[0] != 4) {
+ hid_info(hdev, "data[0] is %d, ignoring\n", data[0]);
+ return 0;
+ }
+
+ // TODO - can I somehow use a bit mask? data & 0x00ffffff != 0
+ for (i = 2; i < size; i++) {
+ if (data[i] != 0) {
+ hid_info(hdev, "data[%d] is %d, ignoring\n", i,
+ data[i]);
+ return 0;
+ }
+ }
+
+ // TODO - don't process the keyup event 04 00 00 00 00 00 00 00
+ // if we don't expect a modifier key 'up' event
+
+ // 2. detect based on previous data what key was pressed or depressed
+ // a key was pressed or depressed if its bit has a different value
+ // between this and the previous invocation. If the bit is set
+ // it was pressed
+
+ changed_key = data[1] ^ microdia_last_handled_modifier;
+ new_value = (data[1] & changed_key) != 0;
+ // TODO - is ilog2 really needed?
+ mapped_key = microdia_modifiers[ilog2(changed_key)];
+
+ hid_info(hdev, "Got payload 0x%x\n", data[1]);
+ hid_info(hdev, "Detected key %d value %d\n", changed_key, new_value);
+
+ // 3. report the key event and track what was sent
+ hi = hid_get_drvdata(hdev);
+
+ hid_info(hdev, "Reporting key %d with value %d\n", mapped_key,
+ new_value);
+ input_report_key(hi->input, mapped_key, new_value);
+
+ microdia_last_handled_modifier = data[1];
+
+ return 1;
+}
+
+static __u8 *microdia_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+
+ int i;
+
+ hid_info(hdev, "Report size is %d", *rsize);
+
+ for (i = 0 ; i < *rsize; i++)
+ hid_info(hdev, "%2d ", rdesc[i]);
+
+ hid_info(hdev, "\n");
+
+ return rdesc;
+}
+
+static const struct hid_device_id microdia_devices[] = {
+ {HID_USB_DEVICE(USB_VENDOR_ID_MICRODIA, USB_DEVICE_ID_REDRAGON_ASURA)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(hid, microdia_devices);
+
+static struct hid_driver microdia_driver = {
+ .name = "microdia",
+ .input_mapping = microdia_input_mapping,
+ .id_table = microdia_devices,
+ .raw_event = microdia_raw_event,
+ .input_configured = microdia_input_configured,
+ .report_fixup = microdia_report_fixup,
+};
+
+module_hid_driver(microdia_driver);
+
+MODULE_LICENSE("GPL");
--
2.15.1
^ permalink raw reply related [flat|nested] 2+ messages in thread* [RFC PATCH] Fix modifier keys for Redragon Asura Keyboard
@ 2017-06-15 10:38 Robert Munteanu
0 siblings, 0 replies; 2+ messages in thread
From: Robert Munteanu @ 2017-06-15 10:38 UTC (permalink / raw)
To: linux-input
The microdia family of keyboards uses a non-standard way of sending modifier
keys
The down event always sets the first bit to 0x04 and the second keycode to a
custom value For instance, left shift sends the following bits
04 02 00 00 00 00 00 00
while left control sends
04 01 00 00 00 00 00 00
As a result all modifier keys are mapped to left shift and the keyboard is
non-functional in that respect. To solve the problem, we capture the raw data
in raw_event and manually genereate the correct input events.
I am sending this patch as a RFC to make sure I am taking the correct approach.
One functional issue that I'm facing is that the overriden modifier keys also
include a KEY_LEFTSHIFT event when pressed. evtest reports the following events
when the left control key is pressed:
Event: time 1497521832.320627, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 1
Event: time 1497521832.320627, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e1
Event: time 1497521832.320627, type 1 (EV_KEY), code 42 (KEY_LEFTSHIFT), value 1
Event: time 1497521832.320627, -------------- SYN_REPORT ------------
Event: time 1497521832.384558, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 0
Event: time 1497521832.384558, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e1
Event: time 1497521832.384558, type 1 (EV_KEY), code 42 (KEY_LEFTSHIFT), value 0
Event: time 1497521832.384558, -------------- SYN_REPORT ------------
I would expect the KEY_LEFTSHIFT event to not be sent since I'm returning 1
from microdia_raw_event.
The printk calls will be removed for the final submission.
Tested on Kernel 4.11.4.
---
drivers/hid/Kconfig | 7 +++
drivers/hid/Makefile | 1 +
drivers/hid/hid-core.c | 1 +
drivers/hid/hid-ids.h | 3 ++
drivers/hid/hid-microdia.c | 121 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 133 insertions(+)
create mode 100644 drivers/hid/hid-microdia.c
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 8c54cb8f5d6d..ef950b11ffbe 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -521,6 +521,13 @@ config HID_MAYFLASH
Say Y here if you have HJZ Mayflash PS3 game controller adapters
and want to enable force feedback support.
+config HID_MICRODIA
+ tristate "Microdia based keyboards"
+ depends on HID
+ default !EXPERT
+ ---help---
+ Support for Microdia based keyboards such as the Redragon Asura.
+
config HID_MICROSOFT
tristate "Microsoft non-fully HID-compliant devices"
depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 4d111f23e801..d3c162d5ecb3 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
+obj-$(CONFIG_HID_MICRODIA) += hid-microdia.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d162f0dc76e3..7a54ab0a59b1 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1974,6 +1974,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_LUXAFOR) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICRODIA, USB_DEVICE_ID_REDRAGON_ASURA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index b26c030926c1..7e4f716c3a98 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1127,4 +1127,7 @@
#define USB_VENDOR_ID_UGTIZER 0x2179
#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053
+#define USB_VENDOR_ID_MICRODIA 0x0c45
+#define USB_DEVICE_ID_REDRAGON_ASURA 0x760b
+
#endif
diff --git a/drivers/hid/hid-microdia.c b/drivers/hid/hid-microdia.c
new file mode 100644
index 000000000000..1a7093ea0ad3
--- /dev/null
+++ b/drivers/hid/hid-microdia.c
@@ -0,0 +1,121 @@
+/*
+ * HID driver for Microdia-based keyboards
+ *
+ * Copyright (c) 2017 Robert Munteanu
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/input-event-codes.h>
+
+#include "hid-ids.h"
+
+
+// The microdia family of keyboards uses a non-standard way of sending modifier keys
+//
+// The down event always sets the first bit to 0x04 and the second keycode to a custom value
+// For instance, left shift sends the following bits
+//
+// 04 02 00 00 00 00 00 00
+//
+// while left control sends
+//
+// 04 01 00 00 00 00 00 00
+//
+// Unfortunately these are all mapped to left shift and the keyboard is non-functional in that respect
+// To solve the problem, we capture the raw data in raw_event and manually genereate the correct input
+// events
+//
+// TODO
+// 1. When overriding pressed keys the left shift key code is also sent
+// 2. A second USB keyboard is registered, but it should be removed
+
+static int microdia_input_configured(struct hid_device *hdev, struct hid_input *hi)
+{
+ hid_set_drvdata(hdev, hi);
+
+ return 0;
+}
+
+static int last_key;
+
+static int microdia_raw_event(struct hid_device *hdev, struct hid_report *report,
+ u8 *data, int size)
+{
+ int ret = 0, key, value;
+ struct hid_input *hi = hid_get_drvdata(hdev);
+
+ if ( size >= 2 && data[0] == 4 && data[1] != 2) {
+ value = data[1] != 0;
+ printk("[hid-microdia] raw (sz=%d) %d %d: ", size, data[0], data[1]);
+ if ( !value ) {
+ key = last_key;
+ last_key = 0;
+ ret = 1;
+ } else {
+ switch(data[1]) {
+ case 1: // left control
+ key = KEY_LEFTCTRL;
+ ret = 1;
+ break;
+ // case 2: // left shift
+ case 4: // left alt
+ key = KEY_LEFTALT;
+ ret = 1;
+ break;
+ case 8: // left win key
+ key = KEY_LEFTMETA;
+ ret = 1;
+ break;
+ case 16: // right ctrl
+ key = KEY_RIGHTCTRL;
+ ret = 1;
+ break;
+ case 32: // right shift
+ key = KEY_RIGHTSHIFT;
+ ret = 1;
+ break;
+ case 64: // right alt
+ key = KEY_RIGHTALT;
+ ret = 1;
+ break;
+ }
+ }
+
+ if ( ret ) {
+ input_report_key(hi->input, key, value);
+ last_key = key;
+ printk("Remapped %d %d to key %d value %d\n", data[0], data[1], key, value);
+ }
+ }
+ printk("Returning %d\n", ret);
+ return ret;
+}
+
+static const struct hid_device_id microdia_devices[] = {
+ {HID_USB_DEVICE(USB_VENDOR_ID_MICRODIA, USB_DEVICE_ID_REDRAGON_ASURA)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(hid, microdia_devices);
+
+static struct hid_driver microdia_driver = {
+ .name = "microdia",
+ .id_table = microdia_devices,
+ .raw_event = microdia_raw_event,
+ .input_configured = microdia_input_configured,
+};
+
+module_hid_driver(microdia_driver);
+
+MODULE_LICENSE("GPL");
--
2.13.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2018-01-10 9:23 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-01-10 9:23 [RFC PATCH] Fix modifier keys for Redragon Asura Keyboard Robert Munteanu
-- strict thread matches above, loose matches on Subject: below --
2017-06-15 10:38 Robert Munteanu
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.