162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci *  HID driver for UC-Logic devices not fully compliant with HID standard
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (c) 2022 José Expósito <jose.exposito89@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <kunit/test.h>
1062306a36Sopenharmony_ci#include "./hid-uclogic-params.h"
1162306a36Sopenharmony_ci#include "./hid-uclogic-rdesc.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define MAX_STR_DESC_SIZE 14
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct uclogic_parse_ugee_v2_desc_case {
1662306a36Sopenharmony_ci	const char *name;
1762306a36Sopenharmony_ci	int res;
1862306a36Sopenharmony_ci	const __u8 str_desc[MAX_STR_DESC_SIZE];
1962306a36Sopenharmony_ci	size_t str_desc_size;
2062306a36Sopenharmony_ci	const s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
2162306a36Sopenharmony_ci	enum uclogic_params_frame_type frame_type;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic struct uclogic_parse_ugee_v2_desc_case uclogic_parse_ugee_v2_desc_cases[] = {
2562306a36Sopenharmony_ci	{
2662306a36Sopenharmony_ci		.name = "invalid_str_desc",
2762306a36Sopenharmony_ci		.res = -EINVAL,
2862306a36Sopenharmony_ci		.str_desc = {},
2962306a36Sopenharmony_ci		.str_desc_size = 0,
3062306a36Sopenharmony_ci		.desc_params = {},
3162306a36Sopenharmony_ci		.frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
3262306a36Sopenharmony_ci	},
3362306a36Sopenharmony_ci	{
3462306a36Sopenharmony_ci		.name = "resolution_with_value_0",
3562306a36Sopenharmony_ci		.res = 0,
3662306a36Sopenharmony_ci		.str_desc = {
3762306a36Sopenharmony_ci			0x0E, 0x03,
3862306a36Sopenharmony_ci			0x70, 0xB2,
3962306a36Sopenharmony_ci			0x10, 0x77,
4062306a36Sopenharmony_ci			0x08,
4162306a36Sopenharmony_ci			0x00,
4262306a36Sopenharmony_ci			0xFF, 0x1F,
4362306a36Sopenharmony_ci			0x00, 0x00,
4462306a36Sopenharmony_ci		},
4562306a36Sopenharmony_ci		.str_desc_size = 12,
4662306a36Sopenharmony_ci		.desc_params = {
4762306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB270,
4862306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0,
4962306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7710,
5062306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0,
5162306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
5262306a36Sopenharmony_ci			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
5362306a36Sopenharmony_ci		},
5462306a36Sopenharmony_ci		.frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
5562306a36Sopenharmony_ci	},
5662306a36Sopenharmony_ci	/* XP-PEN Deco L str_desc: Frame with 8 buttons */
5762306a36Sopenharmony_ci	{
5862306a36Sopenharmony_ci		.name = "frame_type_buttons",
5962306a36Sopenharmony_ci		.res = 0,
6062306a36Sopenharmony_ci		.str_desc = {
6162306a36Sopenharmony_ci			0x0E, 0x03,
6262306a36Sopenharmony_ci			0x70, 0xB2,
6362306a36Sopenharmony_ci			0x10, 0x77,
6462306a36Sopenharmony_ci			0x08,
6562306a36Sopenharmony_ci			0x00,
6662306a36Sopenharmony_ci			0xFF, 0x1F,
6762306a36Sopenharmony_ci			0xD8, 0x13,
6862306a36Sopenharmony_ci		},
6962306a36Sopenharmony_ci		.str_desc_size = 12,
7062306a36Sopenharmony_ci		.desc_params = {
7162306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB270,
7262306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2320,
7362306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7710,
7462306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x1770,
7562306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
7662306a36Sopenharmony_ci			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
7762306a36Sopenharmony_ci		},
7862306a36Sopenharmony_ci		.frame_type = UCLOGIC_PARAMS_FRAME_BUTTONS,
7962306a36Sopenharmony_ci	},
8062306a36Sopenharmony_ci	/* PARBLO A610 PRO str_desc: Frame with 9 buttons and dial */
8162306a36Sopenharmony_ci	{
8262306a36Sopenharmony_ci		.name = "frame_type_dial",
8362306a36Sopenharmony_ci		.res = 0,
8462306a36Sopenharmony_ci		.str_desc = {
8562306a36Sopenharmony_ci			0x0E, 0x03,
8662306a36Sopenharmony_ci			0x96, 0xC7,
8762306a36Sopenharmony_ci			0xF9, 0x7C,
8862306a36Sopenharmony_ci			0x09,
8962306a36Sopenharmony_ci			0x01,
9062306a36Sopenharmony_ci			0xFF, 0x1F,
9162306a36Sopenharmony_ci			0xD8, 0x13,
9262306a36Sopenharmony_ci		},
9362306a36Sopenharmony_ci		.str_desc_size = 12,
9462306a36Sopenharmony_ci		.desc_params = {
9562306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xC796,
9662306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2749,
9762306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x7CF9,
9862306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x1899,
9962306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
10062306a36Sopenharmony_ci			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x09,
10162306a36Sopenharmony_ci		},
10262306a36Sopenharmony_ci		.frame_type = UCLOGIC_PARAMS_FRAME_DIAL,
10362306a36Sopenharmony_ci	},
10462306a36Sopenharmony_ci	/* XP-PEN Deco Pro S str_desc: Frame with 8 buttons and mouse */
10562306a36Sopenharmony_ci	{
10662306a36Sopenharmony_ci		.name = "frame_type_mouse",
10762306a36Sopenharmony_ci		.res = 0,
10862306a36Sopenharmony_ci		.str_desc = {
10962306a36Sopenharmony_ci			0x0E, 0x03,
11062306a36Sopenharmony_ci			0xC8, 0xB3,
11162306a36Sopenharmony_ci			0x34, 0x65,
11262306a36Sopenharmony_ci			0x08,
11362306a36Sopenharmony_ci			0x02,
11462306a36Sopenharmony_ci			0xFF, 0x1F,
11562306a36Sopenharmony_ci			0xD8, 0x13,
11662306a36Sopenharmony_ci		},
11762306a36Sopenharmony_ci		.str_desc_size = 12,
11862306a36Sopenharmony_ci		.desc_params = {
11962306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xB3C8,
12062306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0x2363,
12162306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0x6534,
12262306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0x13EC,
12362306a36Sopenharmony_ci			[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0x1FFF,
12462306a36Sopenharmony_ci			[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0x08,
12562306a36Sopenharmony_ci		},
12662306a36Sopenharmony_ci		.frame_type = UCLOGIC_PARAMS_FRAME_MOUSE,
12762306a36Sopenharmony_ci	},
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void uclogic_parse_ugee_v2_desc_case_desc(struct uclogic_parse_ugee_v2_desc_case *t,
13162306a36Sopenharmony_ci						 char *desc)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ciKUNIT_ARRAY_PARAM(uclogic_parse_ugee_v2_desc, uclogic_parse_ugee_v2_desc_cases,
13762306a36Sopenharmony_ci		  uclogic_parse_ugee_v2_desc_case_desc);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int res;
14262306a36Sopenharmony_ci	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
14362306a36Sopenharmony_ci	enum uclogic_params_frame_type frame_type;
14462306a36Sopenharmony_ci	const struct uclogic_parse_ugee_v2_desc_case *params = test->param_value;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	res = uclogic_params_parse_ugee_v2_desc(params->str_desc,
14762306a36Sopenharmony_ci						params->str_desc_size,
14862306a36Sopenharmony_ci						desc_params,
14962306a36Sopenharmony_ci						ARRAY_SIZE(desc_params),
15062306a36Sopenharmony_ci						&frame_type);
15162306a36Sopenharmony_ci	KUNIT_ASSERT_EQ(test, res, params->res);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (res)
15462306a36Sopenharmony_ci		return;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test,
15762306a36Sopenharmony_ci			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM],
15862306a36Sopenharmony_ci			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM]);
15962306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test,
16062306a36Sopenharmony_ci			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM],
16162306a36Sopenharmony_ci			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM]);
16262306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test,
16362306a36Sopenharmony_ci			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM],
16462306a36Sopenharmony_ci			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM]);
16562306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test,
16662306a36Sopenharmony_ci			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM],
16762306a36Sopenharmony_ci			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM]);
16862306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test,
16962306a36Sopenharmony_ci			params->desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM],
17062306a36Sopenharmony_ci			desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM]);
17162306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test,
17262306a36Sopenharmony_ci			params->desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM],
17362306a36Sopenharmony_ci			desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM]);
17462306a36Sopenharmony_ci	KUNIT_EXPECT_EQ(test, params->frame_type, frame_type);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistruct fake_device {
17862306a36Sopenharmony_ci	unsigned long quirks;
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void hid_test_uclogic_params_cleanup_event_hooks(struct kunit *test)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	int res, n;
18462306a36Sopenharmony_ci	struct hid_device *hdev;
18562306a36Sopenharmony_ci	struct fake_device *fake_dev;
18662306a36Sopenharmony_ci	struct uclogic_params p = {0, };
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	hdev = kunit_kzalloc(test, sizeof(struct hid_device), GFP_KERNEL);
18962306a36Sopenharmony_ci	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdev);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	fake_dev = kunit_kzalloc(test, sizeof(struct fake_device), GFP_KERNEL);
19262306a36Sopenharmony_ci	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_dev);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	hid_set_drvdata(hdev, fake_dev);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	res = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);
19762306a36Sopenharmony_ci	KUNIT_ASSERT_EQ(test, res, 0);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	/* Check that the function can be called repeatedly */
20062306a36Sopenharmony_ci	for (n = 0; n < 4; n++) {
20162306a36Sopenharmony_ci		uclogic_params_cleanup_event_hooks(&p);
20262306a36Sopenharmony_ci		KUNIT_EXPECT_PTR_EQ(test, p.event_hooks, NULL);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic struct kunit_case hid_uclogic_params_test_cases[] = {
20762306a36Sopenharmony_ci	KUNIT_CASE_PARAM(hid_test_uclogic_parse_ugee_v2_desc,
20862306a36Sopenharmony_ci			 uclogic_parse_ugee_v2_desc_gen_params),
20962306a36Sopenharmony_ci	KUNIT_CASE(hid_test_uclogic_params_cleanup_event_hooks),
21062306a36Sopenharmony_ci	{}
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic struct kunit_suite hid_uclogic_params_test_suite = {
21462306a36Sopenharmony_ci	.name = "hid_uclogic_params_test",
21562306a36Sopenharmony_ci	.test_cases = hid_uclogic_params_test_cases,
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cikunit_test_suite(hid_uclogic_params_test_suite);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ciMODULE_DESCRIPTION("KUnit tests for the UC-Logic driver");
22162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
22262306a36Sopenharmony_ciMODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");
223