HID: uclogic: Support Gray-coded rotary encoders
authorNikolai Kondrashov <spbnick@gmail.com>
Sun, 10 Feb 2019 10:14:04 +0000 (12:14 +0200)
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>
Thu, 21 Feb 2019 11:00:54 +0000 (12:00 +0100)
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>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
drivers/hid/hid-uclogic-core.c
drivers/hid/hid-uclogic-params.h

index f5fb612..dfacb04 100644 (file)
@@ -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;
index 4ba6ecc..ba48b1c 100644 (file)
@@ -88,6 +88,12 @@ struct uclogic_params_frame {
         */
        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
         * if no changes to the report should be made. Only valid if "id" is
@@ -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