diff --git a/hid-ids.h b/hid-ids.h index a657c80..0223494 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_A156P 0x090d #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..10b7a8a 100644 --- a/hid-uclogic-core.c +++ b/hid-uclogic-core.c @@ -24,6 +24,7 @@ #include "compat.h" #include +#include /* Driver data */ struct uclogic_drvdata { @@ -458,6 +459,57 @@ static int uclogic_raw_event(struct hid_device *hdev, report_id = data[0] = subreport->id; continue; } else { + /* A156P tilt compensation */ + if (hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_A156P && + hdev->vendor == USB_VENDOR_ID_UGEE) { + /* All tangent lengths for pen angles 1-64 + * degrees with a sensor height of 1.8mm + */ + const u16 tangents[] = { + 3, 6, 9, 12, 15, 18, 21, 25, 28, 30, 33, 36, + 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, + 73, 76, 79, 82, 85, 88, 92, 95, 98, 102, + 105, 109, 112, 116, 120, 124, 127, 131, + 135, 140, 144, 148, 153, 158, 162, 167, + 173, 178, 184, 189, 195, 202, 208, 215, + 223, 231, 239, 247, 257, 266, 277 + }; + // sqrt(8) / 4 = 0.7071067811865476 + const s32 discriminant = 707106781; + s8 tx = data[8]; + s8 ty = data[9]; + s8 abs_tilt; + s32 skew; + + if (tx != 0 && ty != 0) { + abs_tilt = abs(tx); + skew = get_unaligned_le16(&data[2]) - + (tx / abs_tilt) * tangents[abs_tilt] * + discriminant / 10000000; + skew = clamp(skew, 0, 34419); + put_unaligned_le16(skew, &data[2]); + + abs_tilt = abs(ty); + skew = get_unaligned_le16(&data[4]) - + (ty / abs_tilt) * tangents[abs_tilt] * + discriminant / 10000000; + skew = clamp(skew, 0, 19461); + put_unaligned_le16(skew, &data[4]); + } else if (tx != 0) { + abs_tilt = abs(tx); + skew = get_unaligned_le16(&data[2]) - + (tx / abs_tilt) * tangents[abs_tilt]; + skew = clamp(skew, 0, 34419); + put_unaligned_le16(skew, &data[2]); + } else if (ty != 0) { + abs_tilt = abs(ty); + skew = get_unaligned_le16(&data[4]) - + (ty / abs_tilt) * tangents[abs_tilt]; + skew = clamp(skew, 0, 19461); + put_unaligned_le16(skew, &data[4]); + } + } + return uclogic_raw_event_pen(drvdata, data, size); } } @@ -534,6 +586,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_A156P) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/hid-uclogic-params.c b/hid-uclogic-params.c index b13ae12..30a6777 100644 --- a/hid-uclogic-params.c +++ b/hid-uclogic-params.c @@ -998,6 +998,135 @@ static int uclogic_params_huion_init(struct uclogic_params *params, return rc; } +/** + * uclogic_params_init_ugee_xppen_pro() - initialization procedure + * common to XP-Pen Pro series devices + * + * @hdev: The HID device of the tablet interface to initialize and get + * parameters from. Cannot be NULL. + * @params: Parameters to fill in (to be cleaned with + * uclogic_params_cleanup()). Not modified in case of error. + * Cannot be NULL. + * @interface: The device interface the control packet is sent to. + * @init_packet: Magic packet to send on usb to activate device. + * @packet_size: Size of the init packet. + * @rdesc_pen_arr: Pen report descriptor array. + * @rdesc_pen_size: Size of the pen array. + * @rdesc_frame_arr: Frame report descriptor array. + * @rdesc_frame_size: Size of the frame array. + * + * Returns: + * Zero, if successful. A negative errno code on error. + */ +static int uclogic_params_init_ugee_xppen_pro(struct hid_device *hdev, + struct uclogic_params *p, const uint8_t interface, + const u8 init_packet[], const size_t packet_size, + const u8 rdesc_pen_arr[], const size_t rdesc_pen_size, + const u8 rdesc_frame_arr[], const size_t rdesc_frame_size) +{ + const size_t str_desc_len = 12; + struct usb_device *udev = hid_to_usb_dev(hdev); + u8 *buf = kmemdup(init_packet, packet_size, GFP_KERNEL); + s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + int actual_len, rc; + u16 resolution; + + if (hdev == NULL || p == NULL) + return -EINVAL; + + rc = usb_interrupt_msg( + udev, + usb_sndintpipe(udev, interface), + buf, + packet_size, + &actual_len, + USB_CTRL_SET_TIMEOUT); + kfree(buf); + if (rc == -EPIPE) { + hid_err(hdev, + "broken pipe sending init packet\n"); + return rc; + } else if (rc < 0) { + hid_err(hdev, "failed sending init packet: %d\n", rc); + return rc; + } else if (actual_len != packet_size) { + hid_err(hdev, + "failed to transfer complete init packet, only %d bytes sent\n", + actual_len); + return -1; + } + + rc = uclogic_params_get_str_desc(&buf, hdev, 100, str_desc_len); + if (rc != str_desc_len) { + if (rc == -EPIPE) { + hid_err(hdev, + "string descriptor with pen parameters not found\n"); + } else if (rc < 0) { + hid_err(hdev, + "failed retrieving pen parameters: %d\n", rc); + } else { + hid_err(hdev, + "string descriptor with pen parameters has invalid length (got %d, expected %lu)\n", + rc, str_desc_len); + rc = -1; + } + kfree(buf); + return rc; + } + + 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); + /* buf + 6 is the number of pad buttons? Its 0x0008 */ + desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = + get_unaligned_le16(buf + 8); + resolution = get_unaligned_le16(buf + 10); + kfree(buf); + if (resolution == 0) { + hid_err(hdev, "resolution of 0 in descriptor string\n"); + return -1; + } + 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; + + hid_dbg( + hdev, + "Received parameters: X: %d Y: %d Pressure: %d Resolution: %u\n", + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM], + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM], + desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM], + resolution + ); + + p->pen.desc_ptr = uclogic_rdesc_template_apply( + rdesc_pen_arr, + rdesc_pen_size, + desc_params, + ARRAY_SIZE(desc_params) + ); + p->pen.desc_size = rdesc_pen_size; + p->pen.id = 0x02; + + rc = uclogic_params_frame_init_with_desc( + &p->frame_list[0], + rdesc_frame_arr, + rdesc_frame_size, + UCLOGIC_RDESC_V1_FRAME_ID + ); + if (rc < 0) { + hid_err(hdev, "initializing frame params failed: %d\n", rc); + return rc; + } + + p->pen.subreport_list[0].value = 0xf0; + p->pen.subreport_list[0].id = p->frame_list[0].id; + + return 0; +} + /** * uclogic_params_init() - initialize a tablet interface and discover its * parameters. @@ -1293,6 +1422,30 @@ 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_A156P): + static const u8 init_packet[] = { + 0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const size_t packet_size = sizeof(init_packet); + + /* Only use the uniform interface */ + if (bInterfaceNumber != 2) { + uclogic_params_init_invalid(&p); + break; + } + + rc = uclogic_params_init_ugee_xppen_pro(hdev, &p, 0x03, init_packet, packet_size, + uclogic_rdesc_xppen_a156p_pen_arr, + uclogic_rdesc_xppen_a156p_pen_size, + uclogic_rdesc_xppen_a156p_frame_arr, + uclogic_rdesc_xppen_a156p_frame_size); + if (rc != 0) { + hid_err(hdev, "a156p init failed: %d\n", rc); + goto cleanup; + } + break; } diff --git a/hid-uclogic-rdesc.c b/hid-uclogic-rdesc.c index 6381911..f85f12b 100644 --- a/hid-uclogic-rdesc.c +++ b/hid-uclogic-rdesc.c @@ -975,6 +975,108 @@ const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = { const size_t uclogic_rdesc_xppen_deco01_frame_size = sizeof(uclogic_rdesc_xppen_deco01_frame_arr); + /* Report descriptor template for XP-Pen Artist 15.6 Pro pen */ +const __u8 uclogic_rdesc_xppen_a156p_pen_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x01, /* Input (Constant), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 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), */ + 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xA4, /* Push, */ + 0x54, /* Unit Exponent (0), */ + 0x65, 0x14, /* Unit (Degrees), */ + 0x35, 0xC3, /* Physical Minimum (-61), */ + 0x45, 0x3C, /* Physical Maximum (60), */ + 0x15, 0xC3, /* Logical Minimum (-61), */ + 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), */ + 0xB4, /* Pop, */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_a156p_pen_size = + sizeof(uclogic_rdesc_xppen_a156p_pen_arr); + +/* Report descriptor template for XP-Pen Artist 15.6 Pro frame */ +const __u8 uclogic_rdesc_xppen_a156p_frame_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, UCLOGIC_RDESC_V1_FRAME_ID, + /* Report ID (Virtual report), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x08, /* Usage Maximum (08h), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x20, /* Report Count (32), */ + 0x81, 0x01, /* Input (Constant), */ + 0x19, 0x09, /* Usage Minimum (09h), */ + 0x29, 0x0A, /* Usage Maximum (0Ah), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x16, /* Report Count (22), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_a156p_frame_size = + sizeof(uclogic_rdesc_xppen_a156p_frame_arr); + /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the diff --git a/hid-uclogic-rdesc.h b/hid-uclogic-rdesc.h index 0c6e95e..238c35c 100644 --- a/hid-uclogic-rdesc.h +++ b/hid-uclogic-rdesc.h @@ -167,6 +167,14 @@ extern const size_t uclogic_rdesc_xppen_deco01_frame_size; extern const __u8 uclogic_rdesc_ugee_g5_frame_arr[]; extern const size_t uclogic_rdesc_ugee_g5_frame_size; +/* Report descriptor template for XP-Pen Artist 15.6 Pro pen */ +extern const __u8 uclogic_rdesc_xppen_a156p_pen_arr[]; +extern const size_t uclogic_rdesc_xppen_a156p_pen_size; + +/* Report descriptor template for XP-Pen Artist 15.6 Pro frame */ +extern const __u8 uclogic_rdesc_xppen_a156p_frame_arr[]; +extern const size_t uclogic_rdesc_xppen_a156p_frame_size; + /* Report ID of Ugee G5 frame control reports */ #define UCLOGIC_RDESC_UGEE_G5_FRAME_ID 0x06