diff options
Diffstat (limited to 'drivers/hid/hid-ntrig.c')
-rw-r--r-- | drivers/hid/hid-ntrig.c | 519 |
1 files changed, 286 insertions, 233 deletions
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index beb403421e7..9fae2ebdd75 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -110,6 +110,36 @@ static int ntrig_version_string(unsigned char *raw, char *buf) return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e); } +static inline int ntrig_get_mode(struct hid_device *hdev) +{ + struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT]. + report_id_hash[0x0d]; + + if (!report) + return -EINVAL; + + usbhid_submit_report(hdev, report, USB_DIR_IN); + usbhid_wait_io(hdev); + return (int)report->field[0]->value[0]; +} + +static inline void ntrig_set_mode(struct hid_device *hdev, const int mode) +{ + struct hid_report *report; + __u8 mode_commands[4] = { 0xe, 0xf, 0x1b, 0x10 }; + + if (mode < 0 || mode > 3) + return; + + report = hdev->report_enum[HID_FEATURE_REPORT]. + report_id_hash[mode_commands[mode]]; + + if (!report) + return; + + usbhid_submit_report(hdev, report, USB_DIR_IN); +} + static void ntrig_report_version(struct hid_device *hdev) { int ret; @@ -539,277 +569,288 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int ntrig_event (struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { - struct input_dev *input = field->hidinput->input; struct ntrig_data *nd = hid_get_drvdata(hid); + struct input_dev *input; + + /* Skip processing if not a claimed input */ + if (!(hid->claimed & HID_CLAIMED_INPUT)) + goto not_claimed_input; + + /* This function is being called before the structures are fully + * initialized */ + if(!(field->hidinput && field->hidinput->input)) + return -EINVAL; + + input = field->hidinput->input; /* No special handling needed for the pen */ if (field->application == HID_DG_PEN) return 0; - if (hid->claimed & HID_CLAIMED_INPUT) { - switch (usage->hid) { - case 0xff000001: - /* Tag indicating the start of a multitouch group */ - nd->reading_mt = 1; - nd->first_contact_touch = 0; - break; - case HID_DG_TIPSWITCH: - nd->tipswitch = value; - /* Prevent emission of touch until validated */ - return 1; - case HID_DG_CONFIDENCE: - nd->confidence = value; - break; - case HID_GD_X: - nd->x = value; - /* Clear the contact footer */ - nd->mt_foot_count = 0; - break; - case HID_GD_Y: - nd->y = value; - break; - case HID_DG_CONTACTID: - nd->id = value; - break; - case HID_DG_WIDTH: - nd->w = value; - break; - case HID_DG_HEIGHT: - nd->h = value; + switch (usage->hid) { + case 0xff000001: + /* Tag indicating the start of a multitouch group */ + nd->reading_mt = 1; + nd->first_contact_touch = 0; + break; + case HID_DG_TIPSWITCH: + nd->tipswitch = value; + /* Prevent emission of touch until validated */ + return 1; + case HID_DG_CONFIDENCE: + nd->confidence = value; + break; + case HID_GD_X: + nd->x = value; + /* Clear the contact footer */ + nd->mt_foot_count = 0; + break; + case HID_GD_Y: + nd->y = value; + break; + case HID_DG_CONTACTID: + nd->id = value; + break; + case HID_DG_WIDTH: + nd->w = value; + break; + case HID_DG_HEIGHT: + nd->h = value; + /* + * when in single touch mode, this is the last + * report received in a finger event. We want + * to emit a normal (X, Y) position + */ + if (!nd->reading_mt) { /* - * when in single touch mode, this is the last - * report received in a finger event. We want - * to emit a normal (X, Y) position + * TipSwitch indicates the presence of a + * finger in single touch mode. */ - if (!nd->reading_mt) { - /* - * TipSwitch indicates the presence of a - * finger in single touch mode. - */ - input_report_key(input, BTN_TOUCH, - nd->tipswitch); - input_report_key(input, BTN_TOOL_DOUBLETAP, - nd->tipswitch); - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - } + input_report_key(input, BTN_TOUCH, + nd->tipswitch); + input_report_key(input, BTN_TOOL_DOUBLETAP, + nd->tipswitch); + input_event(input, EV_ABS, ABS_X, nd->x); + input_event(input, EV_ABS, ABS_Y, nd->y); + } + break; + case 0xff000002: + /* + * we receive this when the device is in multitouch + * mode. The first of the three values tagged with + * this usage tells if the contact point is real + * or a placeholder + */ + + /* Shouldn't get more than 4 footer packets, so skip */ + if (nd->mt_foot_count >= 4) break; - case 0xff000002: - /* - * we receive this when the device is in multitouch - * mode. The first of the three values tagged with - * this usage tells if the contact point is real - * or a placeholder - */ - /* Shouldn't get more than 4 footer packets, so skip */ - if (nd->mt_foot_count >= 4) - break; + nd->mt_footer[nd->mt_foot_count++] = value; - nd->mt_footer[nd->mt_foot_count++] = value; + /* if the footer isn't complete break */ + if (nd->mt_foot_count != 4) + break; - /* if the footer isn't complete break */ - if (nd->mt_foot_count != 4) - break; + /* Pen activity signal. */ + if (nd->mt_footer[2]) { + /* + * When the pen deactivates touch, we see a + * bogus frame with ContactCount > 0. + * We can + * save a bit of work by ensuring act_state < 0 + * even if deactivation slack is turned off. + */ + nd->act_state = deactivate_slack - 1; + nd->confidence = 0; + break; + } - /* Pen activity signal. */ - if (nd->mt_footer[2]) { - /* - * When the pen deactivates touch, we see a - * bogus frame with ContactCount > 0. - * We can - * save a bit of work by ensuring act_state < 0 - * even if deactivation slack is turned off. - */ - nd->act_state = deactivate_slack - 1; + /* + * The first footer value indicates the presence of a + * finger. + */ + if (nd->mt_footer[0]) { + /* + * We do not want to process contacts under + * the size threshold, but do not want to + * ignore them for activation state + */ + if (nd->w < nd->min_width || + nd->h < nd->min_height) nd->confidence = 0; - break; - } + } else + break; + if (nd->act_state > 0) { /* - * The first footer value indicates the presence of a - * finger. + * Contact meets the activation size threshold */ - if (nd->mt_footer[0]) { - /* - * We do not want to process contacts under - * the size threshold, but do not want to - * ignore them for activation state - */ - if (nd->w < nd->min_width || - nd->h < nd->min_height) - nd->confidence = 0; - } else - break; - - if (nd->act_state > 0) { - /* - * Contact meets the activation size threshold - */ - if (nd->w >= nd->activation_width && - nd->h >= nd->activation_height) { - if (nd->id) - /* - * first contact, activate now - */ - nd->act_state = 0; - else { - /* - * avoid corrupting this frame - * but ensure next frame will - * be active - */ - nd->act_state = 1; - break; - } - } else + if (nd->w >= nd->activation_width && + nd->h >= nd->activation_height) { + if (nd->id) /* - * Defer adjusting the activation state - * until the end of the frame. + * first contact, activate now */ + nd->act_state = 0; + else { + /* + * avoid corrupting this frame + * but ensure next frame will + * be active + */ + nd->act_state = 1; break; - } - - /* Discarding this contact */ - if (!nd->confidence) - break; - - /* emit a normal (X, Y) for the first point only */ - if (nd->id == 0) { + } + } else /* - * TipSwitch is superfluous in multitouch - * mode. The footer events tell us - * if there is a finger on the screen or - * not. + * Defer adjusting the activation state + * until the end of the frame. */ - nd->first_contact_touch = nd->confidence; - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - } + break; + } - /* Emit MT events */ - input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); + /* Discarding this contact */ + if (!nd->confidence) + break; + /* emit a normal (X, Y) for the first point only */ + if (nd->id == 0) { /* - * Translate from height and width to size - * and orientation. + * TipSwitch is superfluous in multitouch + * mode. The footer events tell us + * if there is a finger on the screen or + * not. */ - if (nd->w > nd->h) { - input_event(input, EV_ABS, - ABS_MT_ORIENTATION, 1); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MAJOR, nd->w); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MINOR, nd->h); - } else { - input_event(input, EV_ABS, - ABS_MT_ORIENTATION, 0); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MAJOR, nd->h); - input_event(input, EV_ABS, - ABS_MT_TOUCH_MINOR, nd->w); - } - input_mt_sync(field->hidinput->input); - break; + nd->first_contact_touch = nd->confidence; + input_event(input, EV_ABS, ABS_X, nd->x); + input_event(input, EV_ABS, ABS_Y, nd->y); + } - case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ - if (!nd->reading_mt) /* Just to be sure */ - break; + /* Emit MT events */ + input_event(input, EV_ABS, ABS_MT_POSITION_X, nd->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, nd->y); + + /* + * Translate from height and width to size + * and orientation. + */ + if (nd->w > nd->h) { + input_event(input, EV_ABS, + ABS_MT_ORIENTATION, 1); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MAJOR, nd->w); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MINOR, nd->h); + } else { + input_event(input, EV_ABS, + ABS_MT_ORIENTATION, 0); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MAJOR, nd->h); + input_event(input, EV_ABS, + ABS_MT_TOUCH_MINOR, nd->w); + } + input_mt_sync(field->hidinput->input); + break; - nd->reading_mt = 0; + case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ + if (!nd->reading_mt) /* Just to be sure */ + break; + nd->reading_mt = 0; + + + /* + * Activation state machine logic: + * + * Fundamental states: + * state > 0: Inactive + * state <= 0: Active + * state < -deactivate_slack: + * Pen termination of touch + * + * Specific values of interest + * state == activate_slack + * no valid input since the last reset + * + * state == 0 + * general operational state + * + * state == -deactivate_slack + * read sufficient empty frames to accept + * the end of input and reset + */ + + if (nd->act_state > 0) { /* Currently inactive */ + if (value) + /* + * Consider each live contact as + * evidence of intentional activity. + */ + nd->act_state = (nd->act_state > value) + ? nd->act_state - value + : 0; + else + /* + * Empty frame before we hit the + * activity threshold, reset. + */ + nd->act_state = nd->activate_slack; /* - * Activation state machine logic: - * - * Fundamental states: - * state > 0: Inactive - * state <= 0: Active - * state < -deactivate_slack: - * Pen termination of touch - * - * Specific values of interest - * state == activate_slack - * no valid input since the last reset - * - * state == 0 - * general operational state - * - * state == -deactivate_slack - * read sufficient empty frames to accept - * the end of input and reset + * Entered this block inactive and no + * coordinates sent this frame, so hold off + * on button state. */ - - if (nd->act_state > 0) { /* Currently inactive */ - if (value) - /* - * Consider each live contact as - * evidence of intentional activity. - */ - nd->act_state = (nd->act_state > value) - ? nd->act_state - value - : 0; - else - /* - * Empty frame before we hit the - * activity threshold, reset. - */ - nd->act_state = nd->activate_slack; - + break; + } else { /* Currently active */ + if (value && nd->act_state >= + nd->deactivate_slack) /* - * Entered this block inactive and no - * coordinates sent this frame, so hold off - * on button state. + * Live point: clear accumulated + * deactivation count. */ - break; - } else { /* Currently active */ - if (value && nd->act_state >= - nd->deactivate_slack) - /* - * Live point: clear accumulated - * deactivation count. - */ - nd->act_state = 0; - else if (nd->act_state <= nd->deactivate_slack) - /* - * We've consumed the deactivation - * slack, time to deactivate and reset. - */ - nd->act_state = - nd->activate_slack; - else { /* Move towards deactivation */ - nd->act_state--; - break; - } - } - - if (nd->first_contact_touch && nd->act_state <= 0) { + nd->act_state = 0; + else if (nd->act_state <= nd->deactivate_slack) /* - * Check to see if we're ready to start - * emitting touch events. - * - * Note: activation slack will decrease over - * the course of the frame, and it will be - * inconsistent from the start to the end of - * the frame. However if the frame starts - * with slack, first_contact_touch will still - * be 0 and we will not get to this point. + * We've consumed the deactivation + * slack, time to deactivate and reset. */ - input_report_key(input, BTN_TOOL_DOUBLETAP, 1); - input_report_key(input, BTN_TOUCH, 1); - } else { - input_report_key(input, BTN_TOOL_DOUBLETAP, 0); - input_report_key(input, BTN_TOUCH, 0); + nd->act_state = + nd->activate_slack; + else { /* Move towards deactivation */ + nd->act_state--; + break; } - break; + } - default: - /* fall-back to the generic hidinput handling */ - return 0; + if (nd->first_contact_touch && nd->act_state <= 0) { + /* + * Check to see if we're ready to start + * emitting touch events. + * + * Note: activation slack will decrease over + * the course of the frame, and it will be + * inconsistent from the start to the end of + * the frame. However if the frame starts + * with slack, first_contact_touch will still + * be 0 and we will not get to this point. + */ + input_report_key(input, BTN_TOOL_DOUBLETAP, 1); + input_report_key(input, BTN_TOUCH, 1); + } else { + input_report_key(input, BTN_TOOL_DOUBLETAP, 0); + input_report_key(input, BTN_TOUCH, 0); } + break; + + default: + /* fall-back to the generic hidinput handling */ + return 0; } +not_claimed_input: + /* we have handled the hidinput part, now remains hiddev */ if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) hid->hiddev_hid_event(hid, field, usage, value); @@ -826,7 +867,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) struct hid_report *report; if (id->driver_data) - hdev->quirks |= HID_QUIRK_MULTI_INPUT; + hdev->quirks |= HID_QUIRK_MULTI_INPUT + | HID_QUIRK_NO_INIT_REPORTS; nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL); if (!nd) { @@ -893,8 +935,19 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) /* This is needed for devices with more recent firmware versions */ report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[0x0a]; - if (report) - usbhid_submit_report(hdev, report, USB_DIR_OUT); + if (report) { + /* Let the device settle to ensure the wakeup message gets + * through */ + usbhid_wait_io(hdev); + usbhid_submit_report(hdev, report, USB_DIR_IN); + + /* + * Sanity check: if the current mode is invalid reset it to + * something reasonable. + */ + if (ntrig_get_mode(hdev) >= 4) + ntrig_set_mode(hdev, 3); + } ntrig_report_version(hdev); |