1a64cbf3cSJosé Expósito // SPDX-License-Identifier: GPL-2.0+
2a64cbf3cSJosé Expósito 
3a64cbf3cSJosé Expósito /*
4a64cbf3cSJosé Expósito  *  HID driver for UC-Logic devices not fully compliant with HID standard
5a64cbf3cSJosé Expósito  *
6a64cbf3cSJosé Expósito  *  Copyright (c) 2022 José Expósito <jose.exposito89@gmail.com>
7a64cbf3cSJosé Expósito  */
8a64cbf3cSJosé Expósito 
9a64cbf3cSJosé Expósito #include <kunit/test.h>
10a092986fSJosé Expósito #include "./hid-uclogic-params.h"
11a64cbf3cSJosé Expósito #include "./hid-uclogic-rdesc.h"
12a64cbf3cSJosé Expósito 
13a64cbf3cSJosé Expósito #define MAX_STR_DESC_SIZE 14
14a64cbf3cSJosé Expósito 
15a64cbf3cSJosé Expósito struct uclogic_parse_ugee_v2_desc_case {
16a64cbf3cSJosé Expósito 	const char *name;
17a64cbf3cSJosé Expósito 	int res;
18a64cbf3cSJosé Expósito 	const __u8 str_desc[MAX_STR_DESC_SIZE];
19a64cbf3cSJosé Expósito 	size_t str_desc_size;
20a64cbf3cSJosé Expósito 	const s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
21a092986fSJosé Expósito 	enum uclogic_params_frame_type frame_type;
22a64cbf3cSJosé Expósito };
23a64cbf3cSJosé Expósito 
24a64cbf3cSJosé Expósito static struct uclogic_parse_ugee_v2_desc_case uclogic_parse_ugee_v2_desc_cases[] = {
25a64cbf3cSJosé Expósito 	{
26a64cbf3cSJosé Expósito 		.name = "invalid_str_desc",
27a64cbf3cSJosé Expósito 		.res = -EINVAL,
28a64cbf3cSJosé Expósito 		.str_desc = {},
29a64cbf3cSJosé Expósito 		.str_desc_size = 0,
30a64cbf3cSJosé Expósito 		.desc_params = {},
31a092986fSJosé Expósito 		.frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
32a64cbf3cSJosé Expósito 	},
33a64cbf3cSJosé Expósito 	{
34a64cbf3cSJosé Expósito 		.name = "resolution_with_value_0",
35a64cbf3cSJosé Expósito 		.res = 0,
36a64cbf3cSJosé Expósito 		.str_desc = {
37a64cbf3cSJosé Expósito 			0x0E, 0x03,
38a64cbf3cSJosé Expósito 			0x70, 0xB2,
39a64cbf3cSJosé Expósito 			0x10, 0x77,
40a64cbf3cSJosé Expósito 			0x08,
41a64cbf3cSJosé Expósito 			0x00,
42a64cbf3cSJosé Expósito 			0xFF, 0x1F,
43a64cbf3cSJosé Expósito 			0x00, 0x00,
44a64cbf3cSJosé Expósito 		},
45a64cbf3cSJosé Expósito 		.str_desc_size = 12,
46a64cbf3cSJosé Expósito 		.desc_params = {
47a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB270,
48a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0,
49a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7710,
50a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0,
51a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
52a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
53a64cbf3cSJosé Expósito 		},
54a092986fSJosé Expósito 		.frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
55a64cbf3cSJosé Expósito 	},
56a64cbf3cSJosé Expósito 	/* XP-PEN Deco L str_desc: Frame with 8 buttons */
57a64cbf3cSJosé Expósito 	{
58a64cbf3cSJosé Expósito 		.name = "frame_type_buttons",
59a64cbf3cSJosé Expósito 		.res = 0,
60a64cbf3cSJosé Expósito 		.str_desc = {
61a64cbf3cSJosé Expósito 			0x0E, 0x03,
62a64cbf3cSJosé Expósito 			0x70, 0xB2,
63a64cbf3cSJosé Expósito 			0x10, 0x77,
64a64cbf3cSJosé Expósito 			0x08,
65a64cbf3cSJosé Expósito 			0x00,
66a64cbf3cSJosé Expósito 			0xFF, 0x1F,
67a64cbf3cSJosé Expósito 			0xD8, 0x13,
68a64cbf3cSJosé Expósito 		},
69a64cbf3cSJosé Expósito 		.str_desc_size = 12,
70a64cbf3cSJosé Expósito 		.desc_params = {
71a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB270,
72a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2320,
73a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7710,
74a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x1770,
75a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
76a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
77a64cbf3cSJosé Expósito 		},
78a092986fSJosé Expósito 		.frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
79a64cbf3cSJosé Expósito 	},
80a64cbf3cSJosé Expósito 	/* PARBLO A610 PRO str_desc: Frame with 9 buttons and dial */
81a64cbf3cSJosé Expósito 	{
82a64cbf3cSJosé Expósito 		.name = "frame_type_dial",
83a64cbf3cSJosé Expósito 		.res = 0,
84a64cbf3cSJosé Expósito 		.str_desc = {
85a64cbf3cSJosé Expósito 			0x0E, 0x03,
86a64cbf3cSJosé Expósito 			0x96, 0xC7,
87a64cbf3cSJosé Expósito 			0xF9, 0x7C,
88a64cbf3cSJosé Expósito 			0x09,
89a64cbf3cSJosé Expósito 			0x01,
90a64cbf3cSJosé Expósito 			0xFF, 0x1F,
91a64cbf3cSJosé Expósito 			0xD8, 0x13,
92a64cbf3cSJosé Expósito 		},
93a64cbf3cSJosé Expósito 		.str_desc_size = 12,
94a64cbf3cSJosé Expósito 		.desc_params = {
95a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xC796,
96a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2749,
97a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7CF9,
98a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x1899,
99a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
100a64cbf3cSJosé Expósito 			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x09,
101a64cbf3cSJosé Expósito 		},
102a092986fSJosé Expósito 		.frame_type = UCLOGIC_PARAMS_FRAME_DIAL,
103a092986fSJosé Expósito 	},
104a092986fSJosé Expósito 	/* XP-PEN Deco Pro S str_desc: Frame with 8 buttons and mouse */
105a092986fSJosé Expósito 	{
106a092986fSJosé Expósito 		.name = "frame_type_mouse",
107a092986fSJosé Expósito 		.res = 0,
108a092986fSJosé Expósito 		.str_desc = {
109a092986fSJosé Expósito 			0x0E, 0x03,
110a092986fSJosé Expósito 			0xC8, 0xB3,
111a092986fSJosé Expósito 			0x34, 0x65,
112a092986fSJosé Expósito 			0x08,
113a092986fSJosé Expósito 			0x02,
114a092986fSJosé Expósito 			0xFF, 0x1F,
115a092986fSJosé Expósito 			0xD8, 0x13,
116a092986fSJosé Expósito 		},
117a092986fSJosé Expósito 		.str_desc_size = 12,
118a092986fSJosé Expósito 		.desc_params = {
119a092986fSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB3C8,
120a092986fSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2363,
121a092986fSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x6534,
122a092986fSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x13EC,
123a092986fSJosé Expósito 			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
124a092986fSJosé Expósito 			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
125a092986fSJosé Expósito 		},
126a092986fSJosé Expósito 		.frame_type = UCLOGIC_PARAMS_FRAME_MOUSE,
127a64cbf3cSJosé Expósito 	},
128a64cbf3cSJosé Expósito };
129a64cbf3cSJosé Expósito 
uclogic_parse_ugee_v2_desc_case_desc(struct uclogic_parse_ugee_v2_desc_case * t,char * desc)130a64cbf3cSJosé Expósito static void uclogic_parse_ugee_v2_desc_case_desc(struct uclogic_parse_ugee_v2_desc_case *t,
131a64cbf3cSJosé Expósito 						 char *desc)
132a64cbf3cSJosé Expósito {
133a64cbf3cSJosé Expósito 	strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
134a64cbf3cSJosé Expósito }
135a64cbf3cSJosé Expósito 
136a64cbf3cSJosé Expósito KUNIT_ARRAY_PARAM(uclogic_parse_ugee_v2_desc, uclogic_parse_ugee_v2_desc_cases,
137a64cbf3cSJosé Expósito 		  uclogic_parse_ugee_v2_desc_case_desc);
138a64cbf3cSJosé Expósito 
hid_test_uclogic_parse_ugee_v2_desc(struct kunit * test)139af89dfdeSJosé Expósito static void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test)
140a64cbf3cSJosé Expósito {
141a64cbf3cSJosé Expósito 	int res;
142a64cbf3cSJosé Expósito 	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
143a092986fSJosé Expósito 	enum uclogic_params_frame_type frame_type;
144a64cbf3cSJosé Expósito 	const struct uclogic_parse_ugee_v2_desc_case *params = test->param_value;
145a64cbf3cSJosé Expósito 
146a64cbf3cSJosé Expósito 	res = uclogic_params_parse_ugee_v2_desc(params->str_desc,
147a64cbf3cSJosé Expósito 						params->str_desc_size,
148a64cbf3cSJosé Expósito 						desc_params,
149a092986fSJosé Expósito 						ARRAY_SIZE(desc_params),
150a092986fSJosé Expósito 						&frame_type);
151a64cbf3cSJosé Expósito 	KUNIT_ASSERT_EQ(test, res, params->res);
152a64cbf3cSJosé Expósito 
153a64cbf3cSJosé Expósito 	if (res)
154a64cbf3cSJosé Expósito 		return;
155a64cbf3cSJosé Expósito 
156a64cbf3cSJosé Expósito 	KUNIT_EXPECT_EQ(test,
157a64cbf3cSJosé Expósito 			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM],
158a64cbf3cSJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM]);
159a64cbf3cSJosé Expósito 	KUNIT_EXPECT_EQ(test,
160a64cbf3cSJosé Expósito 			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM],
161a64cbf3cSJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM]);
162a64cbf3cSJosé Expósito 	KUNIT_EXPECT_EQ(test,
163a64cbf3cSJosé Expósito 			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM],
164a64cbf3cSJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM]);
165a64cbf3cSJosé Expósito 	KUNIT_EXPECT_EQ(test,
166a64cbf3cSJosé Expósito 			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM],
167a64cbf3cSJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM]);
168a64cbf3cSJosé Expósito 	KUNIT_EXPECT_EQ(test,
169a64cbf3cSJosé Expósito 			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM],
170a64cbf3cSJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM]);
171a64cbf3cSJosé Expósito 	KUNIT_EXPECT_EQ(test,
172a64cbf3cSJosé Expósito 			params->desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM],
173a64cbf3cSJosé Expósito 			desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM]);
174a092986fSJosé Expósito 	KUNIT_EXPECT_EQ(test, params->frame_type, frame_type);
175a64cbf3cSJosé Expósito }
176a64cbf3cSJosé Expósito 
177*6c8f9537SJinjie Ruan struct fake_device {
178*6c8f9537SJinjie Ruan 	unsigned long quirks;
179*6c8f9537SJinjie Ruan };
180*6c8f9537SJinjie Ruan 
hid_test_uclogic_params_cleanup_event_hooks(struct kunit * test)181a251d657SJosé Expósito static void hid_test_uclogic_params_cleanup_event_hooks(struct kunit *test)
182a251d657SJosé Expósito {
183a251d657SJosé Expósito 	int res, n;
184*6c8f9537SJinjie Ruan 	struct hid_device *hdev;
185*6c8f9537SJinjie Ruan 	struct fake_device *fake_dev;
186a251d657SJosé Expósito 	struct uclogic_params p = {0, };
187a251d657SJosé Expósito 
188*6c8f9537SJinjie Ruan 	hdev = kunit_kzalloc(test, sizeof(struct hid_device), GFP_KERNEL);
189*6c8f9537SJinjie Ruan 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdev);
190*6c8f9537SJinjie Ruan 
191*6c8f9537SJinjie Ruan 	fake_dev = kunit_kzalloc(test, sizeof(struct fake_device), GFP_KERNEL);
192*6c8f9537SJinjie Ruan 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_dev);
193*6c8f9537SJinjie Ruan 
194*6c8f9537SJinjie Ruan 	hid_set_drvdata(hdev, fake_dev);
195*6c8f9537SJinjie Ruan 
196*6c8f9537SJinjie Ruan 	res = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);
197a251d657SJosé Expósito 	KUNIT_ASSERT_EQ(test, res, 0);
198a251d657SJosé Expósito 
199a251d657SJosé Expósito 	/* Check that the function can be called repeatedly */
200a251d657SJosé Expósito 	for (n = 0; n < 4; n++) {
201a251d657SJosé Expósito 		uclogic_params_cleanup_event_hooks(&p);
202a251d657SJosé Expósito 		KUNIT_EXPECT_PTR_EQ(test, p.event_hooks, NULL);
203a251d657SJosé Expósito 	}
204a251d657SJosé Expósito }
205a251d657SJosé Expósito 
206a64cbf3cSJosé Expósito static struct kunit_case hid_uclogic_params_test_cases[] = {
207af89dfdeSJosé Expósito 	KUNIT_CASE_PARAM(hid_test_uclogic_parse_ugee_v2_desc,
208a64cbf3cSJosé Expósito 			 uclogic_parse_ugee_v2_desc_gen_params),
209a251d657SJosé Expósito 	KUNIT_CASE(hid_test_uclogic_params_cleanup_event_hooks),
210a64cbf3cSJosé Expósito 	{}
211a64cbf3cSJosé Expósito };
212a64cbf3cSJosé Expósito 
213a64cbf3cSJosé Expósito static struct kunit_suite hid_uclogic_params_test_suite = {
214a64cbf3cSJosé Expósito 	.name = "hid_uclogic_params_test",
215a64cbf3cSJosé Expósito 	.test_cases = hid_uclogic_params_test_cases,
216a64cbf3cSJosé Expósito };
217a64cbf3cSJosé Expósito 
218a64cbf3cSJosé Expósito kunit_test_suite(hid_uclogic_params_test_suite);
219a64cbf3cSJosé Expósito 
220a64cbf3cSJosé Expósito MODULE_DESCRIPTION("KUnit tests for the UC-Logic driver");
221a64cbf3cSJosé Expósito MODULE_LICENSE("GPL");
222a64cbf3cSJosé Expósito MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");
223