Merge branch 'for-6.1/uclogic' into for-linus
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>
Wed, 5 Oct 2022 09:34:48 +0000 (10:34 +0100)
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>
Wed, 5 Oct 2022 09:34:48 +0000 (10:34 +0100)
- Add UGEEv2 support (XP-PEN Deco Pro S and Parblo A610 PRO) (José
  Expósito)

drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-ids.h
drivers/hid/hid-uclogic-core.c
drivers/hid/hid-uclogic-params-test.c [new file with mode: 0644]
drivers/hid/hid-uclogic-params.c
drivers/hid/hid-uclogic-params.h
drivers/hid/hid-uclogic-rdesc-test.c
drivers/hid/hid-uclogic-rdesc.c
drivers/hid/hid-uclogic-rdesc.h

index b9adf71..185a077 100644 (file)
@@ -1261,7 +1261,7 @@ config HID_MCP2221
        will be called hid-mcp2221.ko.
 
 config HID_KUNIT_TEST
-       bool "KUnit tests for HID" if !KUNIT_ALL_TESTS
+       tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS
        depends on KUNIT=y
        depends on HID_UCLOGIC
        default KUNIT_ALL_TESTS
index c0615f1..e8014c1 100644 (file)
@@ -147,8 +147,10 @@ obj-$(CONFIG_HID_WIIMOTE)  += hid-wiimote.o
 obj-$(CONFIG_HID_SENSOR_HUB)   += hid-sensor-hub.o
 obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o
 
-obj-$(CONFIG_HID_KUNIT_TEST)   += hid-uclogic-rdesc.o \
+hid-uclogic-test-objs          := hid-uclogic-rdesc.o \
+                                  hid-uclogic-params.o \
                                   hid-uclogic-rdesc-test.o
+obj-$(CONFIG_HID_KUNIT_TEST)   += hid-uclogic-test.o
 
 obj-$(CONFIG_USB_HID)          += usbhid/
 obj-$(CONFIG_USB_MOUSE)                += usbhid/
index 4529fb6..da86565 100644 (file)
 #define USB_DEVICE_ID_YIYNOVA_TABLET           0x004d
 
 #define USB_VENDOR_ID_UGEE             0x28bd
+#define USB_DEVICE_ID_UGEE_PARBLO_A610_PRO     0x1903
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540   0x0075
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640   0x0094
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935
+#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S     0x0909
 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
 #define USB_DEVICE_ID_UGEE_TABLET_G5           0x0074
 #define USB_DEVICE_ID_UGEE_TABLET_EX07S                0x0071
index 47a1737..0fbc408 100644 (file)
@@ -153,6 +153,7 @@ static int uclogic_input_configured(struct hid_device *hdev,
                        suffix = "Pad";
                        break;
                case HID_DG_PEN:
+               case HID_DG_DIGITIZER:
                        suffix = "Pen";
                        break;
                case HID_CP_CONSUMER_CONTROL:
@@ -509,6 +510,8 @@ static const struct hid_device_id uclogic_devices[] = {
                                USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER,
                                USB_DEVICE_ID_UGTIZER_TABLET_GT5040) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
+                               USB_DEVICE_ID_UGEE_PARBLO_A610_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
                                USB_DEVICE_ID_UGEE_TABLET_G5) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
@@ -523,6 +526,8 @@ static const struct hid_device_id uclogic_devices[] = {
                                USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
                                USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
+                               USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
                                USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
        { }
diff --git a/drivers/hid/hid-uclogic-params-test.c b/drivers/hid/hid-uclogic-params-test.c
new file mode 100644 (file)
index 0000000..57ef5d3
--- /dev/null
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ *  HID driver for UC-Logic devices not fully compliant with HID standard
+ *
+ *  Copyright (c) 2022 José Expósito <jose.exposito89@gmail.com>
+ */
+
+#include <kunit/test.h>
+#include "./hid-uclogic-params.h"
+#include "./hid-uclogic-rdesc.h"
+
+#define MAX_STR_DESC_SIZE 14
+
+struct uclogic_parse_ugee_v2_desc_case {
+       const char *name;
+       int res;
+       const __u8 str_desc[MAX_STR_DESC_SIZE];
+       size_t str_desc_size;
+       const s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
+       enum uclogic_params_frame_type frame_type;
+};
+
+static struct uclogic_parse_ugee_v2_desc_case uclogic_parse_ugee_v2_desc_cases[] = {
+       {
+               .name = "invalid_str_desc",
+               .res = -EINVAL,
+               .str_desc = {},
+               .str_desc_size = 0,
+               .desc_params = {},
+               .frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
+       },
+       {
+               .name = "resolution_with_value_0",
+               .res = 0,
+               .str_desc = {
+                       0x0E, 0x03,
+                       0x70, 0xB2,
+                       0x10, 0x77,
+                       0x08,
+                       0x00,
+                       0xFF, 0x1F,
+                       0x00, 0x00,
+               },
+               .str_desc_size = 12,
+               .desc_params = {
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB270,
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7710,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0,
+                       [UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
+                       [UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
+               },
+               .frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
+       },
+       /* XP-PEN Deco L str_desc: Frame with 8 buttons */
+       {
+               .name = "frame_type_buttons",
+               .res = 0,
+               .str_desc = {
+                       0x0E, 0x03,
+                       0x70, 0xB2,
+                       0x10, 0x77,
+                       0x08,
+                       0x00,
+                       0xFF, 0x1F,
+                       0xD8, 0x13,
+               },
+               .str_desc_size = 12,
+               .desc_params = {
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB270,
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2320,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7710,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x1770,
+                       [UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
+                       [UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
+               },
+               .frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
+       },
+       /* PARBLO A610 PRO str_desc: Frame with 9 buttons and dial */
+       {
+               .name = "frame_type_dial",
+               .res = 0,
+               .str_desc = {
+                       0x0E, 0x03,
+                       0x96, 0xC7,
+                       0xF9, 0x7C,
+                       0x09,
+                       0x01,
+                       0xFF, 0x1F,
+                       0xD8, 0x13,
+               },
+               .str_desc_size = 12,
+               .desc_params = {
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xC796,
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2749,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7CF9,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x1899,
+                       [UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
+                       [UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x09,
+               },
+               .frame_type = UCLOGIC_PARAMS_FRAME_DIAL,
+       },
+       /* XP-PEN Deco Pro S str_desc: Frame with 8 buttons and mouse */
+       {
+               .name = "frame_type_mouse",
+               .res = 0,
+               .str_desc = {
+                       0x0E, 0x03,
+                       0xC8, 0xB3,
+                       0x34, 0x65,
+                       0x08,
+                       0x02,
+                       0xFF, 0x1F,
+                       0xD8, 0x13,
+               },
+               .str_desc_size = 12,
+               .desc_params = {
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB3C8,
+                       [UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2363,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x6534,
+                       [UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x13EC,
+                       [UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
+                       [UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
+               },
+               .frame_type = UCLOGIC_PARAMS_FRAME_MOUSE,
+       },
+};
+
+static void uclogic_parse_ugee_v2_desc_case_desc(struct uclogic_parse_ugee_v2_desc_case *t,
+                                                char *desc)
+{
+       strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(uclogic_parse_ugee_v2_desc, uclogic_parse_ugee_v2_desc_cases,
+                 uclogic_parse_ugee_v2_desc_case_desc);
+
+static void uclogic_parse_ugee_v2_desc_test(struct kunit *test)
+{
+       int res;
+       s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
+       enum uclogic_params_frame_type frame_type;
+       const struct uclogic_parse_ugee_v2_desc_case *params = test->param_value;
+
+       res = uclogic_params_parse_ugee_v2_desc(params->str_desc,
+                                               params->str_desc_size,
+                                               desc_params,
+                                               ARRAY_SIZE(desc_params),
+                                               &frame_type);
+       KUNIT_ASSERT_EQ(test, res, params->res);
+
+       if (res)
+               return;
+
+       KUNIT_EXPECT_EQ(test,
+                       params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM],
+                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM]);
+       KUNIT_EXPECT_EQ(test,
+                       params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM],
+                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM]);
+       KUNIT_EXPECT_EQ(test,
+                       params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM],
+                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM]);
+       KUNIT_EXPECT_EQ(test,
+                       params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM],
+                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM]);
+       KUNIT_EXPECT_EQ(test,
+                       params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM],
+                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM]);
+       KUNIT_EXPECT_EQ(test,
+                       params->desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM],
+                       desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM]);
+       KUNIT_EXPECT_EQ(test, params->frame_type, frame_type);
+}
+
+static struct kunit_case hid_uclogic_params_test_cases[] = {
+       KUNIT_CASE_PARAM(uclogic_parse_ugee_v2_desc_test,
+                        uclogic_parse_ugee_v2_desc_gen_params),
+       {}
+};
+
+static struct kunit_suite hid_uclogic_params_test_suite = {
+       .name = "hid_uclogic_params_test",
+       .test_cases = hid_uclogic_params_test_cases,
+};
+
+kunit_test_suite(hid_uclogic_params_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for the UC-Logic driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");
index c11fa23..34fa991 100644 (file)
@@ -1056,6 +1056,161 @@ cleanup:
        return rc;
 }
 
+/**
+ * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing
+ * pen and frame parameters returned by UGEE v2 devices.
+ *
+ * @str_desc:          String descriptor, cannot be NULL.
+ * @str_desc_size:     Size of the string descriptor.
+ * @desc_params:       Output description params list.
+ * @desc_params_size:  Size of the output description params list.
+ * @frame_type:                Output frame type.
+ *
+ * Returns:
+ *     Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc,
+                                            size_t str_desc_size,
+                                            s32 *desc_params,
+                                            size_t desc_params_size,
+                                            enum uclogic_params_frame_type *frame_type)
+{
+       s32 pen_x_lm, pen_y_lm;
+       s32 pen_x_pm, pen_y_pm;
+       s32 pen_pressure_lm;
+       s32 frame_num_buttons;
+       s32 resolution;
+
+       /* Minimum descriptor length required, maximum seen so far is 14 */
+       const int min_str_desc_size = 12;
+
+       if (!str_desc || str_desc_size < min_str_desc_size)
+               return -EINVAL;
+
+       if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)
+               return -EINVAL;
+
+       pen_x_lm = get_unaligned_le16(str_desc + 2);
+       pen_y_lm = get_unaligned_le16(str_desc + 4);
+       frame_num_buttons = str_desc[6];
+       *frame_type = str_desc[7];
+       pen_pressure_lm = get_unaligned_le16(str_desc + 8);
+
+       resolution = get_unaligned_le16(str_desc + 10);
+       if (resolution == 0) {
+               pen_x_pm = 0;
+               pen_y_pm = 0;
+       } else {
+               pen_x_pm = pen_x_lm * 1000 / resolution;
+               pen_y_pm = pen_y_lm * 1000 / resolution;
+       }
+
+       desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm;
+       desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm;
+       desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm;
+       desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm;
+       desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm;
+       desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons;
+
+       return 0;
+}
+
+/**
+ * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with
+ * buttons.
+ * @p:                 Parameters to fill in, cannot be NULL.
+ * @desc_params:       Device description params list.
+ * @desc_params_size:  Size of the description params list.
+ *
+ * Returns:
+ *     Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p,
+                                                    const s32 *desc_params,
+                                                    size_t desc_params_size)
+{
+       __u8 *rdesc_frame = NULL;
+       int rc = 0;
+
+       if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)
+               return -EINVAL;
+
+       rdesc_frame = uclogic_rdesc_template_apply(
+                               uclogic_rdesc_ugee_v2_frame_btn_template_arr,
+                               uclogic_rdesc_ugee_v2_frame_btn_template_size,
+                               desc_params, UCLOGIC_RDESC_PH_ID_NUM);
+       if (!rdesc_frame)
+               return -ENOMEM;
+
+       rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],
+                                                rdesc_frame,
+                                                uclogic_rdesc_ugee_v2_frame_btn_template_size,
+                                                UCLOGIC_RDESC_V1_FRAME_ID);
+       kfree(rdesc_frame);
+       return rc;
+}
+
+/**
+ * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a
+ * bitmap dial.
+ * @p:                 Parameters to fill in, cannot be NULL.
+ * @desc_params:       Device description params list.
+ * @desc_params_size:  Size of the description params list.
+ *
+ * Returns:
+ *     Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p,
+                                                 const s32 *desc_params,
+                                                 size_t desc_params_size)
+{
+       __u8 *rdesc_frame = NULL;
+       int rc = 0;
+
+       if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)
+               return -EINVAL;
+
+       rdesc_frame = uclogic_rdesc_template_apply(
+                               uclogic_rdesc_ugee_v2_frame_dial_template_arr,
+                               uclogic_rdesc_ugee_v2_frame_dial_template_size,
+                               desc_params, UCLOGIC_RDESC_PH_ID_NUM);
+       if (!rdesc_frame)
+               return -ENOMEM;
+
+       rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],
+                                                rdesc_frame,
+                                                uclogic_rdesc_ugee_v2_frame_dial_template_size,
+                                                UCLOGIC_RDESC_V1_FRAME_ID);
+       kfree(rdesc_frame);
+       if (rc)
+               return rc;
+
+       p->frame_list[0].bitmap_dial_byte = 7;
+       return 0;
+}
+
+/**
+ * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a
+ * mouse.
+ * @p:                 Parameters to fill in, cannot be NULL.
+ *
+ * Returns:
+ *     Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p)
+{
+       int rc = 0;
+
+       if (!p)
+               return -EINVAL;
+
+       rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],
+                                                uclogic_rdesc_ugee_v2_frame_mouse_template_arr,
+                                                uclogic_rdesc_ugee_v2_frame_mouse_template_size,
+                                                UCLOGIC_RDESC_V1_FRAME_ID);
+       return rc;
+}
+
 /**
  * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
  * discovering their parameters.
@@ -1084,9 +1239,8 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
        const int str_desc_len = 12;
        __u8 *str_desc = NULL;
        __u8 *rdesc_pen = NULL;
-       __u8 *rdesc_frame = NULL;
        s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
-       s32 resolution;
+       enum uclogic_params_frame_type frame_type;
        __u8 magic_arr[] = {
                0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
        };
@@ -1100,6 +1254,15 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
 
        iface = to_usb_interface(hdev->dev.parent);
        bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
+
+       if (bInterfaceNumber == 0) {
+               rc = uclogic_params_ugee_v2_init_frame_mouse(&p);
+               if (rc)
+                       goto cleanup;
+
+               goto output;
+       }
+
        if (bInterfaceNumber != 2) {
                uclogic_params_init_invalid(&p);
                goto output;
@@ -1128,25 +1291,13 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
                goto output;
        }
 
-       desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
-               get_unaligned_le16(str_desc + 2);
-       desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
-               get_unaligned_le16(str_desc + 4);
-       desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = str_desc[6];
-       desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
-               get_unaligned_le16(str_desc + 8);
-       resolution = get_unaligned_le16(str_desc + 10);
-       if (resolution == 0) {
-               desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
-               desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
-       } else {
-               desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
-                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
-                       resolution;
-               desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
-                       desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
-                       resolution;
-       }
+       rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len,
+                                              desc_params,
+                                              ARRAY_SIZE(desc_params),
+                                              &frame_type);
+       if (rc)
+               goto cleanup;
+
        kfree(str_desc);
        str_desc = NULL;
 
@@ -1167,24 +1318,21 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
        p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;
 
        /* Initialize the frame interface */
-       rdesc_frame = uclogic_rdesc_template_apply(
-                               uclogic_rdesc_ugee_v2_frame_btn_template_arr,
-                               uclogic_rdesc_ugee_v2_frame_btn_template_size,
-                               desc_params, ARRAY_SIZE(desc_params));
-       if (!rdesc_frame) {
-               rc = -ENOMEM;
-               goto cleanup;
+       switch (frame_type) {
+       case UCLOGIC_PARAMS_FRAME_DIAL:
+       case UCLOGIC_PARAMS_FRAME_MOUSE:
+               rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params,
+                                                           ARRAY_SIZE(desc_params));
+               break;
+       case UCLOGIC_PARAMS_FRAME_BUTTONS:
+       default:
+               rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params,
+                                                              ARRAY_SIZE(desc_params));
+               break;
        }
 
-       rc = uclogic_params_frame_init_with_desc(&p.frame_list[0],
-                                                rdesc_frame,
-                                                uclogic_rdesc_ugee_v2_frame_btn_template_size,
-                                                UCLOGIC_RDESC_V1_FRAME_ID);
-       kfree(rdesc_frame);
-       if (rc) {
-               uclogic_params_init_invalid(&p);
-               goto output;
-       }
+       if (rc)
+               goto cleanup;
 
 output:
        /* Output parameters */
@@ -1432,8 +1580,12 @@ int uclogic_params_init(struct uclogic_params *params,
                        uclogic_params_init_invalid(&p);
                }
                break;
+       case VID_PID(USB_VENDOR_ID_UGEE,
+                    USB_DEVICE_ID_UGEE_PARBLO_A610_PRO):
        case VID_PID(USB_VENDOR_ID_UGEE,
                     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):
+       case VID_PID(USB_VENDOR_ID_UGEE,
+                    USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S):
                rc = uclogic_params_ugee_v2_init(&p, hdev);
                if (rc != 0)
                        goto cleanup;
@@ -1517,3 +1669,7 @@ cleanup:
        uclogic_params_cleanup(&p);
        return rc;
 }
+
+#ifdef CONFIG_HID_KUNIT_TEST
+#include "hid-uclogic-params-test.c"
+#endif
index 5bef8da..a97477c 100644 (file)
@@ -29,6 +29,16 @@ enum uclogic_params_pen_inrange {
        UCLOGIC_PARAMS_PEN_INRANGE_NONE,
 };
 
+/* Types of frames */
+enum uclogic_params_frame_type {
+       /* Frame with buttons */
+       UCLOGIC_PARAMS_FRAME_BUTTONS = 0,
+       /* Frame with buttons and a dial */
+       UCLOGIC_PARAMS_FRAME_DIAL,
+       /* Frame with buttons and a mouse (shaped as a dial + touchpad) */
+       UCLOGIC_PARAMS_FRAME_MOUSE,
+};
+
 /*
  * Pen report's subreport data.
  */
index ebebffe..3971a08 100644 (file)
@@ -97,7 +97,7 @@ static const __u8 template_params_none[] = {
 
 static struct uclogic_template_case uclogic_template_cases[] = {
        {
-               .name = "Empty template",
+               .name = "empty_template",
                .template = template_empty,
                .template_size = sizeof(template_empty),
                .param_list = params_pen_all,
@@ -105,7 +105,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = template_empty,
        },
        {
-               .name = "Template smaller than the placeholder",
+               .name = "template_smaller_than_the_placeholder",
                .template = template_small,
                .template_size = sizeof(template_small),
                .param_list = params_pen_all,
@@ -113,7 +113,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = template_small,
        },
        {
-               .name = "No placeholder",
+               .name = "no_placeholder",
                .template = template_no_ph,
                .template_size = sizeof(template_no_ph),
                .param_list = params_pen_all,
@@ -121,7 +121,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = template_no_ph,
        },
        {
-               .name = "Pen placeholder at the end, without ID",
+               .name = "pen_placeholder_at_the_end_without_id",
                .template = template_pen_ph_end,
                .template_size = sizeof(template_pen_ph_end),
                .param_list = params_pen_all,
@@ -129,7 +129,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = template_pen_ph_end,
        },
        {
-               .name = "Frame button placeholder at the end, without ID",
+               .name = "frame_button_placeholder_at_the_end_without_id",
                .template = template_btn_ph_end,
                .template_size = sizeof(template_btn_ph_end),
                .param_list = params_frame_all,
@@ -137,7 +137,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = template_btn_ph_end,
        },
        {
-               .name = "All params present in the pen template",
+               .name = "all_params_present_in_the_pen_template",
                .template = template_pen_all_params,
                .template_size = sizeof(template_pen_all_params),
                .param_list = params_pen_all,
@@ -145,7 +145,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = expected_pen_all_params,
        },
        {
-               .name = "All params present in the frame template",
+               .name = "all_params_present_in_the_frame_template",
                .template = template_frame_all_params,
                .template_size = sizeof(template_frame_all_params),
                .param_list = params_frame_all,
@@ -153,7 +153,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = expected_frame_all_params,
        },
        {
-               .name = "Some params present in the pen template (complete param list)",
+               .name = "some_params_present_in_the_pen_template_with_complete_param_list",
                .template = template_pen_some_params,
                .template_size = sizeof(template_pen_some_params),
                .param_list = params_pen_all,
@@ -161,7 +161,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = expected_pen_some_params,
        },
        {
-               .name = "Some params present in the pen template (incomplete param list)",
+               .name = "some_params_present_in_the_pen_template_with_incomplete_param_list",
                .template = template_pen_some_params,
                .template_size = sizeof(template_pen_some_params),
                .param_list = params_pen_some,
@@ -169,7 +169,7 @@ static struct uclogic_template_case uclogic_template_cases[] = {
                .expected = expected_pen_some_params,
        },
        {
-               .name = "No params present in the template",
+               .name = "no_params_present_in_the_template",
                .template = template_params_none,
                .template_size = sizeof(template_params_none),
                .param_list = params_pen_some,
@@ -208,7 +208,7 @@ static struct kunit_case hid_uclogic_rdesc_test_cases[] = {
 };
 
 static struct kunit_suite hid_uclogic_rdesc_test_suite = {
-       .name = "hid-uclogic-rdesc-test",
+       .name = "hid_uclogic_rdesc_test",
        .test_cases = hid_uclogic_rdesc_test_cases,
 };
 
index 3d68e8b..4bd54c4 100644 (file)
@@ -961,6 +961,80 @@ const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[] = {
 const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size =
                        sizeof(uclogic_rdesc_ugee_v2_frame_btn_template_arr);
 
+/* Fixed report descriptor template for UGEE v2 frame reports (dial) */
+const __u8 uclogic_rdesc_ugee_v2_frame_dial_template_arr[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),                   */
+       0x09, 0x07,         /*  Usage (Keypad),                         */
+       0xA1, 0x01,         /*  Collection (Application),               */
+       0x85, UCLOGIC_RDESC_V1_FRAME_ID,
+                           /*      Report ID,                          */
+       0x05, 0x0D,         /*      Usage Page (Digitizer),             */
+       0x09, 0x39,         /*      Usage (Tablet Function Keys),       */
+       0xA0,               /*      Collection (Physical),              */
+       0x75, 0x01,         /*          Report Size (1),                */
+       0x95, 0x08,         /*          Report Count (8),               */
+       0x81, 0x01,         /*          Input (Constant),               */
+       0x05, 0x09,         /*          Usage Page (Button),            */
+       0x19, 0x01,         /*          Usage Minimum (01h),            */
+       UCLOGIC_RDESC_FRAME_PH_BTN,
+                           /*          Usage Maximum (PLACEHOLDER),    */
+       0x95, 0x0A,         /*          Report Count (10),              */
+       0x14,               /*          Logical Minimum (0),            */
+       0x25, 0x01,         /*          Logical Maximum (1),            */
+       0x81, 0x02,         /*          Input (Variable),               */
+       0x95, 0x06,         /*          Report Count (6),               */
+       0x81, 0x01,         /*          Input (Constant),               */
+       0x75, 0x08,         /*          Report Size (8),                */
+       0x95, 0x03,         /*          Report Count (3),               */
+       0x81, 0x01,         /*          Input (Constant),               */
+       0x05, 0x01,         /*          Usage Page (Desktop),           */
+       0x09, 0x38,         /*          Usage (Wheel),                  */
+       0x95, 0x01,         /*          Report Count (1),               */
+       0x15, 0xFF,         /*          Logical Minimum (-1),           */
+       0x25, 0x01,         /*          Logical Maximum (1),            */
+       0x81, 0x06,         /*          Input (Variable, Relative),     */
+       0x95, 0x02,         /*          Report Count (2),               */
+       0x81, 0x01,         /*          Input (Constant),               */
+       0xC0,               /*      End Collection,                     */
+       0xC0                /*  End Collection                          */
+};
+const size_t uclogic_rdesc_ugee_v2_frame_dial_template_size =
+                       sizeof(uclogic_rdesc_ugee_v2_frame_dial_template_arr);
+
+/* Fixed report descriptor template for UGEE v2 frame reports (mouse) */
+const __u8 uclogic_rdesc_ugee_v2_frame_mouse_template_arr[] = {
+       0x05, 0x01,         /*  Usage Page (Desktop),                   */
+       0x09, 0x02,         /*  Usage (Mouse),                          */
+       0xA1, 0x01,         /*  Collection (Application),               */
+       0x85, 0x01,         /*      Report ID (1),                      */
+       0x05, 0x01,         /*      Usage Page (Pointer),               */
+       0xA0,               /*      Collection (Physical),              */
+       0x75, 0x01,         /*          Report Size (1),                */
+       0x95, 0x02,         /*          Report Count (2),               */
+       0x05, 0x09,         /*          Usage Page (Button),            */
+       0x19, 0x01,         /*          Usage Minimum (01h),            */
+       0x29, 0x02,         /*          Usage Maximum (02h),            */
+       0x14,               /*          Logical Minimum (0),            */
+       0x25, 0x01,         /*          Logical Maximum (1),            */
+       0x81, 0x02,         /*          Input (Variable),               */
+       0x95, 0x06,         /*          Report Count (6),               */
+       0x81, 0x01,         /*          Input (Constant),               */
+       0x05, 0x01,         /*          Usage Page (Generic Desktop),   */
+       0x09, 0x30,         /*          Usage (X),                      */
+       0x09, 0x31,         /*          Usage (Y),                      */
+       0x75, 0x10,         /*          Report Size (16),               */
+       0x95, 0x02,         /*          Report Count (2),               */
+       0x16, 0x00, 0x80,   /*          Logical Minimum (-32768),       */
+       0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),        */
+       0x81, 0x06,         /*          Input (Variable, Relative),     */
+       0x95, 0x01,         /*          Report Count (1),               */
+       0x81, 0x01,         /*          Input (Constant),               */
+       0xC0,               /*      End Collection,                     */
+       0xC0                /*  End Collection                          */
+};
+const size_t uclogic_rdesc_ugee_v2_frame_mouse_template_size =
+                       sizeof(uclogic_rdesc_ugee_v2_frame_mouse_template_arr);
+
 /* Fixed report descriptor for Ugee EX07 frame */
 const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
        0x05, 0x01,             /*  Usage Page (Desktop),                   */
@@ -1113,7 +1187,7 @@ __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr,
                    memcmp(p, pen_head, sizeof(pen_head)) == 0 &&
                    p[sizeof(pen_head)] < param_num) {
                        v = param_list[p[sizeof(pen_head)]];
-                       put_unaligned(cpu_to_le32(v), (s32 *)p);
+                       put_unaligned((__force u32)cpu_to_le32(v), (s32 *)p);
                        p += sizeof(pen_head) + 1;
                } else if (memcmp(p, btn_head, sizeof(btn_head)) == 0 &&
                           p[sizeof(btn_head)] < param_num) {
index 86e64a9..0502a06 100644 (file)
@@ -169,6 +169,14 @@ extern const size_t uclogic_rdesc_ugee_v2_pen_template_size;
 extern const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[];
 extern const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size;
 
+/* Fixed report descriptor template for UGEE v2 frame reports (dial) */
+extern const __u8 uclogic_rdesc_ugee_v2_frame_dial_template_arr[];
+extern const size_t uclogic_rdesc_ugee_v2_frame_dial_template_size;
+
+/* Fixed report descriptor template for UGEE v2 frame reports (mouse) */
+extern const __u8 uclogic_rdesc_ugee_v2_frame_mouse_template_arr[];
+extern const size_t uclogic_rdesc_ugee_v2_frame_mouse_template_size;
+
 /* Fixed report descriptor for Ugee EX07 frame */
 extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[];
 extern const size_t uclogic_rdesc_ugee_ex07_frame_size;