diff options
Diffstat (limited to 'drivers/hid/hid-uclogic-core.c')
-rw-r--r-- | drivers/hid/hid-uclogic-core.c | 258 |
1 files changed, 155 insertions, 103 deletions
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index d8ab0139e5cd..05147f2d7564 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -81,24 +81,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } -static int uclogic_input_mapping(struct hid_device *hdev, - struct hid_input *hi, - struct hid_field *field, - struct hid_usage *usage, - unsigned long **bit, - int *max) -{ - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - struct uclogic_params *params = &drvdata->params; - - /* discard the unused pen interface */ - if (params->pen_unused && (field->application == HID_DG_PEN)) - return -1; - - /* let hid-core decide what to do */ - return 0; -} - static int uclogic_input_configured(struct hid_device *hdev, struct hid_input *hi) { @@ -246,100 +228,171 @@ static int uclogic_resume(struct hid_device *hdev) } #endif +/** + * uclogic_raw_event_pen - handle raw pen events (pen HID reports). + * + * @drvdata: Driver data. + * @data: Report data buffer, can be modified. + * @size: Report data size, bytes. + * + * Returns: + * Negative value on error (stops event delivery), zero for success. + */ +static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata, + u8 *data, int size) +{ + struct uclogic_params_pen *pen = &drvdata->params.pen; + + WARN_ON(drvdata == NULL); + WARN_ON(data == NULL && size != 0); + + /* If in-range reports are inverted */ + if (pen->inrange == + UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) { + /* Invert the in-range bit */ + data[1] ^= 0x40; + } + /* + * If report contains fragmented high-resolution pen + * coordinates + */ + if (size >= 10 && pen->fragmented_hires) { + u8 pressure_low_byte; + u8 pressure_high_byte; + + /* Lift pressure bytes */ + pressure_low_byte = data[6]; + pressure_high_byte = data[7]; + /* + * Move Y coord to make space for high-order X + * coord byte + */ + data[6] = data[5]; + data[5] = data[4]; + /* Move high-order X coord byte */ + data[4] = data[8]; + /* Move high-order Y coord byte */ + data[7] = data[9]; + /* Place pressure bytes */ + data[8] = pressure_low_byte; + data[9] = pressure_high_byte; + } + /* If we need to emulate in-range detection */ + if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { + /* Set in-range bit */ + data[1] |= 0x40; + /* (Re-)start in-range timeout */ + mod_timer(&drvdata->inrange_timer, + jiffies + msecs_to_jiffies(100)); + } + /* If we report tilt and Y direction is flipped */ + if (size >= 12 && pen->tilt_y_flipped) + data[11] = -data[11]; + + return 0; +} + +/** + * uclogic_raw_event_frame - handle raw frame events (frame HID reports). + * + * @drvdata: Driver data. + * @frame: The parameters of the frame controls to handle. + * @data: Report data buffer, can be modified. + * @size: Report data size, bytes. + * + * Returns: + * Negative value on error (stops event delivery), zero for success. + */ +static int uclogic_raw_event_frame( + struct uclogic_drvdata *drvdata, + const struct uclogic_params_frame *frame, + u8 *data, int size) +{ + WARN_ON(drvdata == NULL); + WARN_ON(data == NULL && size != 0); + + /* If need to, and can, set pad device ID for Wacom drivers */ + if (frame->dev_id_byte > 0 && frame->dev_id_byte < size) { + data[frame->dev_id_byte] = 0xf; + } + /* If need to, and can, read rotary encoder state change */ + if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) { + unsigned int byte = frame->re_lsb / 8; + unsigned int bit = frame->re_lsb % 8; + + u8 change; + u8 prev_state = drvdata->re_state; + /* Read Gray-coded state */ + u8 state = (data[byte] >> bit) & 0x3; + /* Encode state change into 2-bit signed integer */ + if ((prev_state == 1 && state == 0) || + (prev_state == 2 && state == 3)) { + change = 1; + } else if ((prev_state == 2 && state == 0) || + (prev_state == 1 && state == 3)) { + change = 3; + } else { + change = 0; + } + /* Write change */ + data[byte] = (data[byte] & ~((u8)3 << bit)) | + (change << bit); + /* Remember state */ + drvdata->re_state = state; + } + + return 0; +} + static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { + unsigned int report_id = report->id; struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); struct uclogic_params *params = &drvdata->params; + struct uclogic_params_pen_subreport *subreport; + struct uclogic_params_pen_subreport *subreport_list_end; + size_t i; - /* Tweak pen reports, if necessary */ - if (!params->pen_unused && - (report->type == HID_INPUT_REPORT) && - (report->id == params->pen.id) && - (size >= 2)) { - /* If it's the "virtual" frame controls report */ - if (params->frame.id != 0 && - data[1] & params->pen_frame_flag) { - /* Change to virtual frame controls report ID */ - data[0] = params->frame.id; - return 0; - } - /* If in-range reports are inverted */ - if (params->pen.inrange == - UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) { - /* Invert the in-range bit */ - data[1] ^= 0x40; - } - /* - * If report contains fragmented high-resolution pen - * coordinates - */ - if (size >= 10 && params->pen.fragmented_hires) { - u8 pressure_low_byte; - u8 pressure_high_byte; - - /* Lift pressure bytes */ - pressure_low_byte = data[6]; - pressure_high_byte = data[7]; - /* - * Move Y coord to make space for high-order X - * coord byte - */ - data[6] = data[5]; - data[5] = data[4]; - /* Move high-order X coord byte */ - data[4] = data[8]; - /* Move high-order Y coord byte */ - data[7] = data[9]; - /* Place pressure bytes */ - data[8] = pressure_low_byte; - data[9] = pressure_high_byte; - } - /* If we need to emulate in-range detection */ - if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { - /* Set in-range bit */ - data[1] |= 0x40; - /* (Re-)start in-range timeout */ - mod_timer(&drvdata->inrange_timer, - jiffies + msecs_to_jiffies(100)); - } - } + /* Do not handle anything but input reports */ + if (report->type != HID_INPUT_REPORT) + return 0; - /* Tweak frame control reports, if necessary */ - if ((report->type == HID_INPUT_REPORT) && - (report->id == params->frame.id)) { - /* If need to, and can, set pad device ID for Wacom drivers */ - if (params->frame.dev_id_byte > 0 && - params->frame.dev_id_byte < size) { - data[params->frame.dev_id_byte] = 0xf; - } - /* If need to, and can, read rotary encoder state change */ - if (params->frame.re_lsb > 0 && - params->frame.re_lsb / 8 < size) { - unsigned int byte = params->frame.re_lsb / 8; - unsigned int bit = params->frame.re_lsb % 8; - - u8 change; - u8 prev_state = drvdata->re_state; - /* Read Gray-coded state */ - u8 state = (data[byte] >> bit) & 0x3; - /* Encode state change into 2-bit signed integer */ - if ((prev_state == 1 && state == 0) || - (prev_state == 2 && state == 3)) { - change = 1; - } else if ((prev_state == 2 && state == 0) || - (prev_state == 1 && state == 3)) { - change = 3; + while (true) { + /* Tweak pen reports, if necessary */ + if ((report_id == params->pen.id) && (size >= 2)) { + subreport_list_end = + params->pen.subreport_list + + ARRAY_SIZE(params->pen.subreport_list); + /* Try to match a subreport */ + for (subreport = params->pen.subreport_list; + subreport < subreport_list_end; subreport++) { + if (subreport->value != 0 && + subreport->value == data[1]) { + break; + } + } + /* If a subreport matched */ + if (subreport < subreport_list_end) { + /* Change to subreport ID, and restart */ + report_id = data[0] = subreport->id; + continue; } else { - change = 0; + return uclogic_raw_event_pen(drvdata, data, size); + } + } + + /* Tweak frame control reports, if necessary */ + for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { + if (report_id == params->frame_list[i].id) { + return uclogic_raw_event_frame( + drvdata, ¶ms->frame_list[i], + data, size); } - /* Write change */ - data[byte] = (data[byte] & ~((u8)3 << bit)) | - (change << bit); - /* Remember state */ - drvdata->re_state = state; } + + break; } return 0; @@ -373,7 +426,7 @@ static const struct hid_device_id uclogic_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_HUION, - USB_DEVICE_ID_HUION_HS64) }, + USB_DEVICE_ID_HUION_TABLET2) }, { HID_USB_DEVICE(USB_VENDOR_ID_TRUST, USB_DEVICE_ID_TRUST_PANORA_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, @@ -415,7 +468,6 @@ static struct hid_driver uclogic_driver = { .remove = uclogic_remove, .report_fixup = uclogic_report_fixup, .raw_event = uclogic_raw_event, - .input_mapping = uclogic_input_mapping, .input_configured = uclogic_input_configured, #ifdef CONFIG_PM .resume = uclogic_resume, |