Commit bc5d0482 authored by Adam Kropelin's avatar Adam Kropelin Committed by Dmitry Torokhov

Input: HID - only report events coming from interrupts to hiddev

Currently hid-core follows the same code path for input reports
regardless of whether they are a result of interrupt transfers or
control transfers. That leads to interrupt events erroneously being
reported to hiddev for regular control transfers.

Prior to 2.6.12 the problem was mitigated by the fact that
reporting to hiddev is supressed if the field value has not changed,
which is often the case. Said filtering was removed in 2.6.12-rc1 which
means any input reports fetched via control transfers result in hiddev
interrupt events. This behavior can quickly lead to a feedback loop
where a userspace app, in response to interrupt events, issues control
transfers which in turn create more interrupt events.

This patch prevents input reports that arrive via control transfers from
being reported to hiddev as interrupt events.
Signed-off-by: default avatarAdam Kropelin <akropel1@rochester.rr.com>
Signed-off-by: default avatarVojtech Pavlik <vojtech@suse.cz>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 153ab429
...@@ -789,12 +789,12 @@ static __inline__ int search(__s32 *array, __s32 value, unsigned n) ...@@ -789,12 +789,12 @@ static __inline__ int search(__s32 *array, __s32 value, unsigned n)
return -1; return -1;
} }
static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, struct pt_regs *regs) static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt, struct pt_regs *regs)
{ {
hid_dump_input(usage, value); hid_dump_input(usage, value);
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_hid_event(hid, field, usage, value, regs); hidinput_hid_event(hid, field, usage, value, regs);
if (hid->claimed & HID_CLAIMED_HIDDEV) if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt)
hiddev_hid_event(hid, field, usage, value, regs); hiddev_hid_event(hid, field, usage, value, regs);
} }
...@@ -804,7 +804,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s ...@@ -804,7 +804,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s
* reporting to the layer). * reporting to the layer).
*/ */
static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, struct pt_regs *regs) static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt, struct pt_regs *regs)
{ {
unsigned n; unsigned n;
unsigned count = field->report_count; unsigned count = field->report_count;
...@@ -831,19 +831,19 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u ...@@ -831,19 +831,19 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
if (HID_MAIN_ITEM_VARIABLE & field->flags) { if (HID_MAIN_ITEM_VARIABLE & field->flags) {
hid_process_event(hid, field, &field->usage[n], value[n], regs); hid_process_event(hid, field, &field->usage[n], value[n], interrupt, regs);
continue; continue;
} }
if (field->value[n] >= min && field->value[n] <= max if (field->value[n] >= min && field->value[n] <= max
&& field->usage[field->value[n] - min].hid && field->usage[field->value[n] - min].hid
&& search(value, field->value[n], count)) && search(value, field->value[n], count))
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, regs); hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt, regs);
if (value[n] >= min && value[n] <= max if (value[n] >= min && value[n] <= max
&& field->usage[value[n] - min].hid && field->usage[value[n] - min].hid
&& search(field->value, value[n], count)) && search(field->value, value[n], count))
hid_process_event(hid, field, &field->usage[value[n] - min], 1, regs); hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt, regs);
} }
memcpy(field->value, value, count * sizeof(__s32)); memcpy(field->value, value, count * sizeof(__s32));
...@@ -851,7 +851,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u ...@@ -851,7 +851,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, __u
kfree(value); kfree(value);
} }
static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs) static int hid_input_report(int type, struct urb *urb, int interrupt, struct pt_regs *regs)
{ {
struct hid_device *hid = urb->context; struct hid_device *hid = urb->context;
struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report_enum *report_enum = hid->report_enum + type;
...@@ -899,7 +899,7 @@ static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs) ...@@ -899,7 +899,7 @@ static int hid_input_report(int type, struct urb *urb, struct pt_regs *regs)
hiddev_report_event(hid, report); hiddev_report_event(hid, report);
for (n = 0; n < report->maxfield; n++) for (n = 0; n < report->maxfield; n++)
hid_input_field(hid, report->field[n], data, regs); hid_input_field(hid, report->field[n], data, interrupt, regs);
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report); hidinput_report_event(hid, report);
...@@ -918,7 +918,7 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs) ...@@ -918,7 +918,7 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
switch (urb->status) { switch (urb->status) {
case 0: /* success */ case 0: /* success */
hid_input_report(HID_INPUT_REPORT, urb, regs); hid_input_report(HID_INPUT_REPORT, urb, 1, regs);
break; break;
case -ECONNRESET: /* unlink */ case -ECONNRESET: /* unlink */
case -ENOENT: case -ENOENT:
...@@ -1142,7 +1142,7 @@ static void hid_ctrl(struct urb *urb, struct pt_regs *regs) ...@@ -1142,7 +1142,7 @@ static void hid_ctrl(struct urb *urb, struct pt_regs *regs)
switch (urb->status) { switch (urb->status) {
case 0: /* success */ case 0: /* success */
if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN) if (hid->ctrl[hid->ctrltail].dir == USB_DIR_IN)
hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, regs); hid_input_report(hid->ctrl[hid->ctrltail].report->type, urb, 0, regs);
case -ESHUTDOWN: /* unplug */ case -ESHUTDOWN: /* unplug */
case -EILSEQ: /* unplug timectrl on uhci */ case -EILSEQ: /* unplug timectrl on uhci */
unplug = 1; unplug = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment