diff --git a/hid-ids.h b/hid-ids.h index a657c80..aea31e1 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 ce30c45..4bf8e22 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 b13ae12..6302827 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 5bef8da..ebfbcf0 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 6381911..88f69c4 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 0c6e95e..17d35af 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