summaryrefslogtreecommitdiff
path: root/drivers/hid/hid-input.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-input.c')
-rw-r--r--drivers/hid/hid-input.c583
1 files changed, 479 insertions, 104 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 4286a51f7f16..2633fcd8f910 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -48,10 +48,56 @@ 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))
#define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
+#define map_msc(c) hid_map_usage(hidinput, usage, &bit, &max, EV_MSC, (c))
#define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
&max, EV_ABS, (c))
@@ -160,6 +206,7 @@ static int hidinput_setkeycode(struct input_dev *dev,
if (usage) {
*old_keycode = usage->type == EV_KEY ?
usage->code : KEY_RESERVED;
+ usage->type = EV_KEY;
usage->code = ke->keycode;
clear_bit(*old_keycode, dev->keybit);
@@ -256,6 +303,19 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
}
break;
+ case ABS_PRESSURE:
+ case ABS_MT_PRESSURE:
+ if (field->unit == HID_UNIT_NEWTON) {
+ /* Convert to grams, 1 newton is 101.97 grams */
+ prev = physical_extents;
+ physical_extents *= 10197;
+ if (physical_extents < prev)
+ return 0;
+ unit_exponent -= 2;
+ } else if (field->unit != HID_UNIT_GRAM) {
+ return 0;
+ }
+ break;
default:
return 0;
}
@@ -293,6 +353,7 @@ static enum power_supply_property hidinput_battery_props[] = {
#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */
#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */
#define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */
+#define HID_BATTERY_QUIRK_AVOID_QUERY (1 << 3) /* do not query the battery */
static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -310,6 +371,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
+ USB_DEVICE_ID_APPLE_MAGICTRACKPAD),
+ HID_BATTERY_QUIRK_IGNORE },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
USB_DEVICE_ID_ELECOM_BM084),
HID_BATTERY_QUIRK_IGNORE },
@@ -324,10 +388,22 @@ static const struct hid_device_id hid_battery_quirks[] = {
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
- { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15),
- HID_BATTERY_QUIRK_IGNORE },
- { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN),
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L),
+ HID_BATTERY_QUIRK_AVOID_QUERY },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW),
+ HID_BATTERY_QUIRK_AVOID_QUERY },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW),
+ HID_BATTERY_QUIRK_AVOID_QUERY },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM),
+ HID_BATTERY_QUIRK_AVOID_QUERY },
+ /*
+ * Elan HID touchscreens seem to all report a non present battery,
+ * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices.
+ */
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
{}
};
@@ -419,10 +495,8 @@ static int hidinput_get_battery_property(struct power_supply *psy,
if (dev->battery_status == HID_BATTERY_UNKNOWN)
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
- else if (dev->battery_capacity == 100)
- val->intval = POWER_SUPPLY_STATUS_FULL;
else
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ val->intval = dev->battery_charge_status;
break;
case POWER_SUPPLY_PROP_SCOPE:
@@ -490,6 +564,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->battery_max = max;
dev->battery_report_type = report_type;
dev->battery_report_id = field->report->id;
+ dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
/*
* Stylus is normally not connected to the device and thus we
@@ -499,6 +574,9 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
dev->battery_avoid_query = report_type == HID_INPUT_REPORT &&
field->physical == HID_DG_STYLUS;
+ if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY)
+ dev->battery_avoid_query = true;
+
dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
if (IS_ERR(dev->battery)) {
error = PTR_ERR(dev->battery);
@@ -531,14 +609,37 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
dev->battery = NULL;
}
-static void hidinput_update_battery(struct hid_device *dev, int value)
+static bool hidinput_update_battery_charge_status(struct hid_device *dev,
+ unsigned int usage, int value)
+{
+ switch (usage) {
+ case HID_BAT_CHARGING:
+ dev->battery_charge_status = value ?
+ POWER_SUPPLY_STATUS_CHARGING :
+ POWER_SUPPLY_STATUS_DISCHARGING;
+ return true;
+ }
+
+ return false;
+}
+
+static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
+ int value)
{
int capacity;
if (!dev->battery)
return;
- if (value == 0 || value < dev->battery_min || value > dev->battery_max)
+ if (hidinput_update_battery_charge_status(dev, usage, value)) {
+ power_supply_changed(dev->battery);
+ return;
+ }
+
+ if ((usage & HID_USAGE_PAGE) == HID_UP_DIGITIZER && value == 0)
+ return;
+
+ if (value < dev->battery_min || value > dev->battery_max)
return;
capacity = hidinput_scale_battery_capacity(dev, value);
@@ -564,7 +665,8 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
{
}
-static void hidinput_update_battery(struct hid_device *dev, int value)
+static void hidinput_update_battery(struct hid_device *dev, unsigned int usage,
+ int value)
{
}
#endif /* CONFIG_HID_BATTERY_STRENGTH */
@@ -580,11 +682,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;
@@ -596,12 +700,35 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (field->report_count < 1)
goto ignore;
- /* only LED usages are supported in output fields */
+ /* only LED and HAPTIC usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT &&
- (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
+ (usage->hid & HID_USAGE_PAGE) != HID_UP_LED &&
+ (usage->hid & HID_USAGE_PAGE) != HID_UP_HAPTIC) {
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);
@@ -652,10 +779,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
code += KEY_MACRO1;
else
code += BTN_TRIGGER_HAPPY - 0x1e;
- } else {
- goto ignore;
+ break;
}
- break;
+ fallthrough;
default:
switch (field->physical) {
case HID_GD_MOUSE:
@@ -703,6 +829,36 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
}
+ if ((usage->hid & 0xf0) == 0x90) { /* SystemControl & D-pad */
+ switch (usage->hid) {
+ case HID_GD_UP: usage->hat_dir = 1; break;
+ case HID_GD_DOWN: usage->hat_dir = 5; break;
+ case HID_GD_RIGHT: usage->hat_dir = 3; break;
+ case HID_GD_LEFT: usage->hat_dir = 7; break;
+ case HID_GD_DO_NOT_DISTURB:
+ map_key_clear(KEY_DO_NOT_DISTURB); break;
+ default: goto unknown;
+ }
+
+ if (usage->hid <= HID_GD_LEFT) {
+ if (field->dpad) {
+ map_abs(field->dpad);
+ goto ignore;
+ }
+ map_abs(ABS_HAT0X);
+ }
+ break;
+ }
+
+ if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */
+ switch (usage->hid & 0xf) {
+ case 0x9: map_key_clear(KEY_MICMUTE); break;
+ case 0xa: map_key_clear(KEY_ACCESSIBILITY); break;
+ default: goto ignore;
+ }
+ break;
+ }
+
if ((usage->hid & 0xf0) == 0xb0) { /* SC - Display */
switch (usage->hid & 0xf) {
case 0x05: map_key_clear(KEY_SWITCHVIDEOMODE); break;
@@ -720,25 +876,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (field->application == HID_GD_SYSTEM_CONTROL)
goto ignore;
- if ((usage->hid & 0xf0) == 0x90) { /* D-pad */
- switch (usage->hid) {
- case HID_GD_UP: usage->hat_dir = 1; break;
- case HID_GD_DOWN: usage->hat_dir = 5; break;
- case HID_GD_RIGHT: usage->hat_dir = 3; break;
- case HID_GD_LEFT: usage->hat_dir = 7; break;
- default: goto unknown;
- }
- if (field->dpad) {
- map_abs(field->dpad);
- goto ignore;
- }
- map_abs(ABS_HAT0X);
- break;
- }
-
switch (usage->hid) {
/* These usage IDs map directly to the usage codes. */
- case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+ case HID_GD_X: case HID_GD_Y:
case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
if (field->flags & HID_MAIN_ITEM_RELATIVE)
map_rel(usage->hid & 0xf);
@@ -746,6 +886,22 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_abs_clear(usage->hid & 0xf);
break;
+ case HID_GD_Z:
+ /* HID_GD_Z is mapped to ABS_DISTANCE for stylus/pen */
+ if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+ map_rel(usage->hid & 0xf);
+ } else {
+ if (field->application == HID_DG_PEN ||
+ field->physical == HID_DG_PEN ||
+ field->logical == HID_DG_STYLUS ||
+ field->physical == HID_DG_STYLUS ||
+ field->application == HID_DG_DIGITIZER)
+ map_abs_clear(ABS_DISTANCE);
+ else
+ map_abs_clear(usage->hid & 0xf);
+ }
+ break;
+
case HID_GD_WHEEL:
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
set_bit(REL_WHEEL, input->relbit);
@@ -823,10 +979,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;
@@ -836,6 +1013,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
return;
case 0x3c: /* Invert */
+ device->quirks &= ~HID_QUIRK_NOINVERT;
map_key_clear(BTN_TOOL_RUBBER);
break;
@@ -861,9 +1039,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x45: /* ERASER */
/*
* This event is reported when eraser tip touches the surface.
- * Actual eraser (BTN_TOOL_RUBBER) is set by Invert usage when
- * tool gets in proximity.
+ * Actual eraser (BTN_TOOL_RUBBER) is set and released either
+ * by Invert if tool reports proximity or by Eraser directly.
*/
+ if (!test_bit(BTN_TOOL_RUBBER, input->keybit)) {
+ device->quirks |= HID_QUIRK_NOINVERT;
+ set_bit(BTN_TOOL_RUBBER, input->keybit);
+ }
map_key_clear(BTN_TOUCH);
break;
@@ -873,10 +1055,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
case 0x5b: /* TransducerSerialNumber */
- usage->type = EV_MSC;
- usage->code = MSC_SERIAL;
- bit = input->mscbit;
- max = MSC_MAX;
+ case 0x6e: /* TransducerSerialNumber2 */
+ map_msc(MSC_SERIAL);
break;
default: goto unknown;
@@ -943,6 +1123,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break;
case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break;
+ case 0x076: map_key_clear(KEY_CAMERA_ACCESS_ENABLE); break;
+ case 0x077: map_key_clear(KEY_CAMERA_ACCESS_DISABLE); break;
+ case 0x078: map_key_clear(KEY_CAMERA_ACCESS_TOGGLE); break;
+
case 0x079: map_key_clear(KEY_KBDILLUMUP); break;
case 0x07a: map_key_clear(KEY_KBDILLUMDOWN); break;
case 0x07c: map_key_clear(KEY_KBDILLUMTOGGLE); break;
@@ -989,6 +1173,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break;
case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break;
+ case 0x0d8: map_key_clear(KEY_DICTATE); break;
case 0x0d9: map_key_clear(KEY_EMOJI_PICKER); break;
case 0x0e0: map_abs_clear(ABS_VOLUME); break;
@@ -1080,6 +1265,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x29d: map_key_clear(KEY_KBD_LAYOUT_NEXT); break;
+ case 0x2a2: map_key_clear(KEY_ALL_APPLICATIONS); break;
+
case 0x2c7: map_key_clear(KEY_KBDINPUTASSIST_PREV); break;
case 0x2c8: map_key_clear(KEY_KBDINPUTASSIST_NEXT); break;
case 0x2c9: map_key_clear(KEY_KBDINPUTASSIST_PREVGROUP); break;
@@ -1108,8 +1295,21 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
hidinput_setup_battery(device, HID_INPUT_REPORT, field, true);
usage->type = EV_PWR;
return;
+ case HID_BAT_CHARGING:
+ usage->type = EV_PWR;
+ return;
}
goto unknown;
+ case HID_UP_CAMERA:
+ switch (usage->hid & HID_USAGE) {
+ case 0x020:
+ map_key_clear(KEY_CAMERA_FOCUS); break;
+ case 0x021:
+ map_key_clear(KEY_CAMERA); break;
+ default:
+ goto ignore;
+ }
+ break;
case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */
set_bit(EV_REP, input->evbit);
@@ -1312,16 +1512,45 @@ 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)
return;
if (usage->type == EV_PWR) {
- hidinput_update_battery(hid, value);
+ hidinput_update_battery(hid, usage->hid, value);
return;
}
@@ -1340,61 +1569,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.
@@ -1407,7 +1581,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)) {
@@ -1419,6 +1593,135 @@ 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;
+ else if (*quirks & HID_QUIRK_NOINVERT &&
+ !test_bit(BTN_TOUCH, input->key)) {
+ /*
+ * There is no invert to release the tool, let hid_input
+ * send BTN_TOUCH with scancode and release the tool after.
+ */
+ hid_report_release_tool(report, input, BTN_TOOL_RUBBER);
+ 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)
+ report->tool = usage->code;
+
+ /* drivers may have changed the value behind our back, resend it */
+ hid_report_set_tool(report, input, report->tool);
+ } 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
@@ -1462,7 +1765,8 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
}
EXPORT_SYMBOL_GPL(hidinput_report_event);
-int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
+static int hidinput_find_field(struct hid_device *hid, unsigned int type,
+ unsigned int code, struct hid_field **field)
{
struct hid_report *report;
int i, j;
@@ -1477,7 +1781,6 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
}
return -1;
}
-EXPORT_SYMBOL_GPL(hidinput_find_field);
struct hid_field *hidinput_get_led_field(struct hid_device *hid)
{
@@ -1740,6 +2043,16 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
case HID_GD_MOUSE:
suffix = "Mouse";
break;
+ case HID_DG_PEN:
+ /*
+ * yes, there is an issue here:
+ * DG_PEN -> "Stylus"
+ * DG_STYLUS -> "Pen"
+ * But changing this now means users with config snippets
+ * will have to change it and the test suite will not be happy.
+ */
+ suffix = "Stylus";
+ break;
case HID_DG_STYLUS:
suffix = "Pen";
break;
@@ -1911,12 +2224,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);
}
/*
@@ -2010,7 +2374,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
if (list_empty(&hid->inputs)) {
- hid_err(hid, "No inputs registered, leaving\n");
+ hid_dbg(hid, "No inputs registered, leaving\n");
goto out_unwind;
}
@@ -2051,3 +2415,14 @@ void hidinput_disconnect(struct hid_device *hid)
cancel_work_sync(&hid->led_work);
}
EXPORT_SYMBOL_GPL(hidinput_disconnect);
+
+void hidinput_reset_resume(struct hid_device *hid)
+{
+ /* renegotiate host-device shared state after reset */
+ hidinput_change_resolution_multipliers(hid);
+}
+EXPORT_SYMBOL_GPL(hidinput_reset_resume);
+
+#ifdef CONFIG_HID_KUNIT_TEST
+#include "hid-input-test.c"
+#endif