summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/input/goodix,gt7375p.yaml7
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/hid/.kunitconfig1
-rw-r--r--drivers/hid/Kconfig8
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.c2
-rw-r--r--drivers/hid/hid-asus.c37
-rw-r--r--drivers/hid/hid-bigbenff.c75
-rw-r--r--drivers/hid/hid-core.c12
-rw-r--r--drivers/hid/hid-debug.c1
-rw-r--r--drivers/hid/hid-evision.c53
-rw-r--r--drivers/hid/hid-hyperv.c2
-rw-r--r--drivers/hid/hid-ids.h4
-rw-r--r--drivers/hid/hid-input-test.c80
-rw-r--r--drivers/hid/hid-input.c44
-rw-r--r--drivers/hid/hid-letsketch.c2
-rw-r--r--drivers/hid/hid-logitech-dj.c4
-rw-r--r--drivers/hid/hid-logitech-hidpp.c152
-rw-r--r--drivers/hid/hid-sensor-custom.c242
-rw-r--r--drivers/hid/hid-sensor-hub.c6
-rw-r--r--drivers/hid/hid-steam.c2
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-acpi.c26
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-core.c18
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-of-goodix.c98
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid.c2
-rw-r--r--drivers/hid/surface-hid/surface_hid_core.c2
-rw-r--r--drivers/hid/uhid.c3
-rw-r--r--drivers/hid/usbhid/hid-core.c9
-rw-r--r--drivers/iio/light/hid-sensor-als.c27
-rw-r--r--drivers/iio/light/hid-sensor-prox.c37
-rw-r--r--drivers/platform/x86/asus-tf103c-dock.c4
-rw-r--r--drivers/staging/greybus/hid.c2
-rw-r--r--include/linux/hid-sensor-ids.h1
-rw-r--r--include/linux/hid.h28
-rw-r--r--net/bluetooth/hidp/core.c3
35 files changed, 665 insertions, 337 deletions
diff --git a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml
index 1c191bc5a178..ce18d7dadae2 100644
--- a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml
+++ b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml
@@ -36,6 +36,13 @@ properties:
vdd-supply:
description: The 3.3V supply to the touchscreen.
+ mainboard-vddio-supply:
+ description:
+ The supply on the main board needed to power up IO signals going
+ to the touchscreen. This supply need not go to the touchscreen
+ itself as long as it allows the main board to make signals compatible
+ with what the touchscreen is expecting for its IO rails.
+
required:
- compatible
- reg
diff --git a/MAINTAINERS b/MAINTAINERS
index fb1471cb5ed3..327679567645 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9217,6 +9217,13 @@ L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-logitech-*
+HID++ LOGITECH DRIVERS
+R: Filipe Laíns <lains@riseup.net>
+R: Bastien Nocera <hadess@hadess.net>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-logitech-hidpp.c
+
HID PLAYSTATION DRIVER
M: Roderick Colenbrander <roderick.colenbrander@sony.com>
L: linux-input@vger.kernel.org
diff --git a/drivers/hid/.kunitconfig b/drivers/hid/.kunitconfig
index 04daeff5c970..675a8209c7ae 100644
--- a/drivers/hid/.kunitconfig
+++ b/drivers/hid/.kunitconfig
@@ -1,5 +1,6 @@
CONFIG_KUNIT=y
CONFIG_USB=y
CONFIG_USB_HID=y
+CONFIG_HID_BATTERY_STRENGTH=y
CONFIG_HID_UCLOGIC=y
CONFIG_HID_KUNIT_TEST=y
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e2a5d30c8895..757a6fe8594a 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -329,6 +329,13 @@ config HID_ELO
Support for the ELO USB 4000/4500 touchscreens. Note that this is for
different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO.
+config HID_EVISION
+ tristate "EVision Keyboards Support"
+ depends on HID
+ help
+ Support for some EVision keyboards. Note that this is needed only when
+ applying customization using userspace programs.
+
config HID_EZKEY
tristate "Ezkey BTC 8193 keyboard"
default !EXPERT
@@ -1264,6 +1271,7 @@ config HID_MCP2221
config HID_KUNIT_TEST
tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS
depends on KUNIT=y
+ depends on HID_BATTERY_STRENGTH
depends on HID_UCLOGIC
default KUNIT_ALL_TESTS
help
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e8014c1a2f8b..bd01571ddfe7 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o
obj-$(CONFIG_HID_ELAN) += hid-elan.o
obj-$(CONFIG_HID_ELECOM) += hid-elecom.o
obj-$(CONFIG_HID_ELO) += hid-elo.o
+obj-$(CONFIG_HID_EVISION) += hid-evision.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_FT260) += hid-ft260.o
obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
index 1b18291fc5af..705b52337068 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c
@@ -112,7 +112,7 @@ void amdtp_hid_wakeup(struct hid_device *hid)
}
}
-static struct hid_ll_driver amdtp_hid_ll_driver = {
+static const struct hid_ll_driver amdtp_hid_ll_driver = {
.parse = amdtp_hid_parse,
.start = amdtp_hid_start,
.stop = amdtp_hid_stop,
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index f99752b998f3..d1094bb1aa42 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -98,6 +98,7 @@ struct asus_kbd_leds {
struct hid_device *hdev;
struct work_struct work;
unsigned int brightness;
+ spinlock_t lock;
bool removed;
};
@@ -490,21 +491,42 @@ static int rog_nkey_led_init(struct hid_device *hdev)
return ret;
}
+static void asus_schedule_work(struct asus_kbd_leds *led)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->lock, flags);
+ if (!led->removed)
+ schedule_work(&led->work);
+ spin_unlock_irqrestore(&led->lock, flags);
+}
+
static void asus_kbd_backlight_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
cdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->lock, flags);
led->brightness = brightness;
- schedule_work(&led->work);
+ spin_unlock_irqrestore(&led->lock, flags);
+
+ asus_schedule_work(led);
}
static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev)
{
struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds,
cdev);
+ enum led_brightness brightness;
+ unsigned long flags;
+
+ spin_lock_irqsave(&led->lock, flags);
+ brightness = led->brightness;
+ spin_unlock_irqrestore(&led->lock, flags);
- return led->brightness;
+ return brightness;
}
static void asus_kbd_backlight_work(struct work_struct *work)
@@ -512,11 +534,11 @@ static void asus_kbd_backlight_work(struct work_struct *work)
struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
int ret;
+ unsigned long flags;
- if (led->removed)
- return;
-
+ spin_lock_irqsave(&led->lock, flags);
buf[4] = led->brightness;
+ spin_unlock_irqrestore(&led->lock, flags);
ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf));
if (ret < 0)
@@ -584,6 +606,7 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set;
drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get;
INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
+ spin_lock_init(&drvdata->kbd_backlight->lock);
ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev);
if (ret < 0) {
@@ -1119,9 +1142,13 @@ err_stop_hw:
static void asus_remove(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
+ unsigned long flags;
if (drvdata->kbd_backlight) {
+ spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags);
drvdata->kbd_backlight->removed = true;
+ spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags);
+
cancel_work_sync(&drvdata->kbd_backlight->work);
}
diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c
index e8b16665860d..a02cb517b4c4 100644
--- a/drivers/hid/hid-bigbenff.c
+++ b/drivers/hid/hid-bigbenff.c
@@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = {
struct bigben_device {
struct hid_device *hid;
struct hid_report *report;
+ spinlock_t lock;
bool removed;
u8 led_state; /* LED1 = 1 .. LED4 = 8 */
u8 right_motor_on; /* right motor off/on 0/1 */
@@ -184,18 +185,39 @@ struct bigben_device {
struct work_struct worker;
};
+static inline void bigben_schedule_work(struct bigben_device *bigben)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&bigben->lock, flags);
+ if (!bigben->removed)
+ schedule_work(&bigben->worker);
+ spin_unlock_irqrestore(&bigben->lock, flags);
+}
static void bigben_worker(struct work_struct *work)
{
struct bigben_device *bigben = container_of(work,
struct bigben_device, worker);
struct hid_field *report_field = bigben->report->field[0];
-
- if (bigben->removed || !report_field)
+ bool do_work_led = false;
+ bool do_work_ff = false;
+ u8 *buf;
+ u32 len;
+ unsigned long flags;
+
+ buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
+ if (!buf)
return;
+ len = hid_report_len(bigben->report);
+
+ /* LED work */
+ spin_lock_irqsave(&bigben->lock, flags);
+
if (bigben->work_led) {
bigben->work_led = false;
+ do_work_led = true;
report_field->value[0] = 0x01; /* 1 = led message */
report_field->value[1] = 0x08; /* reserved value, always 8 */
report_field->value[2] = bigben->led_state;
@@ -204,11 +226,22 @@ static void bigben_worker(struct work_struct *work)
report_field->value[5] = 0x00; /* padding */
report_field->value[6] = 0x00; /* padding */
report_field->value[7] = 0x00; /* padding */
- hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
+ hid_output_report(bigben->report, buf);
+ }
+
+ spin_unlock_irqrestore(&bigben->lock, flags);
+
+ if (do_work_led) {
+ hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
+ bigben->report->type, HID_REQ_SET_REPORT);
}
+ /* FF work */
+ spin_lock_irqsave(&bigben->lock, flags);
+
if (bigben->work_ff) {
bigben->work_ff = false;
+ do_work_ff = true;
report_field->value[0] = 0x02; /* 2 = rumble effect message */
report_field->value[1] = 0x08; /* reserved value, always 8 */
report_field->value[2] = bigben->right_motor_on;
@@ -217,8 +250,17 @@ static void bigben_worker(struct work_struct *work)
report_field->value[5] = 0x00; /* padding */
report_field->value[6] = 0x00; /* padding */
report_field->value[7] = 0x00; /* padding */
- hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
+ hid_output_report(bigben->report, buf);
+ }
+
+ spin_unlock_irqrestore(&bigben->lock, flags);
+
+ if (do_work_ff) {
+ hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
+ bigben->report->type, HID_REQ_SET_REPORT);
}
+
+ kfree(buf);
}
static int hid_bigben_play_effect(struct input_dev *dev, void *data,
@@ -228,6 +270,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
struct bigben_device *bigben = hid_get_drvdata(hid);
u8 right_motor_on;
u8 left_motor_force;
+ unsigned long flags;
if (!bigben) {
hid_err(hid, "no device data\n");
@@ -242,10 +285,13 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
if (right_motor_on != bigben->right_motor_on ||
left_motor_force != bigben->left_motor_force) {
+ spin_lock_irqsave(&bigben->lock, flags);
bigben->right_motor_on = right_motor_on;
bigben->left_motor_force = left_motor_force;
bigben->work_ff = true;
- schedule_work(&bigben->worker);
+ spin_unlock_irqrestore(&bigben->lock, flags);
+
+ bigben_schedule_work(bigben);
}
return 0;
@@ -259,6 +305,7 @@ static void bigben_set_led(struct led_classdev *led,
struct bigben_device *bigben = hid_get_drvdata(hid);
int n;
bool work;
+ unsigned long flags;
if (!bigben) {
hid_err(hid, "no device data\n");
@@ -267,6 +314,7 @@ static void bigben_set_led(struct led_classdev *led,
for (n = 0; n < NUM_LEDS; n++) {
if (led == bigben->leds[n]) {
+ spin_lock_irqsave(&bigben->lock, flags);
if (value == LED_OFF) {
work = (bigben->led_state & BIT(n));
bigben->led_state &= ~BIT(n);
@@ -274,10 +322,11 @@ static void bigben_set_led(struct led_classdev *led,
work = !(bigben->led_state & BIT(n));
bigben->led_state |= BIT(n);
}
+ spin_unlock_irqrestore(&bigben->lock, flags);
if (work) {
bigben->work_led = true;
- schedule_work(&bigben->worker);
+ bigben_schedule_work(bigben);
}
return;
}
@@ -307,8 +356,12 @@ static enum led_brightness bigben_get_led(struct led_classdev *led)
static void bigben_remove(struct hid_device *hid)
{
struct bigben_device *bigben = hid_get_drvdata(hid);
+ unsigned long flags;
+ spin_lock_irqsave(&bigben->lock, flags);
bigben->removed = true;
+ spin_unlock_irqrestore(&bigben->lock, flags);
+
cancel_work_sync(&bigben->worker);
hid_hw_stop(hid);
}
@@ -318,7 +371,6 @@ static int bigben_probe(struct hid_device *hid,
{
struct bigben_device *bigben;
struct hid_input *hidinput;
- struct list_head *report_list;
struct led_classdev *led;
char *name;
size_t name_sz;
@@ -343,14 +395,12 @@ static int bigben_probe(struct hid_device *hid,
return error;
}
- report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- if (list_empty(report_list)) {
+ bigben->report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 8);
+ if (!bigben->report) {
hid_err(hid, "no output report found\n");
error = -ENODEV;
goto error_hw_stop;
}
- bigben->report = list_entry(report_list->next,
- struct hid_report, list);
if (list_empty(&hid->inputs)) {
hid_err(hid, "no inputs found\n");
@@ -362,6 +412,7 @@ static int bigben_probe(struct hid_device *hid,
set_bit(FF_RUMBLE, hidinput->input->ffbit);
INIT_WORK(&bigben->worker, bigben_worker);
+ spin_lock_init(&bigben->lock);
error = input_ff_create_memless(hidinput->input, NULL,
hid_bigben_play_effect);
@@ -402,7 +453,7 @@ static int bigben_probe(struct hid_device *hid,
bigben->left_motor_force = 0;
bigben->work_led = true;
bigben->work_ff = true;
- schedule_work(&bigben->worker);
+ bigben_schedule_work(bigben);
hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 5c72aef3d3dd..53e4d5831caf 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -41,11 +41,6 @@
#define DRIVER_DESC "HID core driver"
-int hid_debug = 0;
-module_param_named(debug, hid_debug, int, 0600);
-MODULE_PARM_DESC(debug, "toggle HID debugging messages");
-EXPORT_SYMBOL_GPL(hid_debug);
-
static int hid_ignore_special_drivers = 0;
module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600);
MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver");
@@ -804,7 +799,8 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type)
int i;
if (((parser->global.usage_page << 16) == HID_UP_SENSOR) &&
- type == HID_COLLECTION_PHYSICAL)
+ (type == HID_COLLECTION_PHYSICAL ||
+ type == HID_COLLECTION_APPLICATION))
hid->group = HID_GROUP_SENSOR_HUB;
if (hid->vendor == USB_VENDOR_ID_MICROSOFT &&
@@ -2912,10 +2908,6 @@ static int __init hid_init(void)
{
int ret;
- if (hid_debug)
- pr_warn("hid_debug is now used solely for parser and driver debugging.\n"
- "debugfs is now used for inspecting the device (report descriptor, reports)\n");
-
ret = bus_register(&hid_bus_type);
if (ret) {
pr_err("can't register hid bus\n");
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index e213bdde543a..e7ef1ea107c9 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -975,6 +975,7 @@ static const char *keys[KEY_MAX + 1] = {
[KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable",
[KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle",
[KEY_DICTATE] = "Dictate",
+ [KEY_MICMUTE] = "MicrophoneMute",
[KEY_BRIGHTNESS_MIN] = "BrightnessMin",
[KEY_BRIGHTNESS_MAX] = "BrightnessMax",
[KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",
diff --git a/drivers/hid/hid-evision.c b/drivers/hid/hid-evision.c
new file mode 100644
index 000000000000..ef6b4b435215
--- /dev/null
+++ b/drivers/hid/hid-evision.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for EVision devices
+ * For now, only ignore bogus consumer reports
+ * sent after the keyboard has been configured
+ *
+ * Copyright (c) 2022 Philippe Valembois
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static int evision_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
+ return 0;
+
+ /* Ignore key down event */
+ if ((usage->hid & HID_USAGE) >> 8 == 0x05)
+ return -1;
+ /* Ignore key up event */
+ if ((usage->hid & HID_USAGE) >> 8 == 0x06)
+ return -1;
+
+ switch (usage->hid & HID_USAGE) {
+ /* Ignore configuration saved event */
+ case 0x0401: return -1;
+ /* Ignore reset event */
+ case 0x0402: return -1;
+ }
+ return 0;
+}
+
+static const struct hid_device_id evision_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_EVISION, USB_DEVICE_ID_EVISION_ICL01) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, evision_devices);
+
+static struct hid_driver evision_driver = {
+ .name = "evision",
+ .id_table = evision_devices,
+ .input_mapping = evision_input_mapping,
+};
+module_hid_driver(evision_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index cf12f17e6533..819eb38eb5df 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -424,7 +424,7 @@ static int mousevsc_hid_raw_request(struct hid_device *hid,
return 0;
}
-static struct hid_ll_driver mousevsc_ll_driver = {
+static const struct hid_ll_driver mousevsc_ll_driver = {
.parse = mousevsc_hid_parse,
.open = mousevsc_hid_open,
.close = mousevsc_hid_close,
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9e36b4cd905e..a98c6145e975 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -448,6 +448,9 @@
#define USB_VENDOR_ID_EMS 0x2006
#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
+#define USB_VENDOR_ID_EVISION 0x320f
+#define USB_DEVICE_ID_EVISION_ICL01 0x5041
+
#define USB_VENDOR_ID_FLATFROG 0x25b5
#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
@@ -822,6 +825,7 @@
#define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e
#define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f
#define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262
+#define USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL 0xc26e
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287
diff --git a/drivers/hid/hid-input-test.c b/drivers/hid/hid-input-test.c
new file mode 100644
index 000000000000..77c2d45ac62a
--- /dev/null
+++ b/drivers/hid/hid-input-test.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HID to Linux Input mapping
+ *
+ * Copyright (c) 2022 José Expósito <jose.exposito89@gmail.com>
+ */
+
+#include <kunit/test.h>
+
+static void hid_test_input_set_battery_charge_status(struct kunit *test)
+{
+ struct hid_device *dev;
+ bool handled;
+
+ dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ handled = hidinput_set_battery_charge_status(dev, HID_DG_HEIGHT, 0);
+ KUNIT_EXPECT_FALSE(test, handled);
+ KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_UNKNOWN);
+
+ handled = hidinput_set_battery_charge_status(dev, HID_BAT_CHARGING, 0);
+ KUNIT_EXPECT_TRUE(test, handled);
+ KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_DISCHARGING);
+
+ handled = hidinput_set_battery_charge_status(dev, HID_BAT_CHARGING, 1);
+ KUNIT_EXPECT_TRUE(test, handled);
+ KUNIT_EXPECT_EQ(test, dev->battery_charge_status, POWER_SUPPLY_STATUS_CHARGING);
+}
+
+static void hid_test_input_get_battery_property(struct kunit *test)
+{
+ struct power_supply *psy;
+ struct hid_device *dev;
+ union power_supply_propval val;
+ int ret;
+
+ dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+ dev->battery_avoid_query = true;
+
+ psy = kunit_kzalloc(test, sizeof(*psy), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, psy);
+ psy->drv_data = dev;
+
+ dev->battery_status = HID_BATTERY_UNKNOWN;
+ dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_UNKNOWN);
+
+ dev->battery_status = HID_BATTERY_REPORTED;
+ dev->battery_charge_status = POWER_SUPPLY_STATUS_CHARGING;
+ ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_CHARGING);
+
+ dev->battery_status = HID_BATTERY_REPORTED;
+ dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+ ret = hidinput_get_battery_property(psy, POWER_SUPPLY_PROP_STATUS, &val);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+ KUNIT_EXPECT_EQ(test, val.intval, POWER_SUPPLY_STATUS_DISCHARGING);
+}
+
+static struct kunit_case hid_input_tests[] = {
+ KUNIT_CASE(hid_test_input_set_battery_charge_status),
+ KUNIT_CASE(hid_test_input_get_battery_property),
+ { }
+};
+
+static struct kunit_suite hid_input_test_suite = {
+ .name = "hid_input",
+ .test_cases = hid_input_tests,
+};
+
+kunit_test_suite(hid_input_test_suite);
+
+MODULE_DESCRIPTION("HID input KUnit tests");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("José Expósito <jose.exposito89@gmail.com>");
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 77c8c49852b5..743e518b9836 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -486,7 +486,7 @@ static int hidinput_get_battery_property(struct power_supply *psy,
if (dev->battery_status == HID_BATTERY_UNKNOWN)
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
else
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ val->intval = dev->battery_charge_status;
break;
case POWER_SUPPLY_PROP_SCOPE:
@@ -554,6 +554,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
@@ -620,6 +621,20 @@ static void hidinput_update_battery(struct hid_device *dev, int value)
power_supply_changed(dev->battery);
}
}
+
+static bool hidinput_set_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;
+}
#else /* !CONFIG_HID_BATTERY_STRENGTH */
static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
struct hid_field *field, bool is_percentage)
@@ -634,6 +649,12 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
static void hidinput_update_battery(struct hid_device *dev, int value)
{
}
+
+static bool hidinput_set_battery_charge_status(struct hid_device *dev,
+ unsigned int usage, int value)
+{
+ return false;
+}
#endif /* CONFIG_HID_BATTERY_STRENGTH */
static bool hidinput_field_in_collection(struct hid_device *device, struct hid_field *field,
@@ -793,6 +814,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
}
+ if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */
+ switch (usage->hid & 0xf) {
+ case 0x9: map_key_clear(KEY_MICMUTE); break;
+ default: goto ignore;
+ }
+ break;
+ }
+
if ((usage->hid & 0xf0) == 0xb0) { /* SC - Display */
switch (usage->hid & 0xf) {
case 0x05: map_key_clear(KEY_SWITCHVIDEOMODE); break;
@@ -1223,6 +1252,9 @@ 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;
@@ -1465,7 +1497,11 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
return;
if (usage->type == EV_PWR) {
- hidinput_update_battery(hid, value);
+ bool handled = hidinput_set_battery_charge_status(hid, usage->hid, value);
+
+ if (!handled)
+ hidinput_update_battery(hid, value);
+
return;
}
@@ -2321,3 +2357,7 @@ void hidinput_disconnect(struct hid_device *hid)
cancel_work_sync(&hid->led_work);
}
EXPORT_SYMBOL_GPL(hidinput_disconnect);
+
+#ifdef CONFIG_HID_KUNIT_TEST
+#include "hid-input-test.c"
+#endif
diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c
index 74d17cf518ba..97f047f18136 100644
--- a/drivers/hid/hid-letsketch.c
+++ b/drivers/hid/hid-letsketch.c
@@ -238,7 +238,7 @@ static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id *
char buf[256];
int i, ret;
- if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
+ if (!hid_is_usb(hdev))
return -ENODEV;
intf = to_usb_interface(hdev->dev.parent);
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index c358778e070b..62180414efcc 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -554,7 +554,7 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
#define LOGITECH_DJ_INTERFACE_NUMBER 0x02
-static struct hid_ll_driver logi_dj_ll_driver;
+static const struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
static void delayedwork_callback(struct work_struct *work);
@@ -1506,7 +1506,7 @@ static bool logi_dj_ll_may_wakeup(struct hid_device *hid)
return hid_hw_may_wakeup(djrcv_dev->hidpp);
}
-static struct hid_ll_driver logi_dj_ll_driver = {
+static const struct hid_ll_driver logi_dj_ll_driver = {
.parse = logi_dj_ll_parse,
.start = logi_dj_ll_start,
.stop = logi_dj_ll_stop,
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 9c1ee8e91e0c..25dcda76d6c7 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -30,11 +30,7 @@
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>");
-
-static bool disable_raw_mode;
-module_param(disable_raw_mode, bool, 0644);
-MODULE_PARM_DESC(disable_raw_mode,
- "Disable Raw mode reporting for touchpads and keep firmware gestures.");
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
static bool disable_tap_to_click;
module_param(disable_tap_to_click, bool, 0644);
@@ -71,12 +67,13 @@ MODULE_PARM_DESC(disable_tap_to_click,
/* bits 2..20 are reserved for classes */
/* #define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) disabled */
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
-#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
+#define HIDPP_QUIRK_DELAYED_INIT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_UNIFYING BIT(25)
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(26)
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27)
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
+#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(29)
/* These are just aliases for now */
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@@ -87,8 +84,6 @@ MODULE_PARM_DESC(disable_tap_to_click,
HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL | \
HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL)
-#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
-
#define HIDPP_CAPABILITY_HIDPP10_BATTERY BIT(0)
#define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1)
#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
@@ -225,6 +220,16 @@ struct hidpp_device {
#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
/* HID++ 2.0 error codes */
+#define HIDPP20_ERROR_NO_ERROR 0x00
+#define HIDPP20_ERROR_UNKNOWN 0x01
+#define HIDPP20_ERROR_INVALID_ARGS 0x02
+#define HIDPP20_ERROR_OUT_OF_RANGE 0x03
+#define HIDPP20_ERROR_HW_ERROR 0x04
+#define HIDPP20_ERROR_LOGITECH_INTERNAL 0x05
+#define HIDPP20_ERROR_INVALID_FEATURE_INDEX 0x06
+#define HIDPP20_ERROR_INVALID_FUNCTION_ID 0x07
+#define HIDPP20_ERROR_BUSY 0x08
+#define HIDPP20_ERROR_UNSUPPORTED 0x09
#define HIDPP20_ERROR 0xff
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
@@ -279,6 +284,7 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
struct hidpp_report *response)
{
int ret;
+ int max_retries = 3;
mutex_lock(&hidpp->send_mutex);
@@ -291,34 +297,39 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
*/
*response = *message;
- ret = __hidpp_send_report(hidpp->hid_dev, message);
+ for (; max_retries != 0; max_retries--) {
+ ret = __hidpp_send_report(hidpp->hid_dev, message);
- if (ret) {
- dbg_hid("__hidpp_send_report returned err: %d\n", ret);
- memset(response, 0, sizeof(struct hidpp_report));
- goto exit;
- }
+ if (ret) {
+ dbg_hid("__hidpp_send_report returned err: %d\n", ret);
+ memset(response, 0, sizeof(struct hidpp_report));
+ goto exit;
+ }
- if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
- 5*HZ)) {
- dbg_hid("%s:timeout waiting for response\n", __func__);
- memset(response, 0, sizeof(struct hidpp_report));
- ret = -ETIMEDOUT;
- }
+ if (!wait_event_timeout(hidpp->wait, hidpp->answer_available,
+ 5*HZ)) {
+ dbg_hid("%s:timeout waiting for response\n", __func__);
+ memset(response, 0, sizeof(struct hidpp_report));
+ ret = -ETIMEDOUT;
+ }
- if (response->report_id == REPORT_ID_HIDPP_SHORT &&
- response->rap.sub_id == HIDPP_ERROR) {
- ret = response->rap.params[1];
- dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
- goto exit;
- }
+ if (response->report_id == REPORT_ID_HIDPP_SHORT &&
+ response->rap.sub_id == HIDPP_ERROR) {
+ ret = response->rap.params[1];
+ dbg_hid("%s:got hidpp error %02X\n", __func__, ret);
+ goto exit;
+ }
- if ((response->report_id == REPORT_ID_HIDPP_LONG ||
- response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
- response->fap.feature_index == HIDPP20_ERROR) {
- ret = response->fap.params[1];
- dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
- goto exit;
+ if ((response->report_id == REPORT_ID_HIDPP_LONG ||
+ response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
+ response->fap.feature_index == HIDPP20_ERROR) {
+ ret = response->fap.params[1];
+ if (ret != HIDPP20_ERROR_BUSY) {
+ dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret);
+ goto exit;
+ }
+ dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
+ }
}
exit:
@@ -334,8 +345,13 @@ static int hidpp_send_fap_command_sync(struct hidpp_device *hidpp,
struct hidpp_report *message;
int ret;
- if (param_count > sizeof(message->fap.params))
+ if (param_count > sizeof(message->fap.params)) {
+ hid_dbg(hidpp->hid_dev,
+ "Invalid number of parameters passed to command (%d != %llu)\n",
+ param_count,
+ (unsigned long long) sizeof(message->fap.params));
return -EINVAL;
+ }
message = kzalloc(sizeof(struct hidpp_report), GFP_KERNEL);
if (!message)
@@ -3436,11 +3452,17 @@ static int hi_res_scroll_enable(struct hidpp_device *hidpp)
ret = hidpp10_enable_scrolling_acceleration(hidpp);
multiplier = 8;
}
- if (ret)
+ if (ret) {
+ hid_dbg(hidpp->hid_dev,
+ "Could not enable hi-res scrolling: %d\n", ret);
return ret;
+ }
- if (multiplier == 0)
+ if (multiplier == 0) {
+ hid_dbg(hidpp->hid_dev,
+ "Invalid multiplier 0 from device, setting it to 1\n");
multiplier = 1;
+ }
hidpp->vertical_wheel_counter.wheel_multiplier = multiplier;
hid_dbg(hidpp->hid_dev, "wheel multiplier = %d\n", multiplier);
@@ -3472,14 +3494,8 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp)
hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scrolling\n");
}
} else {
- struct hidpp_report response;
-
- ret = hidpp_send_rap_command_sync(hidpp,
- REPORT_ID_HIDPP_SHORT,
- HIDPP_GET_REGISTER,
- HIDPP_ENABLE_FAST_SCROLL,
- NULL, 0, &response);
- if (!ret) {
+ /* We cannot detect fast scrolling support on HID++ 1.0 devices */
+ if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) {
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_FAST_SCROLL;
hid_dbg(hidpp->hid_dev, "Detected HID++ 1.0 fast scroll\n");
}
@@ -4002,7 +4018,7 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
if (hidpp->capabilities & HIDPP_CAPABILITY_HI_RES_SCROLL)
hi_res_scroll_enable(hidpp);
- if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
+ if (!(hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) || hidpp->delayed_input)
/* if the input nodes are already created, we can stop now */
return;
@@ -4107,6 +4123,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
bool connected;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct hidpp_ff_private_data data;
+ bool will_restart = false;
/* report_fixup needs drvdata to be set before we call hid_parse */
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
@@ -4147,11 +4164,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp_application_equals(hdev, HID_GD_KEYBOARD))
hidpp->quirks |= HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS;
- if (disable_raw_mode) {
- hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP;
- hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT;
- }
-
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_allocate(hdev, id);
if (ret)
@@ -4162,6 +4174,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
+ if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT ||
+ hidpp->quirks & HIDPP_QUIRK_UNIFYING)
+ will_restart = true;
+
INIT_WORK(&hidpp->work, delayed_work_cb);
mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait);
@@ -4176,7 +4192,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
* Plain USB connections need to actually call start and open
* on the transport driver to allow incoming data.
*/
- ret = hid_hw_start(hdev, 0);
+ ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask);
if (ret) {
hid_err(hdev, "hw start failed\n");
goto hid_hw_start_fail;
@@ -4213,6 +4229,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp->wireless_feature_index = 0;
else if (ret)
goto hid_hw_init_fail;
+ ret = 0;
}
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
@@ -4227,19 +4244,21 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp_connect_event(hidpp);
- /* Reset the HID node state */
- hid_device_io_stop(hdev);
- hid_hw_close(hdev);
- hid_hw_stop(hdev);
+ if (will_restart) {
+ /* Reset the HID node state */
+ hid_device_io_stop(hdev);
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
- if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)
- connect_mask &= ~HID_CONNECT_HIDINPUT;
+ if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
+ connect_mask &= ~HID_CONNECT_HIDINPUT;
- /* Now export the actual inputs and hidraw nodes to the world */
- ret = hid_hw_start(hdev, connect_mask);
- if (ret) {
- hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
- goto hid_hw_start_fail;
+ /* Now export the actual inputs and hidraw nodes to the world */
+ ret = hid_hw_start(hdev, connect_mask);
+ if (ret) {
+ hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
+ goto hid_hw_start_fail;
+ }
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
@@ -4297,9 +4316,15 @@ static const struct hid_device_id hidpp_devices[] = {
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP },
+ { /* Mouse Logitech Anywhere MX */
+ LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Mouse logitech M560 */
LDJ_DEVICE(0x402d),
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
+ { /* Mouse Logitech M705 (firmware RQM17) */
+ LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
+ { /* Mouse Logitech Performance MX */
+ LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Keyboard logitech K400 */
LDJ_DEVICE(0x4024),
.driver_data = HIDPP_QUIRK_CLASS_K400 },
@@ -4348,6 +4373,9 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* Logitech G920 Wheel over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
+ { /* Logitech G923 Wheel (Xbox version) over USB */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL),
+ .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS },
{ /* Logitech G Pro Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) },
@@ -4367,6 +4395,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX Ergo trackball over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) },
+ { /* Signature M650 over Bluetooth */
+ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) },
{ /* MX Master 3 mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023) },
{}
diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c
index f444e63e9f36..3e3f89e01d81 100644
--- a/drivers/hid/hid-sensor-custom.c
+++ b/drivers/hid/hid-sensor-custom.c
@@ -5,6 +5,7 @@
*/
#include <linux/ctype.h>
+#include <linux/dmi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -750,114 +751,209 @@ static void hid_sensor_custom_dev_if_remove(struct hid_sensor_custom
}
-/* luid defined in FW (e.g. ISH). Maybe used to identify sensor. */
-static const char *const known_sensor_luid[] = { "020B000000000000" };
+/*
+ * Match a known custom sensor.
+ * tag and luid is mandatory.
+ */
+struct hid_sensor_custom_match {
+ const char *tag;
+ const char *luid;
+ const char *model;
+ const char *manufacturer;
+ bool check_dmi;
+ struct dmi_system_id dmi;
+};
-static int get_luid_table_index(unsigned char *usage_str)
-{
- int i;
+/*
+ * Custom sensor properties used for matching.
+ */
+struct hid_sensor_custom_properties {
+ u16 serial_num[HID_CUSTOM_MAX_FEATURE_BYTES];
+ u16 model[HID_CUSTOM_MAX_FEATURE_BYTES];
+ u16 manufacturer[HID_CUSTOM_MAX_FEATURE_BYTES];
+};
+
+static const struct hid_sensor_custom_match hid_sensor_custom_known_table[] = {
+ /*
+ * Intel Integrated Sensor Hub (ISH)
+ */
+ { /* Intel ISH hinge */
+ .tag = "INT",
+ .luid = "020B000000000000",
+ .manufacturer = "INTEL",
+ },
+ /*
+ * Lenovo Intelligent Sensing Solution (LISS)
+ */
+ { /* ambient light */
+ .tag = "LISS",
+ .luid = "0041010200000082",
+ .model = "STK3X3X Sensor",
+ .manufacturer = "Vendor 258",
+ .check_dmi = true,
+ .dmi.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ }
+ },
+ { /* human presence */
+ .tag = "LISS",
+ .luid = "0226000171AC0081",
+ .model = "VL53L1_HOD Sensor",
+ .manufacturer = "ST_MICRO",
+ .check_dmi = true,
+ .dmi.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ }
+ },
+ {}
+};
- for (i = 0; i < ARRAY_SIZE(known_sensor_luid); i++) {
- if (!strncmp(usage_str, known_sensor_luid[i],
- strlen(known_sensor_luid[i])))
- return i;
+static bool hid_sensor_custom_prop_match_str(const u16 *prop, const char *match,
+ size_t count)
+{
+ while (count-- && *prop && *match) {
+ if (*prop != (u16) *match)
+ return false;
+ prop++;
+ match++;
}
- return -ENODEV;
+ return (count == -1) || *prop == (u16)*match;
}
-static int get_known_custom_sensor_index(struct hid_sensor_hub_device *hsdev)
+static int hid_sensor_custom_get_prop(struct hid_sensor_hub_device *hsdev,
+ u32 prop_usage_id, size_t prop_size,
+ u16 *prop)
{
- struct hid_sensor_hub_attribute_info sensor_manufacturer = { 0 };
- struct hid_sensor_hub_attribute_info sensor_luid_info = { 0 };
- int report_size;
+ struct hid_sensor_hub_attribute_info prop_attr = { 0 };
int ret;
- static u16 w_buf[HID_CUSTOM_MAX_FEATURE_BYTES];
- static char buf[HID_CUSTOM_MAX_FEATURE_BYTES];
- int i;
- memset(w_buf, 0, sizeof(w_buf));
- memset(buf, 0, sizeof(buf));
+ memset(prop, 0, prop_size);
- /* get manufacturer info */
- ret = sensor_hub_input_get_attribute_info(hsdev,
- HID_FEATURE_REPORT, hsdev->usage,
- HID_USAGE_SENSOR_PROP_MANUFACTURER, &sensor_manufacturer);
+ ret = sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT,
+ hsdev->usage, prop_usage_id,
+ &prop_attr);
if (ret < 0)
return ret;
- report_size =
- sensor_hub_get_feature(hsdev, sensor_manufacturer.report_id,
- sensor_manufacturer.index, sizeof(w_buf),
- w_buf);
- if (report_size <= 0) {
- hid_err(hsdev->hdev,
- "Failed to get sensor manufacturer info %d\n",
- report_size);
- return -ENODEV;
+ ret = sensor_hub_get_feature(hsdev, prop_attr.report_id,
+ prop_attr.index, prop_size, prop);
+ if (ret < 0) {
+ hid_err(hsdev->hdev, "Failed to get sensor property %08x %d\n",
+ prop_usage_id, ret);
+ return ret;
}
- /* convert from wide char to char */
- for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
- buf[i] = (char)w_buf[i];
+ return 0;
+}
- /* ensure it's ISH sensor */
- if (strncmp(buf, "INTEL", strlen("INTEL")))
- return -ENODEV;
+static bool
+hid_sensor_custom_do_match(struct hid_sensor_hub_device *hsdev,
+ const struct hid_sensor_custom_match *match,
+ const struct hid_sensor_custom_properties *prop)
+{
+ struct dmi_system_id dmi[] = { match->dmi, { 0 } };
- memset(w_buf, 0, sizeof(w_buf));
- memset(buf, 0, sizeof(buf));
+ if (!hid_sensor_custom_prop_match_str(prop->serial_num, "LUID:", 5) ||
+ !hid_sensor_custom_prop_match_str(prop->serial_num + 5, match->luid,
+ HID_CUSTOM_MAX_FEATURE_BYTES - 5))
+ return false;
- /* get real usage id */
- ret = sensor_hub_input_get_attribute_info(hsdev,
- HID_FEATURE_REPORT, hsdev->usage,
- HID_USAGE_SENSOR_PROP_SERIAL_NUM, &sensor_luid_info);
+ if (match->model &&
+ !hid_sensor_custom_prop_match_str(prop->model, match->model,
+ HID_CUSTOM_MAX_FEATURE_BYTES))
+ return false;
+
+ if (match->manufacturer &&
+ !hid_sensor_custom_prop_match_str(prop->manufacturer, match->manufacturer,
+ HID_CUSTOM_MAX_FEATURE_BYTES))
+ return false;
+
+ if (match->check_dmi && !dmi_check_system(dmi))
+ return false;
+
+ return true;
+}
+
+static int
+hid_sensor_custom_properties_get(struct hid_sensor_hub_device *hsdev,
+ struct hid_sensor_custom_properties *prop)
+{
+ int ret;
+
+ ret = hid_sensor_custom_get_prop(hsdev,
+ HID_USAGE_SENSOR_PROP_SERIAL_NUM,
+ HID_CUSTOM_MAX_FEATURE_BYTES,
+ prop->serial_num);
if (ret < 0)
return ret;
- report_size = sensor_hub_get_feature(hsdev, sensor_luid_info.report_id,
- sensor_luid_info.index, sizeof(w_buf),
- w_buf);
- if (report_size <= 0) {
- hid_err(hsdev->hdev, "Failed to get real usage info %d\n",
- report_size);
- return -ENODEV;
- }
+ /*
+ * Ignore errors on the following model and manufacturer properties.
+ * Because these are optional, it is not an error if they are missing.
+ */
- /* convert from wide char to char */
- for (i = 0; i < ARRAY_SIZE(buf) - 1 && w_buf[i]; i++)
- buf[i] = (char)w_buf[i];
+ hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MODEL,
+ HID_CUSTOM_MAX_FEATURE_BYTES,
+ prop->model);
- if (strlen(buf) != strlen(known_sensor_luid[0]) + 5) {
- hid_err(hsdev->hdev,
- "%s luid length not match %zu != (%zu + 5)\n", __func__,
- strlen(buf), strlen(known_sensor_luid[0]));
- return -ENODEV;
- }
+ hid_sensor_custom_get_prop(hsdev, HID_USAGE_SENSOR_PROP_MANUFACTURER,
+ HID_CUSTOM_MAX_FEATURE_BYTES,
+ prop->manufacturer);
- /* get table index with luid (not matching 'LUID: ' in luid) */
- return get_luid_table_index(&buf[5]);
+ return 0;
+}
+
+static int
+hid_sensor_custom_get_known(struct hid_sensor_hub_device *hsdev,
+ const struct hid_sensor_custom_match **known)
+{
+ int ret;
+ const struct hid_sensor_custom_match *match =
+ hid_sensor_custom_known_table;
+ struct hid_sensor_custom_properties *prop;
+
+ prop = kmalloc(sizeof(struct hid_sensor_custom_properties), GFP_KERNEL);
+ if (!prop)
+ return -ENOMEM;
+
+ ret = hid_sensor_custom_properties_get(hsdev, prop);
+ if (ret < 0)
+ goto out;
+
+ while (match->tag) {
+ if (hid_sensor_custom_do_match(hsdev, match, prop)) {
+ *known = match;
+ ret = 0;
+ goto out;
+ }
+ match++;
+ }
+ ret = -ENODATA;
+out:
+ kfree(prop);
+ return ret;
}
static struct platform_device *
hid_sensor_register_platform_device(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
- int index)
+ const struct hid_sensor_custom_match *match)
{
- char real_usage[HID_SENSOR_USAGE_LENGTH] = { 0 };
+ char real_usage[HID_SENSOR_USAGE_LENGTH];
struct platform_device *custom_pdev;
const char *dev_name;
char *c;
- /* copy real usage id */
- memcpy(real_usage, known_sensor_luid[index], 4);
+ memcpy(real_usage, match->luid, 4);
/* usage id are all lowcase */
for (c = real_usage; *c != '\0'; c++)
*c = tolower(*c);
- /* HID-SENSOR-INT-REAL_USAGE_ID */
- dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-INT-%s", real_usage);
+ /* HID-SENSOR-TAG-REAL_USAGE_ID */
+ dev_name = kasprintf(GFP_KERNEL, "HID-SENSOR-%s-%s",
+ match->tag, real_usage);
if (!dev_name)
return ERR_PTR(-ENOMEM);
@@ -873,7 +969,7 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
struct hid_sensor_custom *sensor_inst;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
int ret;
- int index;
+ const struct hid_sensor_custom_match *match;
sensor_inst = devm_kzalloc(&pdev->dev, sizeof(*sensor_inst),
GFP_KERNEL);
@@ -888,10 +984,10 @@ static int hid_sensor_custom_probe(struct platform_device *pdev)
mutex_init(&sensor_inst->mutex);
platform_set_drvdata(pdev, sensor_inst);
- index = get_known_custom_sensor_index(hsdev);
- if (index >= 0 && index < ARRAY_SIZE(known_sensor_luid)) {
+ ret = hid_sensor_custom_get_known(hsdev, &match);
+ if (!ret) {
sensor_inst->custom_pdev =
- hid_sensor_register_platform_device(pdev, hsdev, index);
+ hid_sensor_register_platform_device(pdev, hsdev, match);
ret = PTR_ERR_OR_ZERO(sensor_inst->custom_pdev);
if (ret) {
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index 6abd3e2a9094..83237b86c8ff 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -397,7 +397,8 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
for (i = 0; i < report->maxfield; ++i) {
field = report->field[i];
if (field->maxusage) {
- if (field->physical == usage_id &&
+ if ((field->physical == usage_id ||
+ field->application == usage_id) &&
(field->logical == attr_usage_id ||
field->usage[0].hid ==
attr_usage_id) &&
@@ -506,7 +507,8 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
collection->usage);
callback = sensor_hub_get_callback(hdev,
- report->field[i]->physical,
+ report->field[i]->physical ? report->field[i]->physical :
+ report->field[i]->application,
report->field[i]->usage[0].collection_index,
&hsdev, &priv);
if (!callback) {
diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c
index 8ee43cb225fc..29ec8b34741a 100644
--- a/drivers/hid/hid-steam.c
+++ b/drivers/hid/hid-steam.c
@@ -674,7 +674,7 @@ static int steam_client_ll_raw_request(struct hid_device *hdev,
report_type, reqtype);
}
-static struct hid_ll_driver steam_client_ll_driver = {
+static const struct hid_ll_driver steam_client_ll_driver = {
.parse = steam_client_ll_parse,
.start = steam_client_ll_start,
.stop = steam_client_ll_stop,
diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c
index 375c77c3db74..3a7e2bcb5412 100644
--- a/drivers/hid/i2c-hid/i2c-hid-acpi.c
+++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c
@@ -39,8 +39,8 @@ static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
* The CHPN0001 ACPI device, which is used to describe the Chipone
* ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
*/
- {"CHPN0001", 0 },
- { },
+ { "CHPN0001" },
+ { }
};
/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
@@ -48,8 +48,9 @@ static guid_t i2c_hid_guid =
GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
-static int i2c_hid_acpi_get_descriptor(struct acpi_device *adev)
+static int i2c_hid_acpi_get_descriptor(struct i2c_hid_acpi *ihid_acpi)
{
+ struct acpi_device *adev = ihid_acpi->adev;
acpi_handle handle = acpi_device_handle(adev);
union acpi_object *obj;
u16 hid_descriptor_address;
@@ -81,38 +82,31 @@ static int i2c_hid_acpi_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct i2c_hid_acpi *ihid_acpi;
- struct acpi_device *adev;
u16 hid_descriptor_address;
int ret;
- adev = ACPI_COMPANION(dev);
- if (!adev) {
- dev_err(&client->dev, "Error could not get ACPI device\n");
- return -ENODEV;
- }
-
ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
if (!ihid_acpi)
return -ENOMEM;
- ihid_acpi->adev = adev;
+ ihid_acpi->adev = ACPI_COMPANION(dev);
ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
- ret = i2c_hid_acpi_get_descriptor(adev);
+ ret = i2c_hid_acpi_get_descriptor(ihid_acpi);
if (ret < 0)
return ret;
hid_descriptor_address = ret;
- acpi_device_fix_up_power(adev);
+ acpi_device_fix_up_power(ihid_acpi->adev);
return i2c_hid_core_probe(client, &ihid_acpi->ops,
hid_descriptor_address, 0);
}
static const struct acpi_device_id i2c_hid_acpi_match[] = {
- {"ACPI0C50", 0 },
- {"PNP0C50", 0 },
- { },
+ { "ACPI0C50" },
+ { "PNP0C50" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index b86b62f97108..0ab8f47a84e9 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -67,16 +67,7 @@
#define I2C_HID_PWR_ON 0x00
#define I2C_HID_PWR_SLEEP 0x01
-/* debug option */
-static bool debug;
-module_param(debug, bool, 0444);
-MODULE_PARM_DESC(debug, "print a lot of debug information");
-
-#define i2c_hid_dbg(ihid, fmt, arg...) \
-do { \
- if (debug) \
- dev_printk(KERN_DEBUG, &(ihid)->client->dev, fmt, ##arg); \
-} while (0)
+#define i2c_hid_dbg(ihid, ...) dev_dbg(&(ihid)->client->dev, __VA_ARGS__)
struct i2c_hid_desc {
__le16 wHIDDescLength;
@@ -842,7 +833,7 @@ static void i2c_hid_close(struct hid_device *hid)
clear_bit(I2C_HID_STARTED, &ihid->flags);
}
-struct hid_ll_driver i2c_hid_ll_driver = {
+static const struct hid_ll_driver i2c_hid_ll_driver = {
.parse = i2c_hid_parse,
.start = i2c_hid_start,
.stop = i2c_hid_stop,
@@ -851,7 +842,6 @@ struct hid_ll_driver i2c_hid_ll_driver = {
.output_report = i2c_hid_output_report,
.raw_request = i2c_hid_raw_request,
};
-EXPORT_SYMBOL_GPL(i2c_hid_ll_driver);
static int i2c_hid_init_irq(struct i2c_client *client)
{
@@ -859,7 +849,7 @@ static int i2c_hid_init_irq(struct i2c_client *client)
unsigned long irqflags = 0;
int ret;
- dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq);
+ i2c_hid_dbg(ihid, "Requesting IRQ: %d\n", client->irq);
if (!irq_get_trigger_type(client->irq))
irqflags = IRQF_TRIGGER_LOW;
@@ -1003,7 +993,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
/* Make sure there is something at this address */
ret = i2c_smbus_read_byte(client);
if (ret < 0) {
- dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
+ i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret);
ret = -ENXIO;
goto err_powered;
}
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
index 29c6cb174032..0060e3dcd775 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c
@@ -26,28 +26,33 @@ struct i2c_hid_of_goodix {
struct i2chid_ops ops;
struct regulator *vdd;
- struct notifier_block nb;
+ struct regulator *vddio;
struct gpio_desc *reset_gpio;
const struct goodix_i2c_hid_timing_data *timings;
};
-static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix,
- bool regulator_just_turned_on)
+static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
{
- if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms)
+ struct i2c_hid_of_goodix *ihid_goodix =
+ container_of(ops, struct i2c_hid_of_goodix, ops);
+ int ret;
+
+ ret = regulator_enable(ihid_goodix->vdd);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(ihid_goodix->vddio);
+ if (ret)
+ return ret;
+
+ if (ihid_goodix->timings->post_power_delay_ms)
msleep(ihid_goodix->timings->post_power_delay_ms);
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
if (ihid_goodix->timings->post_gpio_reset_delay_ms)
msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
-}
-
-static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
-{
- struct i2c_hid_of_goodix *ihid_goodix =
- container_of(ops, struct i2c_hid_of_goodix, ops);
- return regulator_enable(ihid_goodix->vdd);
+ return 0;
}
static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
@@ -55,42 +60,15 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
struct i2c_hid_of_goodix *ihid_goodix =
container_of(ops, struct i2c_hid_of_goodix, ops);
+ gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
+ regulator_disable(ihid_goodix->vddio);
regulator_disable(ihid_goodix->vdd);
}
-static int ihid_goodix_vdd_notify(struct notifier_block *nb,
- unsigned long event,
- void *ignored)
-{
- struct i2c_hid_of_goodix *ihid_goodix =
- container_of(nb, struct i2c_hid_of_goodix, nb);
- int ret = NOTIFY_OK;
-
- switch (event) {
- case REGULATOR_EVENT_PRE_DISABLE:
- gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
- break;
-
- case REGULATOR_EVENT_ENABLE:
- goodix_i2c_hid_deassert_reset(ihid_goodix, true);
- break;
-
- case REGULATOR_EVENT_ABORT_DISABLE:
- goodix_i2c_hid_deassert_reset(ihid_goodix, false);
- break;
-
- default:
- ret = NOTIFY_DONE;
- break;
- }
-
- return ret;
-}
-
static int i2c_hid_of_goodix_probe(struct i2c_client *client)
{
struct i2c_hid_of_goodix *ihid_goodix;
- int ret;
+
ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
GFP_KERNEL);
if (!ihid_goodix)
@@ -109,41 +87,11 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client)
if (IS_ERR(ihid_goodix->vdd))
return PTR_ERR(ihid_goodix->vdd);
- ihid_goodix->timings = device_get_match_data(&client->dev);
+ ihid_goodix->vddio = devm_regulator_get(&client->dev, "mainboard-vddio");
+ if (IS_ERR(ihid_goodix->vddio))
+ return PTR_ERR(ihid_goodix->vddio);
- /*
- * We need to control the "reset" line in lockstep with the regulator
- * actually turning on an off instead of just when we make the request.
- * This matters if the regulator is shared with another consumer.
- * - If the regulator is off then we must assert reset. The reset
- * line is active low and on some boards it could cause a current
- * leak if left high.
- * - If the regulator is on then we don't want reset asserted for very
- * long. Holding the controller in reset apparently draws extra
- * power.
- */
- ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify;
- ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb);
- if (ret)
- return dev_err_probe(&client->dev, ret,
- "regulator notifier request failed\n");
-
- /*
- * If someone else is holding the regulator on (or the regulator is
- * an always-on one) we might never be told to deassert reset. Do it
- * now... and temporarily bump the regulator reference count just to
- * make sure it is impossible for this to race with our own notifier!
- * We also assume that someone else might have _just barely_ turned
- * the regulator on so we'll do the full "post_power_delay" just in
- * case.
- */
- if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd)) {
- ret = regulator_enable(ihid_goodix->vdd);
- if (ret)
- return ret;
- goodix_i2c_hid_deassert_reset(ihid_goodix, true);
- regulator_disable(ihid_goodix->vdd);
- }
+ ihid_goodix->timings = device_get_match_data(&client->dev);
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0);
}
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c
index 14c271d7d8a9..00c6f0ebf356 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid.c
@@ -183,7 +183,7 @@ void ishtp_hid_wakeup(struct hid_device *hid)
wake_up_interruptible(&hid_data->hid_wait);
}
-static struct hid_ll_driver ishtp_hid_ll_driver = {
+static const struct hid_ll_driver ishtp_hid_ll_driver = {
.parse = ishtp_hid_parse,
.start = ishtp_hid_start,
.stop = ishtp_hid_stop,
diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c
index 87637f813de2..a3e9cceddfac 100644
--- a/drivers/hid/surface-hid/surface_hid_core.c
+++ b/drivers/hid/surface-hid/surface_hid_core.c
@@ -174,7 +174,7 @@ static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportn
return -EIO;
}
-static struct hid_ll_driver surface_hid_ll_driver = {
+static const struct hid_ll_driver surface_hid_ll_driver = {
.start = surface_hid_start,
.stop = surface_hid_stop,
.open = surface_hid_open,
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 2a918aeb0af1..f161c95a1ad2 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -387,7 +387,7 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf,
return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT);
}
-struct hid_ll_driver uhid_hid_driver = {
+static const struct hid_ll_driver uhid_hid_driver = {
.start = uhid_hid_start,
.stop = uhid_hid_stop,
.open = uhid_hid_open,
@@ -396,7 +396,6 @@ struct hid_ll_driver uhid_hid_driver = {
.raw_request = uhid_hid_raw_request,
.output_report = uhid_hid_output_report,
};
-EXPORT_SYMBOL_GPL(uhid_hid_driver);
#ifdef CONFIG_COMPAT
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index be4c731aaa65..257dd73e37bf 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1318,7 +1318,7 @@ static bool usbhid_may_wakeup(struct hid_device *hid)
return device_may_wakeup(&dev->dev);
}
-struct hid_ll_driver usb_hid_driver = {
+static const struct hid_ll_driver usb_hid_driver = {
.parse = usbhid_parse,
.start = usbhid_start,
.stop = usbhid_stop,
@@ -1332,7 +1332,12 @@ struct hid_ll_driver usb_hid_driver = {
.idle = usbhid_idle,
.may_wakeup = usbhid_may_wakeup,
};
-EXPORT_SYMBOL_GPL(usb_hid_driver);
+
+bool hid_is_usb(const struct hid_device *hdev)
+{
+ return hdev->ll_driver == &usb_hid_driver;
+}
+EXPORT_SYMBOL_GPL(hid_is_usb);
static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 5a1a625d8d16..eb1aedad7edc 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -86,6 +86,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct als_state *als_state = iio_priv(indio_dev);
+ struct hid_sensor_hub_device *hsdev = als_state->common_attributes.hsdev;
int report_id = -1;
u32 address;
int ret_type;
@@ -110,11 +111,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
hid_sensor_power_state(&als_state->common_attributes,
true);
*val = sensor_hub_input_attr_get_raw_value(
- als_state->common_attributes.hsdev,
- HID_USAGE_SENSOR_ALS, address,
- report_id,
- SENSOR_HUB_SYNC,
- min < 0);
+ hsdev, hsdev->usage, address, report_id,
+ SENSOR_HUB_SYNC, min < 0);
hid_sensor_power_state(&als_state->common_attributes,
false);
} else {
@@ -259,9 +257,7 @@ static int als_parse_report(struct platform_device *pdev,
dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
st->als_illum.report_id);
- st->scale_precision = hid_sensor_format_scale(
- HID_USAGE_SENSOR_ALS,
- &st->als_illum,
+ st->scale_precision = hid_sensor_format_scale(usage_id, &st->als_illum,
&st->scale_pre_decml, &st->scale_post_decml);
return ret;
@@ -285,7 +281,8 @@ static int hid_als_probe(struct platform_device *pdev)
als_state->common_attributes.hsdev = hsdev;
als_state->common_attributes.pdev = pdev;
- ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ hsdev->usage,
&als_state->common_attributes,
als_sensitivity_addresses,
ARRAY_SIZE(als_sensitivity_addresses));
@@ -303,7 +300,8 @@ static int hid_als_probe(struct platform_device *pdev)
ret = als_parse_report(pdev, hsdev,
(struct iio_chan_spec *)indio_dev->channels,
- HID_USAGE_SENSOR_ALS, als_state);
+ hsdev->usage,
+ als_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
@@ -333,8 +331,7 @@ static int hid_als_probe(struct platform_device *pdev)
als_state->callbacks.send_event = als_proc_event;
als_state->callbacks.capture_sample = als_capture_sample;
als_state->callbacks.pdev = pdev;
- ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
- &als_state->callbacks);
+ ret = sensor_hub_register_callback(hsdev, hsdev->usage, &als_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
goto error_iio_unreg;
@@ -356,7 +353,7 @@ static int hid_als_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct als_state *als_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
hid_sensor_remove_trigger(indio_dev, &als_state->common_attributes);
@@ -368,6 +365,10 @@ static const struct platform_device_id hid_als_ids[] = {
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200041",
},
+ {
+ /* Format: HID-SENSOR-custom_sensor_tag-usage_id_in_hex_lowercase */
+ .name = "HID-SENSOR-LISS-0041",
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_als_ids);
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index f10fa2abfe72..a47591e1bad9 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -61,6 +61,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct prox_state *prox_state = iio_priv(indio_dev);
+ struct hid_sensor_hub_device *hsdev;
int report_id = -1;
u32 address;
int ret_type;
@@ -75,6 +76,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
report_id = prox_state->prox_attr.report_id;
min = prox_state->prox_attr.logical_minimum;
address = HID_USAGE_SENSOR_HUMAN_PRESENCE;
+ hsdev = prox_state->common_attributes.hsdev;
break;
default:
report_id = -1;
@@ -84,11 +86,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
hid_sensor_power_state(&prox_state->common_attributes,
true);
*val = sensor_hub_input_attr_get_raw_value(
- prox_state->common_attributes.hsdev,
- HID_USAGE_SENSOR_PROX, address,
- report_id,
- SENSOR_HUB_SYNC,
- min < 0);
+ hsdev, hsdev->usage, address, report_id,
+ SENSOR_HUB_SYNC, min < 0);
hid_sensor_power_state(&prox_state->common_attributes,
false);
} else {
@@ -191,10 +190,16 @@ static int prox_capture_sample(struct hid_sensor_hub_device *hsdev,
switch (usage_id) {
case HID_USAGE_SENSOR_HUMAN_PRESENCE:
- prox_state->human_presence = *(u32 *)raw_data;
- ret = 0;
- break;
- default:
+ switch (raw_len) {
+ case 1:
+ prox_state->human_presence = *(u8 *)raw_data;
+ return 0;
+ case 4:
+ prox_state->human_presence = *(u32 *)raw_data;
+ return 0;
+ default:
+ break;
+ }
break;
}
@@ -244,7 +249,7 @@ static int hid_prox_probe(struct platform_device *pdev)
prox_state->common_attributes.hsdev = hsdev;
prox_state->common_attributes.pdev = pdev;
- ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX,
+ ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
&prox_state->common_attributes,
prox_sensitivity_addresses,
ARRAY_SIZE(prox_sensitivity_addresses));
@@ -262,7 +267,7 @@ static int hid_prox_probe(struct platform_device *pdev)
ret = prox_parse_report(pdev, hsdev,
(struct iio_chan_spec *)indio_dev->channels,
- HID_USAGE_SENSOR_PROX, prox_state);
+ hsdev->usage, prox_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
@@ -291,8 +296,8 @@ static int hid_prox_probe(struct platform_device *pdev)
prox_state->callbacks.send_event = prox_proc_event;
prox_state->callbacks.capture_sample = prox_capture_sample;
prox_state->callbacks.pdev = pdev;
- ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PROX,
- &prox_state->callbacks);
+ ret = sensor_hub_register_callback(hsdev, hsdev->usage,
+ &prox_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
goto error_iio_unreg;
@@ -314,7 +319,7 @@ static int hid_prox_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct prox_state *prox_state = iio_priv(indio_dev);
- sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PROX);
+ sensor_hub_remove_callback(hsdev, hsdev->usage);
iio_device_unregister(indio_dev);
hid_sensor_remove_trigger(indio_dev, &prox_state->common_attributes);
@@ -326,6 +331,10 @@ static const struct platform_device_id hid_prox_ids[] = {
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200011",
},
+ {
+ /* Format: HID-SENSOR-tag-usage_id_in_hex_lowercase */
+ .name = "HID-SENSOR-LISS-0226",
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, hid_prox_ids);
diff --git a/drivers/platform/x86/asus-tf103c-dock.c b/drivers/platform/x86/asus-tf103c-dock.c
index 62310e06282b..aeb1138464df 100644
--- a/drivers/platform/x86/asus-tf103c-dock.c
+++ b/drivers/platform/x86/asus-tf103c-dock.c
@@ -250,7 +250,7 @@ static int tf103c_dock_hid_raw_request(struct hid_device *hid, u8 reportnum,
return 0;
}
-static struct hid_ll_driver tf103c_dock_hid_ll_driver = {
+static const struct hid_ll_driver tf103c_dock_hid_ll_driver = {
.parse = tf103c_dock_hid_parse,
.start = tf103c_dock_hid_start,
.stop = tf103c_dock_hid_stop,
@@ -259,7 +259,7 @@ static struct hid_ll_driver tf103c_dock_hid_ll_driver = {
.raw_request = tf103c_dock_hid_raw_request,
};
-static int tf103c_dock_toprow_codes[13][2] = {
+static const int tf103c_dock_toprow_codes[13][2] = {
/* Normal, AltGr pressed */
{ KEY_POWER, KEY_F1 },
{ KEY_RFKILL, KEY_F2 },
diff --git a/drivers/staging/greybus/hid.c b/drivers/staging/greybus/hid.c
index adb91286803a..15335c38cb26 100644
--- a/drivers/staging/greybus/hid.c
+++ b/drivers/staging/greybus/hid.c
@@ -381,7 +381,7 @@ static int gb_hid_power(struct hid_device *hid, int lvl)
}
/* HID structure to pass callbacks */
-static struct hid_ll_driver gb_hid_ll_driver = {
+static const struct hid_ll_driver gb_hid_ll_driver = {
.parse = gb_hid_parse,
.start = gb_hid_start,
.stop = gb_hid_stop,
diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h
index ac631159403a..13b1e65fbdcc 100644
--- a/include/linux/hid-sensor-ids.h
+++ b/include/linux/hid-sensor-ids.h
@@ -132,6 +132,7 @@
#define HID_USAGE_SENSOR_PROP_FRIENDLY_NAME 0x200301
#define HID_USAGE_SENSOR_PROP_SERIAL_NUM 0x200307
#define HID_USAGE_SENSOR_PROP_MANUFACTURER 0x200305
+#define HID_USAGE_SENSOR_PROP_MODEL 0x200306
#define HID_USAGE_SENSOR_PROP_REPORT_INTERVAL 0x20030E
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F
#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 8677ae38599e..6074d2a828fd 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -312,6 +312,7 @@ struct hid_item {
#define HID_DG_LATENCYMODE 0x000d0060
#define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065
+#define HID_BAT_CHARGING 0x00850044
#define HID_VD_ASUS_CUSTOM_MEDIA_KEYS 0xff310076
@@ -595,7 +596,7 @@ struct hid_device { /* device report descriptor */
struct device dev; /* device */
struct hid_driver *driver;
- struct hid_ll_driver *ll_driver;
+ const struct hid_ll_driver *ll_driver;
struct mutex ll_open_lock;
unsigned int ll_open_count;
@@ -611,6 +612,7 @@ struct hid_device { /* device report descriptor */
__s32 battery_max;
__s32 battery_report_type;
__s32 battery_report_id;
+ __s32 battery_charge_status;
enum hid_battery_status battery_status;
bool battery_avoid_query;
ktime_t battery_ratelimit_time;
@@ -853,21 +855,7 @@ struct hid_ll_driver {
bool (*may_wakeup)(struct hid_device *hdev);
};
-extern struct hid_ll_driver i2c_hid_ll_driver;
-extern struct hid_ll_driver hidp_hid_driver;
-extern struct hid_ll_driver uhid_hid_driver;
-extern struct hid_ll_driver usb_hid_driver;
-
-static inline bool hid_is_using_ll_driver(struct hid_device *hdev,
- struct hid_ll_driver *driver)
-{
- return hdev->ll_driver == driver;
-}
-
-static inline bool hid_is_usb(struct hid_device *hdev)
-{
- return hid_is_using_ll_driver(hdev, &usb_hid_driver);
-}
+extern bool hid_is_usb(const struct hid_device *hdev);
#define PM_HINT_FULLON 1<<5
#define PM_HINT_NORMAL 1<<1
@@ -882,8 +870,6 @@ static inline bool hid_is_usb(struct hid_device *hdev)
/* HID core API */
-extern int hid_debug;
-
extern bool hid_ignore(struct hid_device *);
extern int hid_add_device(struct hid_device *);
extern void hid_destroy_device(struct hid_device *);
@@ -1191,11 +1177,7 @@ int hid_pidff_init(struct hid_device *hid);
#define hid_pidff_init NULL
#endif
-#define dbg_hid(fmt, ...) \
-do { \
- if (hid_debug) \
- printk(KERN_DEBUG "%s: " fmt, __FILE__, ##__VA_ARGS__); \
-} while (0)
+#define dbg_hid(fmt, ...) pr_debug("%s: " fmt, __FILE__, ##__VA_ARGS__)
#define hid_err(hid, fmt, ...) \
dev_err(&(hid)->dev, fmt, ##__VA_ARGS__)
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index cc20e706c639..bed1a7b9205c 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -739,7 +739,7 @@ static void hidp_stop(struct hid_device *hid)
hid->claimed = 0;
}
-struct hid_ll_driver hidp_hid_driver = {
+static const struct hid_ll_driver hidp_hid_driver = {
.parse = hidp_parse,
.start = hidp_start,
.stop = hidp_stop,
@@ -748,7 +748,6 @@ struct hid_ll_driver hidp_hid_driver = {
.raw_request = hidp_raw_request,
.output_report = hidp_output_report,
};
-EXPORT_SYMBOL_GPL(hidp_hid_driver);
/* This function sets up the hid device. It does not add it
to the HID system. That is done in hidp_add_connection(). */