Merge branch 'for-5.18/core' into for-linus
- rework of generic input handling which ultimately makes the processing of tablet events more generic and reliable (Benjamin Tissoires)
This commit is contained in:
commit
a2ff005927
@ -137,7 +137,11 @@ A few EV_KEY codes have special meanings:
|
|||||||
code should be set to a value of 1. When the tool is no longer interacting
|
code should be set to a value of 1. When the tool is no longer interacting
|
||||||
with the input device, the BTN_TOOL_<name> code should be reset to 0. All
|
with the input device, the BTN_TOOL_<name> code should be reset to 0. All
|
||||||
trackpads, tablets, and touchscreens should use at least one BTN_TOOL_<name>
|
trackpads, tablets, and touchscreens should use at least one BTN_TOOL_<name>
|
||||||
code when events are generated.
|
code when events are generated. Likewise all trackpads, tablets, and
|
||||||
|
touchscreens should export only one BTN_TOOL_<name> at a time. To not break
|
||||||
|
existing userspace, it is recommended to not switch tool in one EV_SYN frame
|
||||||
|
but first emitting the old BTN_TOOL_<name> at 0, then emit one SYN_REPORT
|
||||||
|
and then set the new BTN_TOOL_<name> at 1.
|
||||||
|
|
||||||
* BTN_TOUCH:
|
* BTN_TOUCH:
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ struct hid_report *hid_register_report(struct hid_device *device,
|
|||||||
report_enum->report_id_hash[id] = report;
|
report_enum->report_id_hash[id] = report;
|
||||||
|
|
||||||
list_add_tail(&report->list, &report_enum->report_list);
|
list_add_tail(&report->list, &report_enum->report_list);
|
||||||
|
INIT_LIST_HEAD(&report->field_entry_list);
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
}
|
}
|
||||||
@ -101,7 +102,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
|
|||||||
|
|
||||||
field = kzalloc((sizeof(struct hid_field) +
|
field = kzalloc((sizeof(struct hid_field) +
|
||||||
usages * sizeof(struct hid_usage) +
|
usages * sizeof(struct hid_usage) +
|
||||||
usages * sizeof(unsigned)), GFP_KERNEL);
|
3 * usages * sizeof(unsigned int)), GFP_KERNEL);
|
||||||
if (!field)
|
if (!field)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -109,6 +110,8 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
|
|||||||
report->field[field->index] = field;
|
report->field[field->index] = field;
|
||||||
field->usage = (struct hid_usage *)(field + 1);
|
field->usage = (struct hid_usage *)(field + 1);
|
||||||
field->value = (s32 *)(field->usage + usages);
|
field->value = (s32 *)(field->usage + usages);
|
||||||
|
field->new_value = (s32 *)(field->value + usages);
|
||||||
|
field->usages_priorities = (s32 *)(field->new_value + usages);
|
||||||
field->report = report;
|
field->report = report;
|
||||||
|
|
||||||
return field;
|
return field;
|
||||||
@ -656,6 +659,8 @@ static void hid_free_report(struct hid_report *report)
|
|||||||
{
|
{
|
||||||
unsigned n;
|
unsigned n;
|
||||||
|
|
||||||
|
kfree(report->field_entries);
|
||||||
|
|
||||||
for (n = 0; n < report->maxfield; n++)
|
for (n = 0; n < report->maxfield; n++)
|
||||||
kfree(report->field[n]);
|
kfree(report->field[n]);
|
||||||
kfree(report);
|
kfree(report);
|
||||||
@ -1525,25 +1530,41 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Analyse a received field, and fetch the data from it. The field
|
* Checks if the given value is valid within this field
|
||||||
* content is stored for next report processing (we do differential
|
|
||||||
* reporting to the layer).
|
|
||||||
*/
|
*/
|
||||||
|
static inline int hid_array_value_is_valid(struct hid_field *field,
|
||||||
|
__s32 value)
|
||||||
|
{
|
||||||
|
__s32 min = field->logical_minimum;
|
||||||
|
|
||||||
static void hid_input_field(struct hid_device *hid, struct hid_field *field,
|
/*
|
||||||
__u8 *data, int interrupt)
|
* Value needs to be between logical min and max, and
|
||||||
|
* (value - min) is used as an index in the usage array.
|
||||||
|
* This array is of size field->maxusage
|
||||||
|
*/
|
||||||
|
return value >= min &&
|
||||||
|
value <= field->logical_maximum &&
|
||||||
|
value - min < field->maxusage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch the field from the data. The field content is stored for next
|
||||||
|
* report processing (we do differential reporting to the layer).
|
||||||
|
*/
|
||||||
|
static void hid_input_fetch_field(struct hid_device *hid,
|
||||||
|
struct hid_field *field,
|
||||||
|
__u8 *data)
|
||||||
{
|
{
|
||||||
unsigned n;
|
unsigned n;
|
||||||
unsigned count = field->report_count;
|
unsigned count = field->report_count;
|
||||||
unsigned offset = field->report_offset;
|
unsigned offset = field->report_offset;
|
||||||
unsigned size = field->report_size;
|
unsigned size = field->report_size;
|
||||||
__s32 min = field->logical_minimum;
|
__s32 min = field->logical_minimum;
|
||||||
__s32 max = field->logical_maximum;
|
|
||||||
__s32 *value;
|
__s32 *value;
|
||||||
|
|
||||||
value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC);
|
value = field->new_value;
|
||||||
if (!value)
|
memset(value, 0, count * sizeof(__s32));
|
||||||
return;
|
field->ignored = false;
|
||||||
|
|
||||||
for (n = 0; n < count; n++) {
|
for (n = 0; n < count; n++) {
|
||||||
|
|
||||||
@ -1554,35 +1575,228 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field,
|
|||||||
|
|
||||||
/* Ignore report if ErrorRollOver */
|
/* Ignore report if ErrorRollOver */
|
||||||
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
if (!(field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
||||||
value[n] >= min && value[n] <= max &&
|
hid_array_value_is_valid(field, value[n]) &&
|
||||||
value[n] - min < field->maxusage &&
|
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) {
|
||||||
field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1)
|
field->ignored = true;
|
||||||
goto exit;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process a received variable field.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void hid_input_var_field(struct hid_device *hid,
|
||||||
|
struct hid_field *field,
|
||||||
|
int interrupt)
|
||||||
|
{
|
||||||
|
unsigned int count = field->report_count;
|
||||||
|
__s32 *value = field->new_value;
|
||||||
|
unsigned int n;
|
||||||
|
|
||||||
|
for (n = 0; n < count; n++)
|
||||||
|
hid_process_event(hid,
|
||||||
|
field,
|
||||||
|
&field->usage[n],
|
||||||
|
value[n],
|
||||||
|
interrupt);
|
||||||
|
|
||||||
|
memcpy(field->value, value, count * sizeof(__s32));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process a received array field. The field content is stored for
|
||||||
|
* next report processing (we do differential reporting to the layer).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void hid_input_array_field(struct hid_device *hid,
|
||||||
|
struct hid_field *field,
|
||||||
|
int interrupt)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
unsigned int count = field->report_count;
|
||||||
|
__s32 min = field->logical_minimum;
|
||||||
|
__s32 *value;
|
||||||
|
|
||||||
|
value = field->new_value;
|
||||||
|
|
||||||
|
/* ErrorRollOver */
|
||||||
|
if (field->ignored)
|
||||||
|
return;
|
||||||
|
|
||||||
for (n = 0; n < count; n++) {
|
for (n = 0; n < count; n++) {
|
||||||
|
if (hid_array_value_is_valid(field, field->value[n]) &&
|
||||||
|
search(value, field->value[n], count))
|
||||||
|
hid_process_event(hid,
|
||||||
|
field,
|
||||||
|
&field->usage[field->value[n] - min],
|
||||||
|
0,
|
||||||
|
interrupt);
|
||||||
|
|
||||||
if (HID_MAIN_ITEM_VARIABLE & field->flags) {
|
if (hid_array_value_is_valid(field, value[n]) &&
|
||||||
hid_process_event(hid, field, &field->usage[n], value[n], interrupt);
|
search(field->value, value[n], count))
|
||||||
continue;
|
hid_process_event(hid,
|
||||||
}
|
field,
|
||||||
|
&field->usage[value[n] - min],
|
||||||
if (field->value[n] >= min && field->value[n] <= max
|
1,
|
||||||
&& field->value[n] - min < field->maxusage
|
interrupt);
|
||||||
&& field->usage[field->value[n] - min].hid
|
|
||||||
&& search(value, field->value[n], count))
|
|
||||||
hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt);
|
|
||||||
|
|
||||||
if (value[n] >= min && value[n] <= max
|
|
||||||
&& value[n] - min < field->maxusage
|
|
||||||
&& field->usage[value[n] - min].hid
|
|
||||||
&& search(field->value, value[n], count))
|
|
||||||
hid_process_event(hid, field, &field->usage[value[n] - min], 1, interrupt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(field->value, value, count * sizeof(__s32));
|
memcpy(field->value, value, count * sizeof(__s32));
|
||||||
exit:
|
}
|
||||||
kfree(value);
|
|
||||||
|
/*
|
||||||
|
* Analyse a received report, and fetch the data from it. The field
|
||||||
|
* content is stored for next report processing (we do differential
|
||||||
|
* reporting to the layer).
|
||||||
|
*/
|
||||||
|
static void hid_process_report(struct hid_device *hid,
|
||||||
|
struct hid_report *report,
|
||||||
|
__u8 *data,
|
||||||
|
int interrupt)
|
||||||
|
{
|
||||||
|
unsigned int a;
|
||||||
|
struct hid_field_entry *entry;
|
||||||
|
struct hid_field *field;
|
||||||
|
|
||||||
|
/* first retrieve all incoming values in data */
|
||||||
|
for (a = 0; a < report->maxfield; a++)
|
||||||
|
hid_input_fetch_field(hid, field = report->field[a], data);
|
||||||
|
|
||||||
|
if (!list_empty(&report->field_entry_list)) {
|
||||||
|
/* INPUT_REPORT, we have a priority list of fields */
|
||||||
|
list_for_each_entry(entry,
|
||||||
|
&report->field_entry_list,
|
||||||
|
list) {
|
||||||
|
field = entry->field;
|
||||||
|
|
||||||
|
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||||
|
hid_process_event(hid,
|
||||||
|
field,
|
||||||
|
&field->usage[entry->index],
|
||||||
|
field->new_value[entry->index],
|
||||||
|
interrupt);
|
||||||
|
else
|
||||||
|
hid_input_array_field(hid, field, interrupt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we need to do the memcpy at the end for var items */
|
||||||
|
for (a = 0; a < report->maxfield; a++) {
|
||||||
|
field = report->field[a];
|
||||||
|
|
||||||
|
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||||
|
memcpy(field->value, field->new_value,
|
||||||
|
field->report_count * sizeof(__s32));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* FEATURE_REPORT, regular processing */
|
||||||
|
for (a = 0; a < report->maxfield; a++) {
|
||||||
|
field = report->field[a];
|
||||||
|
|
||||||
|
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||||
|
hid_input_var_field(hid, field, interrupt);
|
||||||
|
else
|
||||||
|
hid_input_array_field(hid, field, interrupt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a given usage_index in a field in the list
|
||||||
|
* of processed usages in the report.
|
||||||
|
*
|
||||||
|
* The elements of lower priority score are processed
|
||||||
|
* first.
|
||||||
|
*/
|
||||||
|
static void __hid_insert_field_entry(struct hid_device *hid,
|
||||||
|
struct hid_report *report,
|
||||||
|
struct hid_field_entry *entry,
|
||||||
|
struct hid_field *field,
|
||||||
|
unsigned int usage_index)
|
||||||
|
{
|
||||||
|
struct hid_field_entry *next;
|
||||||
|
|
||||||
|
entry->field = field;
|
||||||
|
entry->index = usage_index;
|
||||||
|
entry->priority = field->usages_priorities[usage_index];
|
||||||
|
|
||||||
|
/* insert the element at the correct position */
|
||||||
|
list_for_each_entry(next,
|
||||||
|
&report->field_entry_list,
|
||||||
|
list) {
|
||||||
|
/*
|
||||||
|
* the priority of our element is strictly higher
|
||||||
|
* than the next one, insert it before
|
||||||
|
*/
|
||||||
|
if (entry->priority > next->priority) {
|
||||||
|
list_add_tail(&entry->list, &next->list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lowest priority score: insert at the end */
|
||||||
|
list_add_tail(&entry->list, &report->field_entry_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hid_report_process_ordering(struct hid_device *hid,
|
||||||
|
struct hid_report *report)
|
||||||
|
{
|
||||||
|
struct hid_field *field;
|
||||||
|
struct hid_field_entry *entries;
|
||||||
|
unsigned int a, u, usages;
|
||||||
|
unsigned int count = 0;
|
||||||
|
|
||||||
|
/* count the number of individual fields in the report */
|
||||||
|
for (a = 0; a < report->maxfield; a++) {
|
||||||
|
field = report->field[a];
|
||||||
|
|
||||||
|
if (field->flags & HID_MAIN_ITEM_VARIABLE)
|
||||||
|
count += field->report_count;
|
||||||
|
else
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate the memory to process the fields */
|
||||||
|
entries = kcalloc(count, sizeof(*entries), GFP_KERNEL);
|
||||||
|
if (!entries)
|
||||||
|
return;
|
||||||
|
|
||||||
|
report->field_entries = entries;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* walk through all fields in the report and
|
||||||
|
* store them by priority order in report->field_entry_list
|
||||||
|
*
|
||||||
|
* - Var elements are individualized (field + usage_index)
|
||||||
|
* - Arrays are taken as one, we can not chose an order for them
|
||||||
|
*/
|
||||||
|
usages = 0;
|
||||||
|
for (a = 0; a < report->maxfield; a++) {
|
||||||
|
field = report->field[a];
|
||||||
|
|
||||||
|
if (field->flags & HID_MAIN_ITEM_VARIABLE) {
|
||||||
|
for (u = 0; u < field->report_count; u++) {
|
||||||
|
__hid_insert_field_entry(hid, report,
|
||||||
|
&entries[usages],
|
||||||
|
field, u);
|
||||||
|
usages++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
__hid_insert_field_entry(hid, report, &entries[usages],
|
||||||
|
field, 0);
|
||||||
|
usages++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hid_process_ordering(struct hid_device *hid)
|
||||||
|
{
|
||||||
|
struct hid_report *report;
|
||||||
|
struct hid_report_enum *report_enum = &hid->report_enum[HID_INPUT_REPORT];
|
||||||
|
|
||||||
|
list_for_each_entry(report, &report_enum->report_list, list)
|
||||||
|
hid_report_process_ordering(hid, report);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1746,7 +1960,6 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
|
|||||||
struct hid_report_enum *report_enum = hid->report_enum + type;
|
struct hid_report_enum *report_enum = hid->report_enum + type;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_driver *hdrv;
|
struct hid_driver *hdrv;
|
||||||
unsigned int a;
|
|
||||||
u32 rsize, csize = size;
|
u32 rsize, csize = size;
|
||||||
u8 *cdata = data;
|
u8 *cdata = data;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -1782,8 +1995,7 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
|
if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
|
||||||
for (a = 0; a < report->maxfield; a++)
|
hid_process_report(hid, report, cdata, interrupt);
|
||||||
hid_input_field(hid, report->field[a], cdata, interrupt);
|
|
||||||
hdrv = hid->driver;
|
hdrv = hid->driver;
|
||||||
if (hdrv && hdrv->report)
|
if (hdrv && hdrv->report)
|
||||||
hdrv->report(hid, report);
|
hdrv->report(hid, report);
|
||||||
@ -1970,6 +2182,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hid_process_ordering(hdev);
|
||||||
|
|
||||||
if ((hdev->claimed & HID_CLAIMED_INPUT) &&
|
if ((hdev->claimed & HID_CLAIMED_INPUT) &&
|
||||||
(connect_mask & HID_CONNECT_FF) && hdev->ff_init)
|
(connect_mask & HID_CONNECT_FF) && hdev->ff_init)
|
||||||
hdev->ff_init(hdev);
|
hdev->ff_init(hdev);
|
||||||
|
@ -48,6 +48,51 @@ static const struct {
|
|||||||
__s32 y;
|
__s32 y;
|
||||||
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
|
} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
|
||||||
|
|
||||||
|
struct usage_priority {
|
||||||
|
__u32 usage; /* the HID usage associated */
|
||||||
|
bool global; /* we assume all usages to be slotted,
|
||||||
|
* unless global
|
||||||
|
*/
|
||||||
|
unsigned int slot_overwrite; /* for globals: allows to set the usage
|
||||||
|
* before or after the slots
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hid-input will convert this list into priorities:
|
||||||
|
* the first element will have the highest priority
|
||||||
|
* (the length of the following array) and the last
|
||||||
|
* element the lowest (1).
|
||||||
|
*
|
||||||
|
* hid-input will then shift the priority by 8 bits to leave some space
|
||||||
|
* in case drivers want to interleave other fields.
|
||||||
|
*
|
||||||
|
* To accommodate slotted devices, the slot priority is
|
||||||
|
* defined in the next 8 bits (defined by 0xff - slot).
|
||||||
|
*
|
||||||
|
* If drivers want to add fields before those, hid-input will
|
||||||
|
* leave out the first 8 bits of the priority value.
|
||||||
|
*
|
||||||
|
* This still leaves us 65535 individual priority values.
|
||||||
|
*/
|
||||||
|
static const struct usage_priority hidinput_usages_priorities[] = {
|
||||||
|
{ /* Eraser (eraser touching) must always come before tipswitch */
|
||||||
|
.usage = HID_DG_ERASER,
|
||||||
|
},
|
||||||
|
{ /* Invert must always come before In Range */
|
||||||
|
.usage = HID_DG_INVERT,
|
||||||
|
},
|
||||||
|
{ /* Is the tip of the tool touching? */
|
||||||
|
.usage = HID_DG_TIPSWITCH,
|
||||||
|
},
|
||||||
|
{ /* Tip Pressure might emulate tip switch */
|
||||||
|
.usage = HID_DG_TIPPRESSURE,
|
||||||
|
},
|
||||||
|
{ /* In Range needs to come after the other tool states */
|
||||||
|
.usage = HID_DG_INRANGE,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
|
#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
|
||||||
#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
|
#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
|
||||||
#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
|
#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
|
||||||
@ -586,11 +631,13 @@ static bool hidinput_field_in_collection(struct hid_device *device, struct hid_f
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
|
static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
|
||||||
struct hid_usage *usage)
|
struct hid_usage *usage, unsigned int usage_index)
|
||||||
{
|
{
|
||||||
struct input_dev *input = hidinput->input;
|
struct input_dev *input = hidinput->input;
|
||||||
struct hid_device *device = input_get_drvdata(input);
|
struct hid_device *device = input_get_drvdata(input);
|
||||||
|
const struct usage_priority *usage_priority = NULL;
|
||||||
int max = 0, code;
|
int max = 0, code;
|
||||||
|
unsigned int i = 0;
|
||||||
unsigned long *bit = NULL;
|
unsigned long *bit = NULL;
|
||||||
|
|
||||||
field->hidinput = hidinput;
|
field->hidinput = hidinput;
|
||||||
@ -608,6 +655,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||||||
goto ignore;
|
goto ignore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* assign a priority based on the static list declared here */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(hidinput_usages_priorities); i++) {
|
||||||
|
if (usage->hid == hidinput_usages_priorities[i].usage) {
|
||||||
|
usage_priority = &hidinput_usages_priorities[i];
|
||||||
|
|
||||||
|
field->usages_priorities[usage_index] =
|
||||||
|
(ARRAY_SIZE(hidinput_usages_priorities) - i) << 8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For slotted devices, we need to also add the slot index
|
||||||
|
* in the priority.
|
||||||
|
*/
|
||||||
|
if (usage_priority && usage_priority->global)
|
||||||
|
field->usages_priorities[usage_index] |=
|
||||||
|
usage_priority->slot_overwrite;
|
||||||
|
else
|
||||||
|
field->usages_priorities[usage_index] |=
|
||||||
|
(0xff - field->slot_idx) << 16;
|
||||||
|
|
||||||
if (device->driver->input_mapping) {
|
if (device->driver->input_mapping) {
|
||||||
int ret = device->driver->input_mapping(device, hidinput, field,
|
int ret = device->driver->input_mapping(device, hidinput, field,
|
||||||
usage, &bit, &max);
|
usage, &bit, &max);
|
||||||
@ -828,10 +897,31 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x32: /* InRange */
|
case 0x32: /* InRange */
|
||||||
switch (field->physical & 0xff) {
|
switch (field->physical) {
|
||||||
case 0x21: map_key(BTN_TOOL_MOUSE); break;
|
case HID_DG_PUCK:
|
||||||
case 0x22: map_key(BTN_TOOL_FINGER); break;
|
map_key(BTN_TOOL_MOUSE);
|
||||||
default: map_key(BTN_TOOL_PEN); break;
|
break;
|
||||||
|
case HID_DG_FINGER:
|
||||||
|
map_key(BTN_TOOL_FINGER);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
* If the physical is not given,
|
||||||
|
* rely on the application.
|
||||||
|
*/
|
||||||
|
if (!field->physical) {
|
||||||
|
switch (field->application) {
|
||||||
|
case HID_DG_TOUCHSCREEN:
|
||||||
|
case HID_DG_TOUCHPAD:
|
||||||
|
map_key_clear(BTN_TOOL_FINGER);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
map_key_clear(BTN_TOOL_PEN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map_key(BTN_TOOL_PEN);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1315,9 +1405,38 @@ static void hidinput_handle_scroll(struct hid_usage *usage,
|
|||||||
input_event(input, EV_REL, usage->code, hi_res);
|
input_event(input, EV_REL, usage->code, hi_res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hid_report_release_tool(struct hid_report *report, struct input_dev *input,
|
||||||
|
unsigned int tool)
|
||||||
|
{
|
||||||
|
/* if the given tool is not currently reported, ignore */
|
||||||
|
if (!test_bit(tool, input->key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if the given tool was previously set, release it,
|
||||||
|
* release any TOUCH and send an EV_SYN
|
||||||
|
*/
|
||||||
|
input_event(input, EV_KEY, BTN_TOUCH, 0);
|
||||||
|
input_event(input, EV_KEY, tool, 0);
|
||||||
|
input_event(input, EV_SYN, SYN_REPORT, 0);
|
||||||
|
|
||||||
|
report->tool = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hid_report_set_tool(struct hid_report *report, struct input_dev *input,
|
||||||
|
unsigned int new_tool)
|
||||||
|
{
|
||||||
|
if (report->tool != new_tool)
|
||||||
|
hid_report_release_tool(report, input, report->tool);
|
||||||
|
|
||||||
|
input_event(input, EV_KEY, new_tool, 1);
|
||||||
|
report->tool = new_tool;
|
||||||
|
}
|
||||||
|
|
||||||
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
|
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||||
{
|
{
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
|
struct hid_report *report = field->report;
|
||||||
unsigned *quirks = &hid->quirks;
|
unsigned *quirks = &hid->quirks;
|
||||||
|
|
||||||
if (!usage->type)
|
if (!usage->type)
|
||||||
@ -1333,12 +1452,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||||||
|
|
||||||
input = field->hidinput->input;
|
input = field->hidinput->input;
|
||||||
|
|
||||||
if (usage->type == EV_ABS &&
|
|
||||||
(((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) ||
|
|
||||||
((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y))) {
|
|
||||||
value = field->logical_maximum - value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usage->hat_min < usage->hat_max || usage->hat_dir) {
|
if (usage->hat_min < usage->hat_max || usage->hat_dir) {
|
||||||
int hat_dir = usage->hat_dir;
|
int hat_dir = usage->hat_dir;
|
||||||
if (!hat_dir)
|
if (!hat_dir)
|
||||||
@ -1349,61 +1462,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usage->hid == HID_DG_INVERT) {
|
|
||||||
*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usage->hid == HID_DG_INRANGE) {
|
|
||||||
if (value) {
|
|
||||||
input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
input_event(input, usage->type, usage->code, 0);
|
|
||||||
input_event(input, usage->type, BTN_TOOL_RUBBER, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usage->hid == HID_DG_TIPPRESSURE && (*quirks & HID_QUIRK_NOTOUCH)) {
|
|
||||||
int a = field->logical_minimum;
|
|
||||||
int b = field->logical_maximum;
|
|
||||||
input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
|
|
||||||
dbg_hid("Maximum Effects - %d\n",value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (usage->hid == (HID_UP_PID | 0x7fUL)) {
|
|
||||||
dbg_hid("PID Pool Report\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES ||
|
|
||||||
usage->code == REL_HWHEEL_HI_RES)) {
|
|
||||||
hidinput_handle_scroll(usage, input, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
|
|
||||||
(usage->code == ABS_VOLUME)) {
|
|
||||||
int count = abs(value);
|
|
||||||
int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
input_event(input, EV_KEY, direction, 1);
|
|
||||||
input_sync(input);
|
|
||||||
input_event(input, EV_KEY, direction, 0);
|
|
||||||
input_sync(input);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ignore out-of-range values as per HID specification,
|
* Ignore out-of-range values as per HID specification,
|
||||||
* section 5.10 and 6.2.25, when NULL state bit is present.
|
* section 5.10 and 6.2.25, when NULL state bit is present.
|
||||||
@ -1416,7 +1474,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||||||
* don't specify logical min and max.
|
* don't specify logical min and max.
|
||||||
*/
|
*/
|
||||||
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
if ((field->flags & HID_MAIN_ITEM_VARIABLE) &&
|
||||||
(field->logical_minimum < field->logical_maximum)) {
|
field->logical_minimum < field->logical_maximum) {
|
||||||
if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
|
if (field->flags & HID_MAIN_ITEM_NULL_STATE &&
|
||||||
(value < field->logical_minimum ||
|
(value < field->logical_minimum ||
|
||||||
value > field->logical_maximum)) {
|
value > field->logical_maximum)) {
|
||||||
@ -1428,6 +1486,123 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||||||
field->logical_maximum);
|
field->logical_maximum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (usage->hid) {
|
||||||
|
case HID_DG_ERASER:
|
||||||
|
report->tool_active |= !!value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if eraser is set, we must enforce BTN_TOOL_RUBBER
|
||||||
|
* to accommodate for devices not following the spec.
|
||||||
|
*/
|
||||||
|
if (value)
|
||||||
|
hid_report_set_tool(report, input, BTN_TOOL_RUBBER);
|
||||||
|
else if (report->tool != BTN_TOOL_RUBBER)
|
||||||
|
/* value is off, tool is not rubber, ignore */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* let hid-input set BTN_TOUCH */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_DG_INVERT:
|
||||||
|
report->tool_active |= !!value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If invert is set, we store BTN_TOOL_RUBBER.
|
||||||
|
*/
|
||||||
|
if (value)
|
||||||
|
hid_report_set_tool(report, input, BTN_TOOL_RUBBER);
|
||||||
|
else if (!report->tool_active)
|
||||||
|
/* tool_active not set means Invert and Eraser are not set */
|
||||||
|
hid_report_release_tool(report, input, BTN_TOOL_RUBBER);
|
||||||
|
|
||||||
|
/* no further processing */
|
||||||
|
return;
|
||||||
|
|
||||||
|
case HID_DG_INRANGE:
|
||||||
|
report->tool_active |= !!value;
|
||||||
|
|
||||||
|
if (report->tool_active) {
|
||||||
|
/*
|
||||||
|
* if tool is not set but is marked as active,
|
||||||
|
* assume ours
|
||||||
|
*/
|
||||||
|
if (!report->tool)
|
||||||
|
hid_report_set_tool(report, input, usage->code);
|
||||||
|
} else {
|
||||||
|
hid_report_release_tool(report, input, usage->code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset tool_active for the next event */
|
||||||
|
report->tool_active = false;
|
||||||
|
|
||||||
|
/* no further processing */
|
||||||
|
return;
|
||||||
|
|
||||||
|
case HID_DG_TIPSWITCH:
|
||||||
|
report->tool_active |= !!value;
|
||||||
|
|
||||||
|
/* if tool is set to RUBBER we should ignore the current value */
|
||||||
|
if (report->tool == BTN_TOOL_RUBBER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_DG_TIPPRESSURE:
|
||||||
|
if (*quirks & HID_QUIRK_NOTOUCH) {
|
||||||
|
int a = field->logical_minimum;
|
||||||
|
int b = field->logical_maximum;
|
||||||
|
|
||||||
|
if (value > a + ((b - a) >> 3)) {
|
||||||
|
input_event(input, EV_KEY, BTN_TOUCH, 1);
|
||||||
|
report->tool_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_UP_PID | 0x83UL: /* Simultaneous Effects Max */
|
||||||
|
dbg_hid("Maximum Effects - %d\n",value);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case HID_UP_PID | 0x7fUL:
|
||||||
|
dbg_hid("PID Pool Report\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (usage->type) {
|
||||||
|
case EV_KEY:
|
||||||
|
if (usage->code == 0) /* Key 0 is "unassigned", not KEY_UNKNOWN */
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_REL:
|
||||||
|
if (usage->code == REL_WHEEL_HI_RES ||
|
||||||
|
usage->code == REL_HWHEEL_HI_RES) {
|
||||||
|
hidinput_handle_scroll(usage, input, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EV_ABS:
|
||||||
|
if ((field->flags & HID_MAIN_ITEM_RELATIVE) &&
|
||||||
|
usage->code == ABS_VOLUME) {
|
||||||
|
int count = abs(value);
|
||||||
|
int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
input_event(input, EV_KEY, direction, 1);
|
||||||
|
input_sync(input);
|
||||||
|
input_event(input, EV_KEY, direction, 0);
|
||||||
|
input_sync(input);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
} else if (((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) ||
|
||||||
|
((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y))
|
||||||
|
value = field->logical_maximum - value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ignore reports for absolute data if the data didn't change. This is
|
* Ignore reports for absolute data if the data didn't change. This is
|
||||||
* not only an optimization but also fixes 'dead' key reports. Some
|
* not only an optimization but also fixes 'dead' key reports. Some
|
||||||
@ -1930,12 +2105,63 @@ static struct hid_input *hidinput_match_application(struct hid_report *report)
|
|||||||
static inline void hidinput_configure_usages(struct hid_input *hidinput,
|
static inline void hidinput_configure_usages(struct hid_input *hidinput,
|
||||||
struct hid_report *report)
|
struct hid_report *report)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j, k;
|
||||||
|
int first_field_index = 0;
|
||||||
|
int slot_collection_index = -1;
|
||||||
|
int prev_collection_index = -1;
|
||||||
|
unsigned int slot_idx = 0;
|
||||||
|
struct hid_field *field;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First tag all the fields that are part of a slot,
|
||||||
|
* a slot needs to have one Contact ID in the collection
|
||||||
|
*/
|
||||||
|
for (i = 0; i < report->maxfield; i++) {
|
||||||
|
field = report->field[i];
|
||||||
|
|
||||||
|
/* ignore fields without usage */
|
||||||
|
if (field->maxusage < 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* janitoring when collection_index changes
|
||||||
|
*/
|
||||||
|
if (prev_collection_index != field->usage->collection_index) {
|
||||||
|
prev_collection_index = field->usage->collection_index;
|
||||||
|
first_field_index = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if we already found a Contact ID in the collection,
|
||||||
|
* tag and continue to the next.
|
||||||
|
*/
|
||||||
|
if (slot_collection_index == field->usage->collection_index) {
|
||||||
|
field->slot_idx = slot_idx;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if the current field has Contact ID */
|
||||||
|
for (j = 0; j < field->maxusage; j++) {
|
||||||
|
if (field->usage[j].hid == HID_DG_CONTACTID) {
|
||||||
|
slot_collection_index = field->usage->collection_index;
|
||||||
|
slot_idx++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mark all previous fields and this one in the
|
||||||
|
* current collection to be slotted.
|
||||||
|
*/
|
||||||
|
for (k = first_field_index; k <= i; k++)
|
||||||
|
report->field[k]->slot_idx = slot_idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < report->maxfield; i++)
|
for (i = 0; i < report->maxfield; i++)
|
||||||
for (j = 0; j < report->field[i]->maxusage; j++)
|
for (j = 0; j < report->field[i]->maxusage; j++)
|
||||||
hidinput_configure_usage(hidinput, report->field[i],
|
hidinput_configure_usage(hidinput, report->field[i],
|
||||||
report->field[i]->usage + j);
|
report->field[i]->usage + j,
|
||||||
|
j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -342,12 +342,12 @@ struct hid_item {
|
|||||||
* HID device quirks.
|
* HID device quirks.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Increase this if you need to configure more HID quirks at module load time
|
* Increase this if you need to configure more HID quirks at module load time
|
||||||
*/
|
*/
|
||||||
#define MAX_USBHID_BOOT_QUIRKS 4
|
#define MAX_USBHID_BOOT_QUIRKS 4
|
||||||
|
|
||||||
#define HID_QUIRK_INVERT BIT(0)
|
/* BIT(0) reserved for backward compatibility, was HID_QUIRK_INVERT */
|
||||||
#define HID_QUIRK_NOTOUCH BIT(1)
|
#define HID_QUIRK_NOTOUCH BIT(1)
|
||||||
#define HID_QUIRK_IGNORE BIT(2)
|
#define HID_QUIRK_IGNORE BIT(2)
|
||||||
#define HID_QUIRK_NOGET BIT(3)
|
#define HID_QUIRK_NOGET BIT(3)
|
||||||
@ -476,31 +476,50 @@ struct hid_field {
|
|||||||
unsigned report_count; /* number of this field in the report */
|
unsigned report_count; /* number of this field in the report */
|
||||||
unsigned report_type; /* (input,output,feature) */
|
unsigned report_type; /* (input,output,feature) */
|
||||||
__s32 *value; /* last known value(s) */
|
__s32 *value; /* last known value(s) */
|
||||||
|
__s32 *new_value; /* newly read value(s) */
|
||||||
|
__s32 *usages_priorities; /* priority of each usage when reading the report
|
||||||
|
* bits 8-16 are reserved for hid-input usage
|
||||||
|
*/
|
||||||
__s32 logical_minimum;
|
__s32 logical_minimum;
|
||||||
__s32 logical_maximum;
|
__s32 logical_maximum;
|
||||||
__s32 physical_minimum;
|
__s32 physical_minimum;
|
||||||
__s32 physical_maximum;
|
__s32 physical_maximum;
|
||||||
__s32 unit_exponent;
|
__s32 unit_exponent;
|
||||||
unsigned unit;
|
unsigned unit;
|
||||||
|
bool ignored; /* this field is ignored in this event */
|
||||||
struct hid_report *report; /* associated report */
|
struct hid_report *report; /* associated report */
|
||||||
unsigned index; /* index into report->field[] */
|
unsigned index; /* index into report->field[] */
|
||||||
/* hidinput data */
|
/* hidinput data */
|
||||||
struct hid_input *hidinput; /* associated input structure */
|
struct hid_input *hidinput; /* associated input structure */
|
||||||
__u16 dpad; /* dpad input code */
|
__u16 dpad; /* dpad input code */
|
||||||
|
unsigned int slot_idx; /* slot index in a report */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HID_MAX_FIELDS 256
|
#define HID_MAX_FIELDS 256
|
||||||
|
|
||||||
|
struct hid_field_entry {
|
||||||
|
struct list_head list;
|
||||||
|
struct hid_field *field;
|
||||||
|
unsigned int index;
|
||||||
|
__s32 priority;
|
||||||
|
};
|
||||||
|
|
||||||
struct hid_report {
|
struct hid_report {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct list_head hidinput_list;
|
struct list_head hidinput_list;
|
||||||
|
struct list_head field_entry_list; /* ordered list of input fields */
|
||||||
unsigned int id; /* id of this report */
|
unsigned int id; /* id of this report */
|
||||||
unsigned int type; /* report type */
|
unsigned int type; /* report type */
|
||||||
unsigned int application; /* application usage for this report */
|
unsigned int application; /* application usage for this report */
|
||||||
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */
|
struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */
|
||||||
|
struct hid_field_entry *field_entries; /* allocated memory of input field_entry */
|
||||||
unsigned maxfield; /* maximum valid field index */
|
unsigned maxfield; /* maximum valid field index */
|
||||||
unsigned size; /* size of the report (bits) */
|
unsigned size; /* size of the report (bits) */
|
||||||
struct hid_device *device; /* associated device */
|
struct hid_device *device; /* associated device */
|
||||||
|
|
||||||
|
/* tool related state */
|
||||||
|
bool tool_active; /* whether the current tool is active */
|
||||||
|
unsigned int tool; /* BTN_TOOL_* */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HID_MAX_IDS 256
|
#define HID_MAX_IDS 256
|
||||||
|
Loading…
x
Reference in New Issue
Block a user