diff mbox series

[v2,18/23] HID: uclogic: Support Gray-coded rotary encoders

Message ID 20190210101409.3511-19-spbnick@gmail.com (mailing list archive)
State Mainlined
Commit 8a47670c35e2a8e70753eabd96d4f8d8b3c0eeba
Delegated to: Jiri Kosina
Headers show
Series [v2,01/23] HID: kye: Add support for EasyPen M406XE | expand

Commit Message

Nikolai Kondrashov Feb. 10, 2019, 10:14 a.m. UTC
Add support for converting Gray-coded rotary encoder input into dial
input compatible with HID standard. Needed for Ugee G5 support.

Signed-off-by: Nikolai Kondrashov <spbnick@gmail.com>
---
 drivers/hid/hid-uclogic-core.c   | 29 +++++++++++++++++++++++++++++
 drivers/hid/hid-uclogic-params.h |  8 ++++++++
 2 files changed, 37 insertions(+)
diff mbox series

Patch

diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index f5fb612daa1e..dfacb04308b1 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -37,6 +37,8 @@  struct uclogic_drvdata {
 	struct input_dev *pen_input;
 	/* In-range timer */
 	struct timer_list inrange_timer;
+	/* Last rotary encoder state, or U8_MAX for none */
+	u8 re_state;
 };
 
 /**
@@ -175,6 +177,7 @@  static int uclogic_probe(struct hid_device *hdev,
 		goto failure;
 	}
 	timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0);
+	drvdata->re_state = U8_MAX;
 	hid_set_drvdata(hdev, drvdata);
 
 	/* Initialize the device and retrieve interface parameters */
@@ -308,6 +311,32 @@  static int uclogic_raw_event(struct hid_device *hdev,
 		    params->frame.dev_id_byte < size) {
 			data[params->frame.dev_id_byte] = 0xf;
 		}
+		/* If need to, and can, read rotary encoder state change */
+		if (params->frame.re_lsb > 0 &&
+		    params->frame.re_lsb / 8 < size) {
+			unsigned int byte = params->frame.re_lsb / 8;
+			unsigned int bit = params->frame.re_lsb % 8;
+
+			u8 change;
+			u8 prev_state = drvdata->re_state;
+			/* Read Gray-coded state */
+			u8 state = (data[byte] >> bit) & 0x3;
+			/* Encode state change into 2-bit signed integer */
+			if ((prev_state == 1 && state == 0) ||
+			    (prev_state == 2 && state == 3)) {
+				change = 1;
+			} else if ((prev_state == 2 && state == 0) ||
+				   (prev_state == 1 && state == 3)) {
+				change = 3;
+			} else {
+				change = 0;
+			}
+			/* Write change */
+			data[byte] = (data[byte] & ~((u8)3 << bit)) |
+					(change << bit);
+			/* Remember state */
+			drvdata->re_state = state;
+		}
 	}
 
 	return 0;
diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h
index 4ba6ecc2b8b8..ba48b1c7a0e5 100644
--- a/drivers/hid/hid-uclogic-params.h
+++ b/drivers/hid/hid-uclogic-params.h
@@ -87,6 +87,12 @@  struct uclogic_params_frame {
 	 * Report ID, if reports should be tweaked, zero if not.
 	 */
 	unsigned int id;
+	/*
+	 * Number of the least-significant bit of the 2-bit state of a rotary
+	 * encoder, in the report. Cannot point to a 2-bit field crossing a
+	 * byte boundary. Zero if not present. Only valid if "id" is not zero.
+	 */
+	unsigned int re_lsb;
 	/*
 	 * Offset of the Wacom-style device ID byte in the report, to be set
 	 * to pad device ID (0xf), for compatibility with Wacom drivers. Zero
@@ -168,6 +174,7 @@  extern int uclogic_params_init(struct uclogic_params *params,
 		".frame.desc_ptr = %p\n"            \
 		".frame.desc_size = %u\n"           \
 		".frame.id = %u\n"                  \
+		".frame.re_lsb = %u\n"              \
 		".frame.dev_id_byte = %u\n"         \
 		".pen_frame_flag = 0x%02x\n"
 
@@ -185,6 +192,7 @@  extern int uclogic_params_init(struct uclogic_params *params,
 		(_params)->frame.desc_ptr,                                  \
 		(_params)->frame.desc_size,                                 \
 		(_params)->frame.id,                                        \
+		(_params)->frame.re_lsb,                                    \
 		(_params)->frame.dev_id_byte,                               \
 		(_params)->pen_frame_flag