HID: uclogic: Support v2 protocol
Add support for UC-Logic v2 protocol to hid-uclogic. This adds support for a bunch of new Huion models. Signed-off-by: Nikolai Kondrashov <spbnick@gmail.com> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
This commit is contained in:
parent
59f2e0fca8
commit
2c3a88c64c
@ -231,6 +231,151 @@ cleanup:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_params_get_le24() - get a 24-bit little-endian number from a
|
||||
* buffer.
|
||||
*
|
||||
* @p: The pointer to the number buffer.
|
||||
*
|
||||
* Returns:
|
||||
* The retrieved number
|
||||
*/
|
||||
static s32 uclogic_params_get_le24(const void *p)
|
||||
{
|
||||
const __u8 *b = p;
|
||||
return b[0] | (b[1] << 8UL) | (b[2] << 16UL);
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_params_pen_init_v2() - initialize tablet interface pen
|
||||
* input and retrieve its parameters from the device, using v2 protocol.
|
||||
*
|
||||
* @pen: Pointer to the pen parameters to initialize (to be
|
||||
* cleaned up with uclogic_params_pen_cleanup()). Not modified in
|
||||
* case of error, or if parameters are not found. Cannot be NULL.
|
||||
* @pfound: Location for a flag which is set to true if the parameters
|
||||
* were found, and to false if not (e.g. device was
|
||||
* incompatible). 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_pen_init_v2(struct uclogic_params_pen *pen,
|
||||
bool *pfound,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
int rc;
|
||||
bool found = false;
|
||||
/* Buffer for (part of) the string descriptor */
|
||||
__u8 *buf = NULL;
|
||||
/* Descriptor length required */
|
||||
const int len = 18;
|
||||
s32 resolution;
|
||||
/* Pen report descriptor template parameters */
|
||||
s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
|
||||
__u8 *desc_ptr = NULL;
|
||||
|
||||
/* Check arguments */
|
||||
if (pen == NULL || pfound == NULL || hdev == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read string descriptor containing pen input parameters.
|
||||
* The specific string descriptor and data were discovered by sniffing
|
||||
* the Windows driver traffic.
|
||||
* NOTE: This enables fully-functional tablet mode.
|
||||
*/
|
||||
rc = uclogic_params_get_str_desc(&buf, hdev, 200, len);
|
||||
if (rc == -EPIPE) {
|
||||
hid_dbg(hdev,
|
||||
"string descriptor with pen parameters not found, assuming not compatible\n");
|
||||
goto finish;
|
||||
} else if (rc < 0) {
|
||||
hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
|
||||
goto cleanup;
|
||||
} else if (rc != len) {
|
||||
hid_dbg(hdev,
|
||||
"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
|
||||
rc, len);
|
||||
goto finish;
|
||||
} else {
|
||||
size_t i;
|
||||
/*
|
||||
* Check it's not just a catch-all UTF-16LE-encoded ASCII
|
||||
* string (such as the model name) some tablets put into all
|
||||
* unknown string descriptors.
|
||||
*/
|
||||
for (i = 2;
|
||||
i < len &&
|
||||
(buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
|
||||
i += 2);
|
||||
if (i >= len) {
|
||||
hid_dbg(hdev,
|
||||
"string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill report descriptor parameters from the string descriptor
|
||||
*/
|
||||
desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
|
||||
uclogic_params_get_le24(buf + 2);
|
||||
desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
|
||||
uclogic_params_get_le24(buf + 5);
|
||||
desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
|
||||
get_unaligned_le16(buf + 8);
|
||||
resolution = get_unaligned_le16(buf + 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(buf);
|
||||
buf = NULL;
|
||||
|
||||
/*
|
||||
* Generate pen report descriptor
|
||||
*/
|
||||
desc_ptr = uclogic_rdesc_template_apply(
|
||||
uclogic_rdesc_pen_v2_template_arr,
|
||||
uclogic_rdesc_pen_v2_template_size,
|
||||
desc_params, ARRAY_SIZE(desc_params));
|
||||
if (desc_ptr == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill-in the parameters
|
||||
*/
|
||||
memset(pen, 0, sizeof(*pen));
|
||||
pen->desc_ptr = desc_ptr;
|
||||
desc_ptr = NULL;
|
||||
pen->desc_size = uclogic_rdesc_pen_v2_template_size;
|
||||
pen->id = UCLOGIC_RDESC_PEN_V2_ID;
|
||||
pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
|
||||
pen->fragmented_hires = true;
|
||||
found = true;
|
||||
finish:
|
||||
*pfound = found;
|
||||
rc = 0;
|
||||
cleanup:
|
||||
kfree(desc_ptr);
|
||||
kfree(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* uclogic_params_frame_cleanup - free resources used by struct
|
||||
* uclogic_params_frame (tablet interface's frame controls input parameters).
|
||||
@ -560,11 +705,15 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
|
||||
struct hid_device *hdev)
|
||||
{
|
||||
int rc;
|
||||
struct usb_device *udev = hid_to_usb_dev(hdev);
|
||||
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
|
||||
__u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
|
||||
bool found;
|
||||
/* The resulting parameters (noop) */
|
||||
struct uclogic_params p = {0, };
|
||||
static const char transition_ver[] = "HUION_T153_160607";
|
||||
char *ver_ptr = NULL;
|
||||
const size_t ver_len = sizeof(transition_ver) + 1;
|
||||
|
||||
/* Check arguments */
|
||||
if (params == NULL || hdev == NULL) {
|
||||
@ -579,6 +728,57 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
|
||||
goto output;
|
||||
}
|
||||
|
||||
/* Try to get firmware version */
|
||||
ver_ptr = kzalloc(ver_len, GFP_KERNEL);
|
||||
if (ver_ptr == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
rc = usb_string(udev, 201, ver_ptr, ver_len);
|
||||
if (ver_ptr == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
if (rc == -EPIPE) {
|
||||
*ver_ptr = '\0';
|
||||
} else if (rc < 0) {
|
||||
hid_err(hdev,
|
||||
"failed retrieving Huion firmware version: %d\n", rc);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* If this is a transition firmware */
|
||||
if (strcmp(ver_ptr, transition_ver) == 0) {
|
||||
hid_dbg(hdev,
|
||||
"transition firmware detected, not probing pen v2 parameters\n");
|
||||
} else {
|
||||
/* Try to probe v2 pen parameters */
|
||||
rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev);
|
||||
if (rc != 0) {
|
||||
hid_err(hdev,
|
||||
"failed probing pen v2 parameters: %d\n", rc);
|
||||
goto cleanup;
|
||||
} else if (found) {
|
||||
hid_dbg(hdev, "pen v2 parameters found\n");
|
||||
/* Create v2 buttonpad parameters */
|
||||
rc = uclogic_params_frame_init_with_desc(
|
||||
&p.frame,
|
||||
uclogic_rdesc_buttonpad_v2_arr,
|
||||
uclogic_rdesc_buttonpad_v2_size,
|
||||
UCLOGIC_RDESC_BUTTONPAD_V2_ID);
|
||||
if (rc != 0) {
|
||||
hid_err(hdev,
|
||||
"failed creating v2 buttonpad parameters: %d\n",
|
||||
rc);
|
||||
goto cleanup;
|
||||
}
|
||||
/* Set bitmask marking frame reports in pen reports */
|
||||
p.pen_frame_flag = 0x20;
|
||||
goto output;
|
||||
}
|
||||
hid_dbg(hdev, "pen v2 parameters not found\n");
|
||||
}
|
||||
|
||||
/* Try to probe v1 pen parameters */
|
||||
rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
|
||||
if (rc != 0) {
|
||||
@ -613,6 +813,7 @@ output:
|
||||
memset(&p, 0, sizeof(p));
|
||||
rc = 0;
|
||||
cleanup:
|
||||
kfree(ver_ptr);
|
||||
uclogic_params_cleanup(&p);
|
||||
return rc;
|
||||
}
|
||||
|
@ -585,6 +585,62 @@ const __u8 uclogic_rdesc_pen_v1_template_arr[] = {
|
||||
const size_t uclogic_rdesc_pen_v1_template_size =
|
||||
sizeof(uclogic_rdesc_pen_v1_template_arr);
|
||||
|
||||
/* Fixed report descriptor template for (tweaked) v2 pen reports */
|
||||
const __u8 uclogic_rdesc_pen_v2_template_arr[] = {
|
||||
0x05, 0x0D, /* Usage Page (Digitizer), */
|
||||
0x09, 0x02, /* Usage (Pen), */
|
||||
0xA1, 0x01, /* Collection (Application), */
|
||||
0x85, 0x08, /* Report ID (8), */
|
||||
0x09, 0x20, /* Usage (Stylus), */
|
||||
0xA0, /* Collection (Physical), */
|
||||
0x14, /* Logical Minimum (0), */
|
||||
0x25, 0x01, /* Logical Maximum (1), */
|
||||
0x75, 0x01, /* Report Size (1), */
|
||||
0x09, 0x42, /* Usage (Tip Switch), */
|
||||
0x09, 0x44, /* Usage (Barrel Switch), */
|
||||
0x09, 0x46, /* Usage (Tablet Pick), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x03, /* Report Count (3), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x09, 0x32, /* Usage (In Range), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0x95, 0x01, /* Report Count (1), */
|
||||
0xA4, /* Push, */
|
||||
0x05, 0x01, /* Usage Page (Desktop), */
|
||||
0x65, 0x13, /* Unit (Inch), */
|
||||
0x55, 0xFD, /* Unit Exponent (-3), */
|
||||
0x75, 0x18, /* Report Size (24), */
|
||||
0x34, /* Physical Minimum (0), */
|
||||
0x09, 0x30, /* Usage (X), */
|
||||
0x27, UCLOGIC_RDESC_PEN_PH(X_LM),
|
||||
/* Logical Maximum (PLACEHOLDER), */
|
||||
0x47, UCLOGIC_RDESC_PEN_PH(X_PM),
|
||||
/* Physical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x09, 0x31, /* Usage (Y), */
|
||||
0x27, UCLOGIC_RDESC_PEN_PH(Y_LM),
|
||||
/* Logical Maximum (PLACEHOLDER), */
|
||||
0x47, UCLOGIC_RDESC_PEN_PH(Y_PM),
|
||||
/* Physical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0xB4, /* Pop, */
|
||||
0x09, 0x30, /* Usage (Tip Pressure), */
|
||||
0x75, 0x10, /* Report Size (16), */
|
||||
0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM),
|
||||
/* Logical Maximum (PLACEHOLDER), */
|
||||
0x81, 0x02, /* Input (Variable), */
|
||||
0x81, 0x03, /* Input (Constant, Variable), */
|
||||
0xC0, /* End Collection, */
|
||||
0xC0 /* End Collection */
|
||||
};
|
||||
|
||||
const size_t uclogic_rdesc_pen_v2_template_size =
|
||||
sizeof(uclogic_rdesc_pen_v2_template_arr);
|
||||
|
||||
/**
|
||||
* Expand to the contents of a generic buttonpad report descriptor.
|
||||
*
|
||||
@ -638,6 +694,13 @@ const __u8 uclogic_rdesc_buttonpad_v1_arr[] = {
|
||||
const size_t uclogic_rdesc_buttonpad_v1_size =
|
||||
sizeof(uclogic_rdesc_buttonpad_v1_arr);
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v2 buttonpad reports */
|
||||
const __u8 uclogic_rdesc_buttonpad_v2_arr[] = {
|
||||
UCLOGIC_RDESC_BUTTONPAD_BYTES(52)
|
||||
};
|
||||
const size_t uclogic_rdesc_buttonpad_v2_size =
|
||||
sizeof(uclogic_rdesc_buttonpad_v2_arr);
|
||||
|
||||
/**
|
||||
* uclogic_rdesc_template_apply() - apply report descriptor parameters to a
|
||||
* report descriptor template, creating a report descriptor. Copies the
|
||||
|
@ -107,6 +107,13 @@ enum uclogic_rdesc_pen_ph_id {
|
||||
extern const __u8 uclogic_rdesc_pen_v1_template_arr[];
|
||||
extern const size_t uclogic_rdesc_pen_v1_template_size;
|
||||
|
||||
/* Report ID for v2 pen reports */
|
||||
#define UCLOGIC_RDESC_PEN_V2_ID 0x08
|
||||
|
||||
/* Fixed report descriptor template for (tweaked) v2 pen reports */
|
||||
extern const __u8 uclogic_rdesc_pen_v2_template_arr[];
|
||||
extern const size_t uclogic_rdesc_pen_v2_template_size;
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v1 buttonpad reports */
|
||||
extern const __u8 uclogic_rdesc_buttonpad_v1_arr[];
|
||||
extern const size_t uclogic_rdesc_buttonpad_v1_size;
|
||||
@ -114,4 +121,11 @@ extern const size_t uclogic_rdesc_buttonpad_v1_size;
|
||||
/* Report ID for tweaked v1 buttonpad reports */
|
||||
#define UCLOGIC_RDESC_BUTTONPAD_V1_ID 0xf7
|
||||
|
||||
/* Fixed report descriptor for (tweaked) v2 buttonpad reports */
|
||||
extern const __u8 uclogic_rdesc_buttonpad_v2_arr[];
|
||||
extern const size_t uclogic_rdesc_buttonpad_v2_size;
|
||||
|
||||
/* Report ID for tweaked v2 buttonpad reports */
|
||||
#define UCLOGIC_RDESC_BUTTONPAD_V2_ID 0xf7
|
||||
|
||||
#endif /* _HID_UCLOGIC_RDESC_H */
|
||||
|
Loading…
Reference in New Issue
Block a user