Loading drivers/hid/hid-ids.h +2 −0 Original line number Diff line number Diff line Loading @@ -1304,7 +1304,9 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2 0x0905 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW 0x0934 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S 0x0909 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW 0x0933 #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 Loading drivers/hid/hid-input.c +4 −0 Original line number Diff line number Diff line Loading @@ -378,6 +378,10 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L), HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW), HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW), HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15), HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100), Loading drivers/hid/hid-uclogic-core-test.c 0 → 100644 +105 −0 Original line number Diff line number Diff line // 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" #define MAX_EVENT_SIZE 12 struct uclogic_raw_event_hook_test { u8 event[MAX_EVENT_SIZE]; size_t size; bool expected; }; static struct uclogic_raw_event_hook_test hook_events[] = { { .event = { 0xA1, 0xB2, 0xC3, 0xD4 }, .size = 4, }, { .event = { 0x1F, 0x2E, 0x3D, 0x4C, 0x5B, 0x6A }, .size = 6, }, }; static struct uclogic_raw_event_hook_test test_events[] = { { .event = { 0xA1, 0xB2, 0xC3, 0xD4 }, .size = 4, .expected = true, }, { .event = { 0x1F, 0x2E, 0x3D, 0x4C, 0x5B, 0x6A }, .size = 6, .expected = true, }, { .event = { 0xA1, 0xB2, 0xC3 }, .size = 3, .expected = false, }, { .event = { 0xA1, 0xB2, 0xC3, 0xD4, 0x00 }, .size = 5, .expected = false, }, { .event = { 0x2E, 0x3D, 0x4C, 0x5B, 0x6A, 0x1F }, .size = 6, .expected = false, }, }; static void hid_test_uclogic_exec_event_hook_test(struct kunit *test) { struct uclogic_params p = {0, }; struct uclogic_raw_event_hook *filter; bool res; int n; /* Initialize the list of events to hook */ p.event_hooks = kunit_kzalloc(test, sizeof(*p.event_hooks), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p.event_hooks); INIT_LIST_HEAD(&p.event_hooks->list); for (n = 0; n < ARRAY_SIZE(hook_events); n++) { filter = kunit_kzalloc(test, sizeof(*filter), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filter); filter->size = hook_events[n].size; filter->event = kunit_kzalloc(test, filter->size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filter->event); memcpy(filter->event, &hook_events[n].event[0], filter->size); list_add_tail(&filter->list, &p.event_hooks->list); } /* Test uclogic_exec_event_hook() */ for (n = 0; n < ARRAY_SIZE(test_events); n++) { res = uclogic_exec_event_hook(&p, &test_events[n].event[0], test_events[n].size); KUNIT_ASSERT_EQ(test, res, test_events[n].expected); } } static struct kunit_case hid_uclogic_core_test_cases[] = { KUNIT_CASE(hid_test_uclogic_exec_event_hook_test), {} }; static struct kunit_suite hid_uclogic_core_test_suite = { .name = "hid_uclogic_core_test", .test_cases = hid_uclogic_core_test_cases, }; kunit_test_suite(hid_uclogic_core_test_suite); MODULE_DESCRIPTION("KUnit tests for the UC-Logic driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>"); drivers/hid/hid-uclogic-core.c +42 −19 Original line number Diff line number Diff line Loading @@ -22,25 +22,6 @@ #include "hid-ids.h" /* Driver data */ struct uclogic_drvdata { /* Interface parameters */ struct uclogic_params params; /* Pointer to the replacement report descriptor. NULL if none. */ __u8 *desc_ptr; /* * Size of the replacement report descriptor. * Only valid if desc_ptr is not NULL */ unsigned int desc_size; /* Pen input device */ 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; }; /** * uclogic_inrange_timeout - handle pen in-range state timeout. * Emulate input events normally generated when pen goes out of range for Loading Loading @@ -202,6 +183,7 @@ static int uclogic_probe(struct hid_device *hdev, } timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0); drvdata->re_state = U8_MAX; drvdata->quirks = id->driver_data; hid_set_drvdata(hdev, drvdata); /* Initialize the device and retrieve interface parameters */ Loading Loading @@ -267,6 +249,34 @@ static int uclogic_resume(struct hid_device *hdev) } #endif /** * uclogic_exec_event_hook - if the received event is hooked schedules the * associated work. * * @p: Tablet interface report parameters. * @event: Raw event. * @size: The size of event. * * Returns: * Whether the event was hooked or not. */ static bool uclogic_exec_event_hook(struct uclogic_params *p, u8 *event, int size) { struct uclogic_raw_event_hook *curr; if (!p->event_hooks) return false; list_for_each_entry(curr, &p->event_hooks->list, list) { if (curr->size == size && memcmp(curr->event, event, size) == 0) { schedule_work(&curr->work); return true; } } return false; } /** * uclogic_raw_event_pen - handle raw pen events (pen HID reports). * Loading Loading @@ -425,6 +435,9 @@ static int uclogic_raw_event(struct hid_device *hdev, if (report->type != HID_INPUT_REPORT) return 0; if (uclogic_exec_event_hook(params, data, size)) return 0; while (true) { /* Tweak pen reports, if necessary */ if ((report_id == params->pen.id) && (size >= 2)) { Loading Loading @@ -529,8 +542,14 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2) }, { 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_MW), .driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK }, { 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_DECO_PRO_SW), .driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, { } Loading @@ -556,3 +575,7 @@ module_hid_driver(uclogic_driver); MODULE_AUTHOR("Martin Rusko"); MODULE_AUTHOR("Nikolai Kondrashov"); MODULE_LICENSE("GPL"); #ifdef CONFIG_HID_KUNIT_TEST #include "hid-uclogic-core-test.c" #endif drivers/hid/hid-uclogic-params-test.c +16 −0 Original line number Diff line number Diff line Loading @@ -174,9 +174,25 @@ static void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test) KUNIT_EXPECT_EQ(test, params->frame_type, frame_type); } static void hid_test_uclogic_params_cleanup_event_hooks(struct kunit *test) { int res, n; struct uclogic_params p = {0, }; res = uclogic_params_ugee_v2_init_event_hooks(NULL, &p); KUNIT_ASSERT_EQ(test, res, 0); /* Check that the function can be called repeatedly */ for (n = 0; n < 4; n++) { uclogic_params_cleanup_event_hooks(&p); KUNIT_EXPECT_PTR_EQ(test, p.event_hooks, NULL); } } static struct kunit_case hid_uclogic_params_test_cases[] = { KUNIT_CASE_PARAM(hid_test_uclogic_parse_ugee_v2_desc, uclogic_parse_ugee_v2_desc_gen_params), KUNIT_CASE(hid_test_uclogic_params_cleanup_event_hooks), {} }; Loading Loading
drivers/hid/hid-ids.h +2 −0 Original line number Diff line number Diff line Loading @@ -1304,7 +1304,9 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2 0x0905 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW 0x0934 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S 0x0909 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW 0x0933 #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 Loading
drivers/hid/hid-input.c +4 −0 Original line number Diff line number Diff line Loading @@ -378,6 +378,10 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L), HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW), HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW), HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15), HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100), Loading
drivers/hid/hid-uclogic-core-test.c 0 → 100644 +105 −0 Original line number Diff line number Diff line // 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" #define MAX_EVENT_SIZE 12 struct uclogic_raw_event_hook_test { u8 event[MAX_EVENT_SIZE]; size_t size; bool expected; }; static struct uclogic_raw_event_hook_test hook_events[] = { { .event = { 0xA1, 0xB2, 0xC3, 0xD4 }, .size = 4, }, { .event = { 0x1F, 0x2E, 0x3D, 0x4C, 0x5B, 0x6A }, .size = 6, }, }; static struct uclogic_raw_event_hook_test test_events[] = { { .event = { 0xA1, 0xB2, 0xC3, 0xD4 }, .size = 4, .expected = true, }, { .event = { 0x1F, 0x2E, 0x3D, 0x4C, 0x5B, 0x6A }, .size = 6, .expected = true, }, { .event = { 0xA1, 0xB2, 0xC3 }, .size = 3, .expected = false, }, { .event = { 0xA1, 0xB2, 0xC3, 0xD4, 0x00 }, .size = 5, .expected = false, }, { .event = { 0x2E, 0x3D, 0x4C, 0x5B, 0x6A, 0x1F }, .size = 6, .expected = false, }, }; static void hid_test_uclogic_exec_event_hook_test(struct kunit *test) { struct uclogic_params p = {0, }; struct uclogic_raw_event_hook *filter; bool res; int n; /* Initialize the list of events to hook */ p.event_hooks = kunit_kzalloc(test, sizeof(*p.event_hooks), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, p.event_hooks); INIT_LIST_HEAD(&p.event_hooks->list); for (n = 0; n < ARRAY_SIZE(hook_events); n++) { filter = kunit_kzalloc(test, sizeof(*filter), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filter); filter->size = hook_events[n].size; filter->event = kunit_kzalloc(test, filter->size, GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filter->event); memcpy(filter->event, &hook_events[n].event[0], filter->size); list_add_tail(&filter->list, &p.event_hooks->list); } /* Test uclogic_exec_event_hook() */ for (n = 0; n < ARRAY_SIZE(test_events); n++) { res = uclogic_exec_event_hook(&p, &test_events[n].event[0], test_events[n].size); KUNIT_ASSERT_EQ(test, res, test_events[n].expected); } } static struct kunit_case hid_uclogic_core_test_cases[] = { KUNIT_CASE(hid_test_uclogic_exec_event_hook_test), {} }; static struct kunit_suite hid_uclogic_core_test_suite = { .name = "hid_uclogic_core_test", .test_cases = hid_uclogic_core_test_cases, }; kunit_test_suite(hid_uclogic_core_test_suite); MODULE_DESCRIPTION("KUnit tests for the UC-Logic driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");
drivers/hid/hid-uclogic-core.c +42 −19 Original line number Diff line number Diff line Loading @@ -22,25 +22,6 @@ #include "hid-ids.h" /* Driver data */ struct uclogic_drvdata { /* Interface parameters */ struct uclogic_params params; /* Pointer to the replacement report descriptor. NULL if none. */ __u8 *desc_ptr; /* * Size of the replacement report descriptor. * Only valid if desc_ptr is not NULL */ unsigned int desc_size; /* Pen input device */ 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; }; /** * uclogic_inrange_timeout - handle pen in-range state timeout. * Emulate input events normally generated when pen goes out of range for Loading Loading @@ -202,6 +183,7 @@ static int uclogic_probe(struct hid_device *hdev, } timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0); drvdata->re_state = U8_MAX; drvdata->quirks = id->driver_data; hid_set_drvdata(hdev, drvdata); /* Initialize the device and retrieve interface parameters */ Loading Loading @@ -267,6 +249,34 @@ static int uclogic_resume(struct hid_device *hdev) } #endif /** * uclogic_exec_event_hook - if the received event is hooked schedules the * associated work. * * @p: Tablet interface report parameters. * @event: Raw event. * @size: The size of event. * * Returns: * Whether the event was hooked or not. */ static bool uclogic_exec_event_hook(struct uclogic_params *p, u8 *event, int size) { struct uclogic_raw_event_hook *curr; if (!p->event_hooks) return false; list_for_each_entry(curr, &p->event_hooks->list, list) { if (curr->size == size && memcmp(curr->event, event, size) == 0) { schedule_work(&curr->work); return true; } } return false; } /** * uclogic_raw_event_pen - handle raw pen events (pen HID reports). * Loading Loading @@ -425,6 +435,9 @@ static int uclogic_raw_event(struct hid_device *hdev, if (report->type != HID_INPUT_REPORT) return 0; if (uclogic_exec_event_hook(params, data, size)) return 0; while (true) { /* Tweak pen reports, if necessary */ if ((report_id == params->pen.id) && (size >= 2)) { Loading Loading @@ -529,8 +542,14 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2) }, { 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_MW), .driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK }, { 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_DECO_PRO_SW), .driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, { } Loading @@ -556,3 +575,7 @@ module_hid_driver(uclogic_driver); MODULE_AUTHOR("Martin Rusko"); MODULE_AUTHOR("Nikolai Kondrashov"); MODULE_LICENSE("GPL"); #ifdef CONFIG_HID_KUNIT_TEST #include "hid-uclogic-core-test.c" #endif
drivers/hid/hid-uclogic-params-test.c +16 −0 Original line number Diff line number Diff line Loading @@ -174,9 +174,25 @@ static void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test) KUNIT_EXPECT_EQ(test, params->frame_type, frame_type); } static void hid_test_uclogic_params_cleanup_event_hooks(struct kunit *test) { int res, n; struct uclogic_params p = {0, }; res = uclogic_params_ugee_v2_init_event_hooks(NULL, &p); KUNIT_ASSERT_EQ(test, res, 0); /* Check that the function can be called repeatedly */ for (n = 0; n < 4; n++) { uclogic_params_cleanup_event_hooks(&p); KUNIT_EXPECT_PTR_EQ(test, p.event_hooks, NULL); } } static struct kunit_case hid_uclogic_params_test_cases[] = { KUNIT_CASE_PARAM(hid_test_uclogic_parse_ugee_v2_desc, uclogic_parse_ugee_v2_desc_gen_params), KUNIT_CASE(hid_test_uclogic_params_cleanup_event_hooks), {} }; Loading