Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Jiri Kosina:

 - rework of generic input handling which ultimately makes the
   processing of tablet events more generic and reliable (Benjamin
   Tissoires)

 - fixes for handling unnumbered reports fully correctly in i2c-hid
   (Angela Czubak, Dmitry Torokhov)

 - untangling of intermingled code for sending and handling output
   reports in i2c-hid (Dmitry Torokhov)

 - Apple magic keyboard support improvements for newer models (José
   Expósito)

 - Apple T2 Macs support improvements (Aun-Ali Zaidi, Paul Pawlowski)

 - driver for Razer Blackwidow keyboards (Jelle van der Waa)

 - driver for SiGma Micro keyboards (Desmond Lim)

 - integration of first part of DIGImend patches in order to ultimately
   vastly improve Linux support of tablets (Nikolai Kondrashov, José
   Expósito)

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (55 commits)
  HID: intel-ish-hid: Use dma_alloc_coherent for firmware update
  Input: docs: add more details on the use of BTN_TOOL
  HID: input: accommodate priorities for slotted devices
  HID: input: remove the need for HID_QUIRK_INVERT
  HID: input: enforce Invert usage to be processed before InRange
  HID: core: for input reports, process the usages by priority list
  HID: compute an ordered list of input fields to process
  HID: input: move up out-of-range processing of input values
  HID: input: rework spaghetti code with switch statements
  HID: input: tag touchscreens as such if the physical is not there
  HID: core: split data fetching from processing in hid_input_field()
  HID: core: de-duplicate some code in hid_input_field()
  HID: core: statically allocate read buffers
  HID: uclogic: Support multiple frame input devices
  HID: uclogic: Define report IDs before their descriptors
  HID: uclogic: Put version first in rdesc namespace
  HID: uclogic: Use "frame" instead of "buttonpad"
  HID: uclogic: Use different constants for frame report IDs
  HID: uclogic: Specify total report size to buttonpad macro
  HID: uclogic: Switch to matching subreport bytes
  ...
This commit is contained in:
Linus Torvalds
2022-03-25 12:22:16 -07:00
20 changed files with 1846 additions and 760 deletions

View File

@@ -48,6 +48,51 @@ static const struct {
__s32 y;
} 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_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))
@@ -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,
struct hid_usage *usage)
struct hid_usage *usage, unsigned int usage_index)
{
struct input_dev *input = hidinput->input;
struct hid_device *device = input_get_drvdata(input);
const struct usage_priority *usage_priority = NULL;
int max = 0, code;
unsigned int i = 0;
unsigned long *bit = NULL;
field->hidinput = hidinput;
@@ -608,6 +655,28 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
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) {
int ret = device->driver->input_mapping(device, hidinput, field,
usage, &bit, &max);
@@ -828,10 +897,31 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
case 0x32: /* InRange */
switch (field->physical & 0xff) {
case 0x21: map_key(BTN_TOOL_MOUSE); break;
case 0x22: map_key(BTN_TOOL_FINGER); break;
default: map_key(BTN_TOOL_PEN); break;
switch (field->physical) {
case HID_DG_PUCK:
map_key(BTN_TOOL_MOUSE);
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;
@@ -1318,9 +1408,38 @@ static void hidinput_handle_scroll(struct hid_usage *usage,
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)
{
struct input_dev *input;
struct hid_report *report = field->report;
unsigned *quirks = &hid->quirks;
if (!usage->type)
@@ -1336,12 +1455,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
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) {
int hat_dir = usage->hat_dir;
if (!hat_dir)
@@ -1352,61 +1465,6 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
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,
* section 5.10 and 6.2.25, when NULL state bit is present.
@@ -1419,7 +1477,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
* don't specify logical min and max.
*/
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 &&
(value < field->logical_minimum ||
value > field->logical_maximum)) {
@@ -1431,6 +1489,123 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
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
* not only an optimization but also fixes 'dead' key reports. Some
@@ -1933,12 +2108,63 @@ static struct hid_input *hidinput_match_application(struct hid_report *report)
static inline void hidinput_configure_usages(struct hid_input *hidinput,
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 (j = 0; j < report->field[i]->maxusage; j++)
hidinput_configure_usage(hidinput, report->field[i],
report->field[i]->usage + j);
report->field[i]->usage + j,
j);
}
/*