Skip to content

Commit

Permalink
Merge branch 'for-5.20/uclogic' into for-linus
Browse files Browse the repository at this point in the history
- XP-PEN Deco L support (José Expósito)
  • Loading branch information
Jiri Kosina committed Aug 2, 2022
2 parents db24433 + 0cb1fc0 commit a60885b
Show file tree
Hide file tree
Showing 9 changed files with 581 additions and 14 deletions.
5 changes: 5 additions & 0 deletions drivers/hid/.kunitconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CONFIG_KUNIT=y
CONFIG_USB=y
CONFIG_USB_HID=y
CONFIG_HID_UCLOGIC=y
CONFIG_HID_KUNIT_TEST=y
16 changes: 16 additions & 0 deletions drivers/hid/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,22 @@ config HID_MCP2221
To compile this driver as a module, choose M here: the module
will be called hid-mcp2221.ko.

config HID_KUNIT_TEST
bool "KUnit tests for HID" if !KUNIT_ALL_TESTS
depends on KUNIT=y
depends on HID_UCLOGIC
default KUNIT_ALL_TESTS
help
This builds unit tests for HID. This option is not useful for
distributions or general kernels, but only for kernel
developers working on HID and associated drivers.

For more information on KUnit and unit tests in general,
please refer to the KUnit documentation in
Documentation/dev-tools/kunit/.

If in doubt, say "N".

endmenu

endif # HID
Expand Down
3 changes: 3 additions & 0 deletions drivers/hid/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ 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-rdesc-test.o

obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
obj-$(CONFIG_USB_KBD) += usbhid/
Expand Down
1 change: 1 addition & 0 deletions drivers/hid/hid-ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,7 @@
#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_STAR06 0x0078
#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
Expand Down
2 changes: 2 additions & 0 deletions drivers/hid/hid-uclogic-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
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_STAR06) },
{ }
Expand Down
201 changes: 199 additions & 2 deletions drivers/hid/hid-uclogic-params.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
const int len = 12;
s32 resolution;
/* Pen report descriptor template parameters */
s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
__u8 *desc_ptr = NULL;

/* Check arguments */
Expand Down Expand Up @@ -383,7 +383,7 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
size_t i;
s32 resolution;
/* Pen report descriptor template parameters */
s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
__u8 *desc_ptr = NULL;

/* Check arguments */
Expand Down Expand Up @@ -1006,6 +1006,197 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
return rc;
}

/**
* uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or
* the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.
*
* @hdev: The HID device of the tablet interface to initialize and get
* parameters from. Cannot be NULL.
* @magic_arr: The magic data that should be sent to probe the interface.
* Cannot be NULL.
* @magic_size: Size of the magic data.
* @endpoint: Endpoint where the magic data should be sent.
*
* Returns:
* Zero, if successful. A negative errno code on error.
*/
static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr,
int magic_size, int endpoint)
{
struct usb_device *udev;
unsigned int pipe = 0;
int sent;
u8 *buf = NULL;
int rc = 0;

if (!hdev || !magic_arr) {
rc = -EINVAL;
goto cleanup;
}

buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto cleanup;
}

udev = hid_to_usb_dev(hdev);
pipe = usb_sndintpipe(udev, endpoint);

rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);
if (rc || sent != magic_size) {
hid_err(hdev, "Interface probing failed: %d\n", rc);
rc = -1;
goto cleanup;
}

rc = 0;
cleanup:
kfree(buf);
return rc;
}

/**
* uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
* discovering their parameters.
*
* These tables, internally designed as v2 to differentiate them from older
* models, expect a payload of magic data in orther to be switched to the fully
* functional mode and expose their parameters in a similar way to the
* information present in uclogic_params_pen_init_v1() but with some
* differences.
*
* @params: Parameters to fill in (to be cleaned with
* uclogic_params_cleanup()). Not modified in case of error.
* Cannot be NULL.
* @hdev: The HID device of the tablet interface to initialize and get
* parameters from. Cannot be NULL.
*
* Returns:
* Zero, if successful. A negative errno code on error.
*/
static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
struct hid_device *hdev)
{
int rc = 0;
struct usb_interface *iface;
__u8 bInterfaceNumber;
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;
__u8 magic_arr[] = {
0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* The resulting parameters (noop) */
struct uclogic_params p = {0, };

if (!params || !hdev) {
rc = -EINVAL;
goto cleanup;
}

iface = to_usb_interface(hdev->dev.parent);
bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
if (bInterfaceNumber != 2) {
uclogic_params_init_invalid(&p);
goto output;
}

/*
* Initialize the interface by sending magic data.
* The specific data was discovered by sniffing the Windows driver
* traffic.
*/
rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03);
if (rc) {
uclogic_params_init_invalid(&p);
goto output;
}

/*
* Read the string descriptor containing pen and frame parameters.
* The specific string descriptor and data were discovered by sniffing
* the Windows driver traffic.
*/
rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
if (rc != str_desc_len) {
hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
uclogic_params_init_invalid(&p);
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;
}
kfree(str_desc);
str_desc = NULL;

/* Initialize the pen interface */
rdesc_pen = uclogic_rdesc_template_apply(
uclogic_rdesc_ugee_v2_pen_template_arr,
uclogic_rdesc_ugee_v2_pen_template_size,
desc_params, ARRAY_SIZE(desc_params));
if (!rdesc_pen) {
rc = -ENOMEM;
goto cleanup;
}

p.pen.desc_ptr = rdesc_pen;
p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
p.pen.id = 0x02;
p.pen.subreport_list[0].value = 0xf0;
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;
}

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;
}

output:
/* Output parameters */
memcpy(params, &p, sizeof(*params));
memset(&p, 0, sizeof(p));
rc = 0;
cleanup:
kfree(str_desc);
uclogic_params_cleanup(&p);
return rc;
}

/**
* uclogic_params_init() - initialize a tablet interface and discover its
* parameters.
Expand Down Expand Up @@ -1241,6 +1432,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_XPPEN_TABLET_DECO_L):
rc = uclogic_params_ugee_v2_init(&p, hdev);
if (rc != 0)
goto cleanup;
break;
case VID_PID(USB_VENDOR_ID_TRUST,
USB_DEVICE_ID_TRUST_PANORA_TABLET):
case VID_PID(USB_VENDOR_ID_UGEE,
Expand Down
Loading

0 comments on commit a60885b

Please sign in to comment.