From 3fcb1fed7cf6f1e2695320238264a25a119dbbae Mon Sep 17 00:00:00 2001 From: studberzst6233 Date: Wed, 9 Mar 2022 03:10:52 +0100 Subject: [PATCH] We add support for the newer Xppen tablet mini7. The thing is that the tablet reports the buttons as keyboard events. This can only be circumvented by using the vendor defined usage and usage page, for which the events are reported in a unified format. To activate this interface, we need to send a a certain key on a certain endpoint, shown in: https://github.com/nic3-14159/uclogic-new-init Still this interface is opaque in that the report descritpor only shows the length, not what the individual bits do. Therefore we need to add two more devices, one for the pen and one for the buttons. The first interface doesn't go away but doesn't report anything either, since it's reports are translated into the other two devices. I have called this original device "VendorDevice". Once all this is done, native support with the wacom driver is achieved. Though since the wacom driver doesn't recognize the IDs yet, we are in need of X config file like: Section "InputClass" Identifier "xppen wacom all" MatchUSBID "28bd:0928" Driver "wacom" EndSection That's all. --- hid-ids.h | 1 + hid-uclogic-core.c | 14 +++ hid-uclogic-params.c | 226 +++++++++++++++++++++++++++++++++++++++++++ hid-uclogic-params.h | 5 + hid-uclogic-rdesc.c | 117 ++++++++++++++++++++++ hid-uclogic-rdesc.h | 13 +++ 6 files changed, 376 insertions(+) diff --git a/hid-ids.h b/hid-ids.h index a657c805..aea31e15 100644 --- a/hid-ids.h +++ b/hid-ids.h @@ -54,6 +54,7 @@ #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_STAR06 0x0078 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_MINI7 0x0928 #define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055 diff --git a/hid-uclogic-core.c b/hid-uclogic-core.c index ce30c45d..4bf8e22c 100644 --- a/hid-uclogic-core.c +++ b/hid-uclogic-core.c @@ -173,6 +173,10 @@ static int uclogic_input_configured(struct hid_device *hdev, suffix = "System Control"; break; } + + if(field->application >= 0xff000000){ + suffix = "VendorDevice"; + } } if (suffix) { @@ -295,6 +299,14 @@ static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata, WARN_ON(drvdata == NULL); WARN_ON(data == NULL && size != 0); + /* + * the main report is vendor defined and opaque, so we couldn't define + * buttons for example. + */ + if (pen->late_id) { + data[0] = pen->late_id; + } + /* If in-range reports are inverted */ if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) { @@ -534,6 +546,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_MINI7) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/hid-uclogic-params.c b/hid-uclogic-params.c index b13ae123..63028275 100644 --- a/hid-uclogic-params.c +++ b/hid-uclogic-params.c @@ -205,6 +205,78 @@ static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) memset(pen, 0, sizeof(*pen)); } +static int uclogic_params_pen_v1_get_desc_params(struct uclogic_params_pen *pen, + bool *pfound, + s32 *desc_params, + struct hid_device *hdev) +{ + int rc; + bool found = false; + /* Buffer for (part of) the string descriptor */ + __u8 *buf = NULL; + /* Minimum descriptor length required, maximum seen so far is 18 */ + const int len = 12; + s32 resolution; + + /* 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, 100, len); + if (rc == -EPIPE) { + hid_info(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_info(hdev, + "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", + rc, len); + goto finish; + } + + /* + * Fill report descriptor parameters from the string descriptor + */ + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = + get_unaligned_le16(buf + 2); + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = + get_unaligned_le16(buf + 4); + 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; + } + + + found = true; +finish: + *pfound = found; + rc = 0; +cleanup: + kfree(buf); + return rc; +} + /** * uclogic_params_pen_init_v1() - initialize tablet interface pen * input and retrieve its parameters from the device, using v1 protocol. @@ -778,6 +850,111 @@ static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, return rc; } +/** + * uclogic_params_xppen_new_init() - initialize a newer xppen tablet interface + * and discover its parameters. + * + * @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_xppen_new_init(struct uclogic_params *params, + struct hid_device *hdev) +{ + s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + struct uclogic_params p = {0, }; + __u8 *desc_ptr; + bool found; + int rc; + + desc_ptr = kmalloc(uclogic_rdesc_xppen_new_vendor_size, GFP_KERNEL); + if (desc_ptr) { + p.desc_ptr = desc_ptr; + p.desc_size = uclogic_rdesc_xppen_new_vendor_size; + memcpy(p.desc_ptr, + uclogic_rdesc_xppen_new_vendor_arr, + uclogic_rdesc_xppen_new_vendor_size); + desc_ptr = NULL; + + }else{ + rc = -ENOMEM; + goto cleanup; + } + + rc = uclogic_params_pen_v1_get_desc_params(&p.pen, &found, desc_params, hdev); + + if (rc != 0) { + hid_err(hdev, "pen probing failed: %d\n", rc); + goto cleanup; + } + + /* + * Generate pen report descriptor + */ + desc_ptr = uclogic_rdesc_template_apply( + uclogic_rdesc_xppen_new_pen_arr, + uclogic_rdesc_xppen_new_pen_size, + desc_params, ARRAY_SIZE(desc_params)); + + if (desc_ptr) { + p.pen.desc_ptr = desc_ptr; + desc_ptr = NULL; + p.pen.desc_size = uclogic_rdesc_xppen_new_pen_size; + p.pen.id = 0x02; + p.pen.late_id = 0x08; + p.pen.inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; + p.pen.fragmented_hires = false; + p.pen.tilt_y_flipped = false; + }else{ + rc = -ENOMEM; + goto cleanup; + } + + rc = uclogic_params_frame_init_with_desc( + &p.frame_list[0], + uclogic_rdesc_xppen_new_buttons_arr, + uclogic_rdesc_xppen_new_buttons_size, + 0x06); + + if (rc != 0) { + hid_err(hdev, "failed setting frame parameters: %d\n", rc); + goto cleanup; + } + + p.pen.subreport_list[0].value = 0xf0; + p.pen.subreport_list[0].id = 0x06; + + if (!found) { + hid_warn(hdev, "pen parameters not found"); + uclogic_params_init_invalid(&p); + } + +output: + /* Output parameters */ + memcpy(params, &p, sizeof(*params)); + memset(&p, 0, sizeof(p)); + rc = 0; +cleanup: + kfree(desc_ptr); + uclogic_params_cleanup(&p); + return rc; +} + +/** + * xppen_new_urb_completion() - The urb completion function for newer + * xppen tablets, which doesn't do anything right now. + * + * @params: The urb that has been completed. + */ +void xppen_new_urb_completion(struct urb * daUrb) +{ +} + /** * uclogic_params_huion_init() - initialize a Huion tablet interface and * discover its parameters. @@ -1230,6 +1407,55 @@ 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_MINI7): + + /* If this is the vendor interface */ + if(bInterfaceNumber == 2){ + struct urb * daUrb; + u8 * buf; + u8 key[] = { + 0x02, 0xB0, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + buf = devm_kzalloc(&hdev->dev, sizeof(key), GFP_KERNEL); + if (buf == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + daUrb = usb_alloc_urb(0, GFP_KERNEL); + if (daUrb == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + memcpy(buf, key, sizeof(key)); + usb_fill_int_urb(daUrb, + udev, + usb_sndintpipe(udev, 3), + buf, sizeof(key), + xppen_new_urb_completion, NULL, + 0x10); + rc = usb_submit_urb(daUrb, GFP_KERNEL); + usb_free_urb(daUrb); + + if (rc != 0) { + hid_err(hdev, "vendor init failed: %d\n", rc); + goto cleanup; + } + + uclogic_params_xppen_new_init(&p, hdev); + if (rc != 0) { + goto cleanup; + } + + } else { + uclogic_params_init_invalid(&p); + } + break; + case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_G5): /* Ignore non-pen interfaces */ diff --git a/hid-uclogic-params.h b/hid-uclogic-params.h index 5bef8daa..ebfbcf06 100644 --- a/hid-uclogic-params.h +++ b/hid-uclogic-params.h @@ -73,6 +73,11 @@ struct uclogic_params_pen { unsigned int desc_size; /* Report ID, if reports should be tweaked, zero if not */ unsigned int id; + /* + * if the main report is actually a vendor defined data structure + * with no posibility of defining small report sizes for buttons and such, + * as with the newer xppen tablets */ + unsigned int late_id; /* The list of subreports, only valid if "id" is not zero */ struct uclogic_params_pen_subreport subreport_list[3]; /* Type of in-range reporting, only valid if "id" is not zero */ diff --git a/hid-uclogic-rdesc.c b/hid-uclogic-rdesc.c index 63819110..88f69c4c 100644 --- a/hid-uclogic-rdesc.c +++ b/hid-uclogic-rdesc.c @@ -531,6 +531,123 @@ __u8 uclogic_rdesc_twha60_fixed1_arr[] = { const size_t uclogic_rdesc_twha60_fixed1_size = sizeof(uclogic_rdesc_twha60_fixed1_arr); +/* Vendor defined report descriptor for newer xppen tablets */ +__u8 uclogic_rdesc_xppen_new_vendor_arr[] = { + 0x06, 0x0A, 0xFF, /* Usage Page (FF0Ah), */ + 0x09, 0x01, /* Usage (01h), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x02, /* Usage (02h), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x0B, /* Report Count (11), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x03, /* Usage (03h), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x09, /* Report Count (9), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x91, 0x02, /* Output (Variable), */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_new_vendor_size = + sizeof(uclogic_rdesc_xppen_new_vendor_arr); + +/* pen report descriptor for newer xppen tablets */ +__u8 uclogic_rdesc_xppen_new_pen_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x01, /* Usage (Digitizer), */ + 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, 0x10, /* Report Size (16), */ + 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), */ + 0x54, /* Unit Exponent (0), */ + 0x65, 0x14, /* Unit (Degrees), */ + 0x35, 0xC4, /* Physical Minimum (-60), */ + 0x45, 0x3C, /* Physical Maximum (60), */ + 0x15, 0xC4, /* Logical Minimum (-60), */ + 0x25, 0x3C, /* Logical Maximum (60), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x02, /* Report Count (2), */ + 0x09, 0x3D, /* Usage (X Tilt), */ + 0x09, 0x3E, /* Usage (Y Tilt), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_new_pen_size = + sizeof(uclogic_rdesc_xppen_new_pen_arr); + +/* button report descriptor for newer xppen tablets */ +__u8 uclogic_rdesc_xppen_new_buttons_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x06, /* Report ID (6), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x08, /* Usage Maximum (08h), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_new_buttons_size = + sizeof(uclogic_rdesc_xppen_new_buttons_arr); + /* Fixed report descriptor template for (tweaked) v1 pen reports */ const __u8 uclogic_rdesc_v1_pen_template_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ diff --git a/hid-uclogic-rdesc.h b/hid-uclogic-rdesc.h index 0c6e95e8..17d35afc 100644 --- a/hid-uclogic-rdesc.h +++ b/hid-uclogic-rdesc.h @@ -80,6 +80,19 @@ extern const size_t uclogic_rdesc_twha60_fixed0_size; extern __u8 uclogic_rdesc_twha60_fixed1_arr[]; extern const size_t uclogic_rdesc_twha60_fixed1_size; +/* + * These three are for newer xppen tablets, where everything is sent via + * a vendor defined usage in the range of FF00-FFFF + */ +extern __u8 uclogic_rdesc_xppen_new_vendor_arr[]; +extern const size_t uclogic_rdesc_xppen_new_vendor_size; + +extern __u8 uclogic_rdesc_xppen_new_pen_arr[]; +extern const size_t uclogic_rdesc_xppen_new_pen_size; + +extern __u8 uclogic_rdesc_xppen_new_buttons_arr[]; +extern const size_t uclogic_rdesc_xppen_new_buttons_size; + /* Report descriptor template placeholder head */ #define UCLOGIC_RDESC_PH_HEAD 0xFE, 0xED, 0x1D