Message ID | 20190210101409.3511-11-spbnick@gmail.com (mailing list archive) |
---|---|
State | Mainlined |
Commit | 2c3a88c64cb62cc59010359dbfc97f734e98d683 |
Delegated to: | Jiri Kosina |
Headers | show |
Series | [v2,01/23] HID: kye: Add support for EasyPen M406XE | expand |
On Sun, Feb 10, 2019 at 11:15 AM Nikolai Kondrashov <spbnick@gmail.com> wrote: > > 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> > --- > drivers/hid/hid-uclogic-params.c | 201 +++++++++++++++++++++++++++++++ > drivers/hid/hid-uclogic-rdesc.c | 63 ++++++++++ > drivers/hid/hid-uclogic-rdesc.h | 14 +++ > 3 files changed, 278 insertions(+) > > diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c > index b5e4d99c6771..132663a87f38 100644 > --- a/drivers/hid/hid-uclogic-params.c > +++ b/drivers/hid/hid-uclogic-params.c > @@ -231,6 +231,151 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, > 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); Nitpick, I am pretty sure we already have the bits in the kernel for that. But OTOH, not sure I'll request a v3 just for that. Cheers, Benjamin > +} > + > +/** > + * 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 @@ static int uclogic_params_huion_init(struct uclogic_params *params, > memset(&p, 0, sizeof(p)); > rc = 0; > cleanup: > + kfree(ver_ptr); > uclogic_params_cleanup(&p); > return rc; > } > diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c > index 359e72394d83..ef1d3cf918a4 100644 > --- a/drivers/hid/hid-uclogic-rdesc.c > +++ b/drivers/hid/hid-uclogic-rdesc.c > @@ -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 > diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h > index cf03b98ae7cb..f205254a733c 100644 > --- a/drivers/hid/hid-uclogic-rdesc.h > +++ b/drivers/hid/hid-uclogic-rdesc.h > @@ -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 */ > -- > 2.20.1 >
On 2/21/19 12:41 PM, Benjamin Tissoires wrote: > On Sun, Feb 10, 2019 at 11:15 AM Nikolai Kondrashov <spbnick@gmail.com> wrote: >> +/** >> + * 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); > > Nitpick, I am pretty sure we already have the bits in the kernel for that. I tried to find it and couldn't, do you have any hints where I can find it? > But OTOH, not sure I'll request a v3 just for that. Will be glad to fix this, if you'll need v3, or later. Nick
On Thu, Feb 21, 2019 at 11:54 AM Nikolai Kondrashov <spbnick@gmail.com> wrote: > > On 2/21/19 12:41 PM, Benjamin Tissoires wrote: > > On Sun, Feb 10, 2019 at 11:15 AM Nikolai Kondrashov <spbnick@gmail.com> wrote: > >> +/** > >> + * 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); > > > > Nitpick, I am pretty sure we already have the bits in the kernel for that. > > I tried to find it and couldn't, do you have any hints where I can find it? For non aligned bits in the middle of a report, we have hid_field_extract() (https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git/tree/drivers/hid/hid-core.c?h=for-next#n1301) For other regular operations, there are the get_unaligned_le*() operations, but they are for 16, 32 or 64 bits. include/target/target_core_backend.h has a get_unaligned_be24() but it requires to read p - 1, and I am not sure you can in your case. > > > But OTOH, not sure I'll request a v3 just for that. > > Will be glad to fix this, if you'll need v3, or later. Probably later :) Cheers, Benjamin > > Nick
On 2/21/19 12:59 PM, Benjamin Tissoires wrote: > On Thu, Feb 21, 2019 at 11:54 AM Nikolai Kondrashov <spbnick@gmail.com> wrote: >> >> On 2/21/19 12:41 PM, Benjamin Tissoires wrote: >>> On Sun, Feb 10, 2019 at 11:15 AM Nikolai Kondrashov <spbnick@gmail.com> wrote: >>>> +/** >>>> + * 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); >>> >>> Nitpick, I am pretty sure we already have the bits in the kernel for that. >> >> I tried to find it and couldn't, do you have any hints where I can find it? > > For non aligned bits in the middle of a report, we have > hid_field_extract() > (https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git/tree/drivers/hid/hid-core.c?h=for-next#n1301) Yeah, that could work. However, if you don't mind, I'd rather keep using my version for now, since hid_field_extract is only available starting v4.2. That would let digimend-kernel-drivers build on older distros without having the code differ that much from upstream. Maybe next year. > For other regular operations, there are the get_unaligned_le*() > operations, but they are for 16, 32 or 64 bits. Yes, I found these. > include/target/target_core_backend.h has a get_unaligned_be24() but it > requires to read p - 1, and I am not sure you can in your case. Yeah, I'd rather not use a function which does p - 1, has no hint of that limitation in its name, and is a part of the unrelated storage subsystem. Thanks :) Nick
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index b5e4d99c6771..132663a87f38 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -231,6 +231,151 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, 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 @@ static int uclogic_params_huion_init(struct uclogic_params *params, memset(&p, 0, sizeof(p)); rc = 0; cleanup: + kfree(ver_ptr); uclogic_params_cleanup(&p); return rc; } diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 359e72394d83..ef1d3cf918a4 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -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 diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index cf03b98ae7cb..f205254a733c 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -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 */
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> --- drivers/hid/hid-uclogic-params.c | 201 +++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.c | 63 ++++++++++ drivers/hid/hid-uclogic-rdesc.h | 14 +++ 3 files changed, 278 insertions(+)