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:
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user