mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
![José Expósito](/assets/img/avatar_default.png)
UGEEv2 tablets with battery can be connected using a USB cable or a USB Bluetooth dongle. When the Bluetooth dongle is used, the connection to that tablet can be lost because the tablet is out of the range of the receiver or because it was switched off using the switch placed in the back of the tablet's frame. After losing connection, the tablet is able to reconnect automatically and its firmware sends a special packet indicating that the device was reconnected. In response to this packet, the tablet needs to receive the same array of magic data it expects on probe to enable its interfaces. This patch implements a generic mechanism to hook raw events and schedule a work to perform any custom action. Tested-by: Mia Kanashi <chad@redpilled.dev> Tested-by: Andreas Grosse <andig.mail@t-online.de> Signed-off-by: José Expósito <jose.exposito89@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
208 lines
5.5 KiB
C
208 lines
5.5 KiB
C
// 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 hid_test_uclogic_parse_ugee_v2_desc(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 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),
|
|
{}
|
|
};
|
|
|
|
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>");
|