diff options
Diffstat (limited to 'drivers/hid/hid-uclogic-core.c')
| -rw-r--r-- | drivers/hid/hid-uclogic-core.c | 113 |
1 files changed, 93 insertions, 20 deletions
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index f67835f9ed4c..90ebb81041ea 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -32,8 +32,8 @@ */ static void uclogic_inrange_timeout(struct timer_list *t) { - struct uclogic_drvdata *drvdata = from_timer(drvdata, t, - inrange_timer); + struct uclogic_drvdata *drvdata = timer_container_of(drvdata, t, + inrange_timer); struct input_dev *input = drvdata->pen_input; if (input == NULL) @@ -50,18 +50,42 @@ static void uclogic_inrange_timeout(struct timer_list *t) input_sync(input); } -static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); if (drvdata->desc_ptr != NULL) { - rdesc = drvdata->desc_ptr; *rsize = drvdata->desc_size; + return drvdata->desc_ptr; } return rdesc; } +/* Buttons considered valid tablet pad inputs. */ +static const unsigned int uclogic_extra_input_mapping[] = { + BTN_0, + BTN_1, + BTN_2, + BTN_3, + BTN_4, + BTN_5, + BTN_6, + BTN_7, + BTN_8, + BTN_RIGHT, + BTN_MIDDLE, + BTN_SIDE, + BTN_EXTRA, + BTN_FORWARD, + BTN_BACK, + BTN_B, + BTN_A, + BTN_BASE, + BTN_BASE2, + BTN_X +}; + static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, @@ -72,9 +96,27 @@ static int uclogic_input_mapping(struct hid_device *hdev, struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_params *params = &drvdata->params; - /* Discard invalid pen usages */ - if (params->pen.usage_invalid && (field->application == HID_DG_PEN)) - return -1; + if (field->application == HID_GD_KEYPAD) { + /* + * Remap input buttons to sensible ones that are not invalid. + * This only affects previous behavior for devices with more than ten or so buttons. + */ + const int key = (usage->hid & HID_USAGE) - 1; + + if (key < ARRAY_SIZE(uclogic_extra_input_mapping)) { + hid_map_usage(hi, + usage, + bit, + max, + EV_KEY, + uclogic_extra_input_mapping[key]); + return 1; + } + } else if (field->application == HID_DG_PEN) { + /* Discard invalid pen usages */ + if (params->pen.usage_invalid) + return -1; + } /* Let hid-core decide what to do */ return 0; @@ -85,10 +127,8 @@ static int uclogic_input_configured(struct hid_device *hdev, { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_params *params = &drvdata->params; - char *name; const char *suffix = NULL; struct hid_field *field; - size_t len; size_t i; const struct uclogic_params_frame *frame; @@ -144,15 +184,11 @@ static int uclogic_input_configured(struct hid_device *hdev, suffix = "System Control"; break; } - } - - if (suffix) { - len = strlen(hdev->name) + 2 + strlen(suffix); - name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); - if (name) { - snprintf(name, len, "%s %s", hdev->name, suffix); - hi->input->name = name; - } + } else { + hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s %s", hdev->name, suffix); + if (!hi->input->name) + return -ENOMEM; } return 0; @@ -326,6 +362,23 @@ static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata, data[8] = pressure_low_byte; data[9] = pressure_high_byte; } + if (size == 12 && pen->fragmented_hires2) { + // 00 00 when on the left side, 01 00 in the right + // we move these to the end of the x coord (u16) to create a correct x coord (u32) + u8 lsb_low_byte = data[10]; + u8 lsb_high_byte = data[11]; + + // shift everything right by 2 bytes, to make space for the moved lsb + data[11] = data[9]; + data[10] = data[8]; + data[9] = data[7]; + data[8] = data[6]; + data[7] = data[5]; + data[6] = data[4]; + + data[4] = lsb_low_byte; + data[5] = lsb_high_byte; + } /* If we need to emulate in-range detection */ if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { /* Set in-range bit */ @@ -413,8 +466,22 @@ static int uclogic_raw_event_frame( /* If need to, and can, transform the bitmap dial reports */ if (frame->bitmap_dial_byte > 0 && frame->bitmap_dial_byte < size) { - if (data[frame->bitmap_dial_byte] == 2) + switch (data[frame->bitmap_dial_byte]) { + case 2: data[frame->bitmap_dial_byte] = -1; + break; + + /* Everything below here is for tablets that shove multiple dials into 1 byte */ + case 16: + data[frame->bitmap_dial_byte] = 0; + data[frame->bitmap_second_dial_destination_byte] = 1; + break; + + case 32: + data[frame->bitmap_dial_byte] = 0; + data[frame->bitmap_second_dial_destination_byte] = -1; + break; + } } return 0; @@ -481,7 +548,7 @@ static void uclogic_remove(struct hid_device *hdev) { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - del_timer_sync(&drvdata->inrange_timer); + timer_delete_sync(&drvdata->inrange_timer); hid_hw_stop(hdev); kfree(drvdata->desc_ptr); uclogic_params_cleanup(&drvdata->params); @@ -552,6 +619,10 @@ static const struct hid_device_id uclogic_devices[] = { .driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK }, { 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_22R_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); @@ -574,7 +645,9 @@ module_hid_driver(uclogic_driver); MODULE_AUTHOR("Martin Rusko"); MODULE_AUTHOR("Nikolai Kondrashov"); +MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard"); #ifdef CONFIG_HID_KUNIT_TEST #include "hid-uclogic-core-test.c" |
