for-linus-2023110101

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIVAwUAZUGPxqZi849r7WBJAQIMkhAAxZqCeGjJ2QsO/C41DodzxbVVyvfkPWwC
 zkNL0KORtTmaGI4UOhakh0447lyUwNN1GT17GXIC7BjD8BCm0vm/474FOI9hXsEb
 wHP4RsVUTb2zK5r6zeWvzurLmBmsryCKb95Co4o7yPXKY4DHSjyYgvAJIOEOl9ov
 rmk3SZ8SweFp6SlkxANjZ3jU1CF13EmUOL9yPviXfXSa0qYAeIVF3kMMJoNMsi61
 wl32r4NZJg6JpkUxyDUlWAayB0R9L8ean+t+UbH1sOzWhISOizn0Ceq6Nh+9X0AO
 DIulQv9z8GDgn9JkkwFExd5oU9Tknd6eSCeiAN4RlB4iqOhET13pgeUxcwayrQ+4
 WwBncKfJf/1gFu7nQCloQ0qEm+Ehq6CPVmeNa+xEBNPlD6nsRXldNnVqTG4I0rtF
 thB2Ep2w+7luS49wqN93R6sgmWBunSwGpRN7eo5hpLoJ1ybhFT5pn8PX7Brflkyn
 Ru934APRChMf1WKdTgZu9sGkLpgRBMDe1836kkTiLAO6aJu45ln5H7L5nBzQ4aUB
 cjj5kv7j2aE4TPWXOvvd0KwLPU66TJ6j/n6SrEqWUQicsJNp09sUpMAzfjhS7gZG
 ipJBEoiC6dBHRikaEy3+lLBrfEnq0ptfLUDBiOClzilsrgTGwCqgcNaDpKuqCCfN
 TEN9LoDIIs8=
 =Xcc6
 -----END PGP SIGNATURE-----

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

Pull HID updates from Jiri Kosina:

 - fixes for crashes detected by CONFIG_KUNIT_ALL_TESTS in hid-uclogic
   driver (Jinjie Ruan)

 - HID selftests fixes and improvements (Benjamin Tissoires)

 - probe error handling path fixes in hid-nvidia-shield driver
   (Christophe JAILLET)

 - cleanup of LED handling in hid-nintendo (Martino Fontana)

 - big cleanup of logitech-hidpp probe code (Hans de Goede)

 - Suspend/Resume fix for USB Thinkpad Compact Keyboard (Jamie Lentin)

 - firmware detection improvement for Lenovo cptkbd (Mikhail
   Khvainitski)

 - IRQ shutdown and workqueue initialization fixes for hid-cp2112 driver
   (Danny Kaehn)

 - #ifdef CONFIG_PM removal from HID code (Thomas Weißschuh)

 - other assorted device-ID additions and quirks

* tag 'for-linus-2023110101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (31 commits)
  HID: Add quirk for Dell Pro Wireless Keyboard and Mouse KM5221W
  HID: logitech-hidpp: Stop IO before calling hid_connect()
  HID: logitech-hidpp: Drop HIDPP_QUIRK_UNIFYING
  HID: logitech-hidpp: Drop delayed_work_cb()
  HID: logitech-hidpp: Fix connect event race
  HID: logitech-hidpp: Remove unused connected param from *_connect()
  HID: logitech-hidpp: Remove connected check for non-unifying devices
  HID: logitech-hidpp: Add hidpp_non_unifying_init() helper
  HID: logitech-hidpp: Move hidpp_overwrite_name() to before connect check
  HID: logitech-hidpp: Move g920_get_config() to just before hidpp_ff_init()
  HID: logitech-hidpp: Remove wtp_get_config() call from probe()
  HID: logitech-hidpp: Move get_wireless_feature_index() check to hidpp_connect_event()
  HID: logitech-hidpp: Revert "Don't restart communication if not necessary"
  HID: logitech-hidpp: Don't restart IO, instead defer hid_connect() only
  HID: rmi: remove #ifdef CONFIG_PM
  HID: multitouch: remove #ifdef CONFIG_PM
  HID: usbhid: remove #ifdef CONFIG_PM
  HID: core: remove #ifdef CONFIG_PM from hid_driver
  hid: lenovo: Resend all settings on reset_resume for compact keyboards
  HID: uclogic: Fix a work->entry not empty bug in __queue_work()
  ...
This commit is contained in:
Linus Torvalds 2023-11-02 14:29:10 -10:00
commit 4ea4ed22b5
16 changed files with 355 additions and 257 deletions

View File

@ -1151,8 +1151,6 @@ static unsigned int cp2112_gpio_irq_startup(struct irq_data *d)
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct cp2112_device *dev = gpiochip_get_data(gc);
INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
if (!dev->gpio_poll) {
dev->gpio_poll = true;
schedule_delayed_work(&dev->gpio_poll_worker, 0);
@ -1168,7 +1166,11 @@ static void cp2112_gpio_irq_shutdown(struct irq_data *d)
struct cp2112_device *dev = gpiochip_get_data(gc);
cp2112_gpio_irq_mask(d);
cancel_delayed_work_sync(&dev->gpio_poll_worker);
if (!dev->irq_mask) {
dev->gpio_poll = false;
cancel_delayed_work_sync(&dev->gpio_poll_worker);
}
}
static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type)
@ -1307,6 +1309,8 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
girq->handler = handle_simple_irq;
girq->threaded = true;
INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
ret = gpiochip_add_data(&dev->gc, dev);
if (ret < 0) {
hid_err(hdev, "error registering gpio chip\n");

View File

@ -366,6 +366,7 @@
#define USB_VENDOR_ID_DELL 0x413c
#define USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE 0x301a
#define USB_DEVICE_ID_DELL_PRO_WIRELESS_KM5221W 0x4503
#define USB_VENDOR_ID_DELORME 0x1163
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100

View File

@ -51,7 +51,12 @@ struct lenovo_drvdata {
int select_right;
int sensitivity;
int press_speed;
u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
/* 0: Up
* 1: Down (undecided)
* 2: Scrolling
* 3: Patched firmware, disable workaround
*/
u8 middlebutton_state;
bool fn_lock;
};
@ -521,6 +526,19 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev)
int ret;
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
/*
* Tell the keyboard a driver understands it, and turn F7, F9, F11 into
* regular keys
*/
ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
if (ret)
hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
/* Switch middle button to native mode */
ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
if (ret)
hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
if (ret)
hid_err(hdev, "Fn-lock setting failed: %d\n", ret);
@ -668,31 +686,48 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
{
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
/* "wheel" scroll events */
if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
usage->code == REL_HWHEEL)) {
/* Scroll events disable middle-click event */
cptkbd_data->middlebutton_state = 2;
return 0;
}
/* Middle click events */
if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
if (value == 1) {
cptkbd_data->middlebutton_state = 1;
} else if (value == 0) {
if (cptkbd_data->middlebutton_state == 1) {
/* No scrolling inbetween, send middle-click */
input_event(field->hidinput->input,
EV_KEY, BTN_MIDDLE, 1);
input_sync(field->hidinput->input);
input_event(field->hidinput->input,
EV_KEY, BTN_MIDDLE, 0);
input_sync(field->hidinput->input);
}
cptkbd_data->middlebutton_state = 0;
if (cptkbd_data->middlebutton_state != 3) {
/* REL_X and REL_Y events during middle button pressed
* are only possible on patched, bug-free firmware
* so set middlebutton_state to 3
* to never apply workaround anymore
*/
if (cptkbd_data->middlebutton_state == 1 &&
usage->type == EV_REL &&
(usage->code == REL_X || usage->code == REL_Y)) {
cptkbd_data->middlebutton_state = 3;
/* send middle button press which was hold before */
input_event(field->hidinput->input,
EV_KEY, BTN_MIDDLE, 1);
input_sync(field->hidinput->input);
}
/* "wheel" scroll events */
if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
usage->code == REL_HWHEEL)) {
/* Scroll events disable middle-click event */
cptkbd_data->middlebutton_state = 2;
return 0;
}
/* Middle click events */
if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
if (value == 1) {
cptkbd_data->middlebutton_state = 1;
} else if (value == 0) {
if (cptkbd_data->middlebutton_state == 1) {
/* No scrolling inbetween, send middle-click */
input_event(field->hidinput->input,
EV_KEY, BTN_MIDDLE, 1);
input_sync(field->hidinput->input);
input_event(field->hidinput->input,
EV_KEY, BTN_MIDDLE, 0);
input_sync(field->hidinput->input);
}
cptkbd_data->middlebutton_state = 0;
}
return 1;
}
return 1;
}
if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
@ -1126,22 +1161,6 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
}
hid_set_drvdata(hdev, cptkbd_data);
/*
* Tell the keyboard a driver understands it, and turn F7, F9, F11 into
* regular keys (Compact only)
*/
if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD ||
hdev->product == USB_DEVICE_ID_LENOVO_CBTKBD) {
ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
if (ret)
hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
}
/* Switch middle button to native mode */
ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
if (ret)
hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
/* Set keyboard settings to known state */
cptkbd_data->middlebutton_state = 0;
cptkbd_data->fn_lock = true;
@ -1264,6 +1283,24 @@ err:
return ret;
}
#ifdef CONFIG_PM
static int lenovo_reset_resume(struct hid_device *hdev)
{
switch (hdev->product) {
case USB_DEVICE_ID_LENOVO_CUSBKBD:
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
if (hdev->type == HID_TYPE_USBMOUSE)
lenovo_features_set_cptkbd(hdev);
break;
default:
break;
}
return 0;
}
#endif
static void lenovo_remove_tpkbd(struct hid_device *hdev)
{
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
@ -1380,6 +1417,9 @@ static struct hid_driver lenovo_driver = {
.raw_event = lenovo_raw_event,
.event = lenovo_event,
.report_fixup = lenovo_report_fixup,
#ifdef CONFIG_PM
.reset_resume = lenovo_reset_resume,
#endif
};
module_hid_driver(lenovo_driver);

View File

@ -69,12 +69,11 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#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)
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(30)
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(25)
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(26)
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(27)
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(28)
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(29)
/* These are just aliases for now */
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@ -194,7 +193,6 @@ struct hidpp_device {
struct work_struct work;
struct kfifo delayed_work_fifo;
atomic_t connected;
struct input_dev *delayed_input;
unsigned long quirks;
@ -235,8 +233,6 @@ struct hidpp_device {
#define HIDPP20_ERROR_UNSUPPORTED 0x09
#define HIDPP20_ERROR 0xff
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
static int __hidpp_send_report(struct hid_device *hdev,
struct hidpp_report *hidpp_report)
{
@ -450,13 +446,6 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
return ret;
}
static void delayed_work_cb(struct work_struct *work)
{
struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
work);
hidpp_connect_event(hidpp);
}
static inline bool hidpp_match_answer(struct hidpp_report *question,
struct hidpp_report *answer)
{
@ -1835,15 +1824,14 @@ static int hidpp_battery_get_property(struct power_supply *psy,
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b
static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
static int hidpp_get_wireless_feature_index(struct hidpp_device *hidpp, u8 *feature_index)
{
u8 feature_type;
int ret;
ret = hidpp_root_get_feature(hidpp,
HIDPP_PAGE_WIRELESS_DEVICE_STATUS,
&hidpp->wireless_feature_index,
&feature_type);
feature_index, &feature_type);
return ret;
}
@ -3142,7 +3130,7 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
};
static int wtp_connect(struct hid_device *hdev, bool connected)
static int wtp_connect(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct wtp_data *wd = hidpp->private_data;
@ -3204,7 +3192,7 @@ static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
#define M560_SUB_ID 0x0a
#define M560_BUTTON_MODE_REGISTER 0x35
static int m560_send_config_command(struct hid_device *hdev, bool connected)
static int m560_send_config_command(struct hid_device *hdev)
{
struct hidpp_report response;
struct hidpp_device *hidpp_dev;
@ -3399,7 +3387,7 @@ static int k400_allocate(struct hid_device *hdev)
return 0;
};
static int k400_connect(struct hid_device *hdev, bool connected)
static int k400_connect(struct hid_device *hdev)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
@ -3894,8 +3882,6 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
}
if (unlikely(hidpp_report_is_connect_event(hidpp, report))) {
atomic_set(&hidpp->connected,
!(report->rap.params[0] & (1 << 6)));
if (schedule_work(&hidpp->work) == 0)
dbg_hid("%s: connect event already queued\n", __func__);
return 1;
@ -4131,24 +4117,22 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
return ret;
}
static void hidpp_overwrite_name(struct hid_device *hdev)
/* Get name + serial for USB and Bluetooth HID++ devices */
static void hidpp_non_unifying_init(struct hidpp_device *hidpp)
{
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct hid_device *hdev = hidpp->hid_dev;
char *name;
if (hidpp->protocol_major < 2)
return;
/* Bluetooth devices already have their serialnr set */
if (hid_is_usb(hdev))
hidpp_serial_init(hidpp);
name = hidpp_get_device_name(hidpp);
if (!name) {
hid_err(hdev, "unable to retrieve the name of the device");
} else {
if (name) {
dbg_hid("HID++: Got name: %s\n", name);
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
kfree(name);
}
kfree(name);
}
static int hidpp_input_open(struct input_dev *dev)
@ -4189,15 +4173,18 @@ static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
return input_dev;
}
static void hidpp_connect_event(struct hidpp_device *hidpp)
static void hidpp_connect_event(struct work_struct *work)
{
struct hidpp_device *hidpp = container_of(work, struct hidpp_device, work);
struct hid_device *hdev = hidpp->hid_dev;
int ret = 0;
bool connected = atomic_read(&hidpp->connected);
struct input_dev *input;
char *name, *devm_name;
int ret;
if (!connected) {
/* Get device version to check if it is connected */
ret = hidpp_root_get_protocol_version(hidpp);
if (ret) {
hid_info(hidpp->hid_dev, "Disconnected\n");
if (hidpp->battery.ps) {
hidpp->battery.online = false;
hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
@ -4208,15 +4195,15 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_connect(hdev, connected);
ret = wtp_connect(hdev);
if (ret)
return;
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
ret = m560_send_config_command(hdev, connected);
ret = m560_send_config_command(hdev);
if (ret)
return;
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
ret = k400_connect(hdev, connected);
ret = k400_connect(hdev);
if (ret)
return;
}
@ -4239,14 +4226,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
return;
}
/* the device is already connected, we can ask for its name and
* protocol */
if (!hidpp->protocol_major) {
ret = hidpp_root_get_protocol_version(hidpp);
if (ret) {
hid_err(hdev, "Can not get the protocol version.\n");
return;
}
if (hidpp->protocol_major >= 2) {
u8 feature_index;
if (!hidpp_get_wireless_feature_index(hidpp, &feature_index))
hidpp->wireless_feature_index = feature_index;
}
if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
@ -4391,10 +4375,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp;
int ret;
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);
@ -4423,9 +4404,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
hidpp_application_equals(hdev, HID_GD_MOUSE))
hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
@ -4445,11 +4423,7 @@ 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);
INIT_WORK(&hidpp->work, hidpp_connect_event);
mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait);
@ -4460,10 +4434,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->name);
/*
* Plain USB connections need to actually call start and open
* on the transport driver to allow incoming data.
* First call hid_hw_start(hdev, 0) to allow IO without connecting any
* hid subdrivers (hid-input, hidraw). This allows retrieving the dev's
* name and serial number and store these in hdev->name and hdev->uniq,
* before the hid-input and hidraw drivers expose these to userspace.
*/
ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask);
ret = hid_hw_start(hdev, 0);
if (ret) {
hid_err(hdev, "hw start failed\n");
goto hid_hw_start_fail;
@ -4479,70 +4455,46 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* Allow incoming packets */
hid_device_io_start(hdev);
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
/* Get name + serial, store in hdev->name + hdev->uniq */
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
hidpp_unifying_init(hidpp);
else if (hid_is_usb(hidpp->hid_dev))
hidpp_serial_init(hidpp);
else
hidpp_non_unifying_init(hidpp);
connected = hidpp_root_get_protocol_version(hidpp) == 0;
atomic_set(&hidpp->connected, connected);
if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
if (!connected) {
ret = -ENODEV;
hid_err(hdev, "Device not connected");
goto hid_hw_init_fail;
}
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
hidpp_overwrite_name(hdev);
}
if (connected && hidpp->protocol_major >= 2) {
ret = hidpp_set_wireless_feature_index(hidpp);
if (ret == -ENOENT)
hidpp->wireless_feature_index = 0;
else if (ret)
goto hid_hw_init_fail;
ret = 0;
}
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
if (ret)
goto hid_hw_init_fail;
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
ret = g920_get_config(hidpp, &data);
if (ret)
goto hid_hw_init_fail;
/* Now export the actual inputs and hidraw nodes to the world */
hid_device_io_stop(hdev);
ret = hid_connect(hdev, connect_mask);
if (ret) {
hid_err(hdev, "%s:hid_connect returned error %d\n", __func__, ret);
goto hid_hw_init_fail;
}
/* Check for connected devices now that incoming packets will not be disabled again */
hid_device_io_start(hdev);
schedule_work(&hidpp->work);
flush_work(&hidpp->work);
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_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;
}
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
ret = hidpp_ff_init(hidpp, &data);
struct hidpp_ff_private_data data;
ret = g920_get_config(hidpp, &data);
if (!ret)
ret = hidpp_ff_init(hidpp, &data);
if (ret)
hid_warn(hidpp->hid_dev,
"Unable to initialize force feedback support, errno %d\n",
ret);
}
/*
* This relies on logi_dj_ll_close() being a no-op so that DJ connection
* events will still be received.
*/
hid_hw_close(hdev);
return ret;
hid_hw_init_fail:

View File

@ -1802,7 +1802,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
}
#ifdef CONFIG_PM
static int mt_suspend(struct hid_device *hdev, pm_message_t state)
{
struct mt_device *td = hid_get_drvdata(hdev);
@ -1836,7 +1835,6 @@ static int mt_resume(struct hid_device *hdev)
return 0;
}
#endif
static void mt_remove(struct hid_device *hdev)
{
@ -2259,10 +2257,8 @@ static struct hid_driver mt_driver = {
.usage_table = mt_grabbed_usages,
.event = mt_event,
.report = mt_report,
#ifdef CONFIG_PM
.suspend = mt_suspend,
.reset_resume = mt_reset_resume,
.resume = mt_resume,
#endif
.suspend = pm_ptr(mt_suspend),
.reset_resume = pm_ptr(mt_reset_resume),
.resume = pm_ptr(mt_resume),
};
module_hid_driver(mt_driver);

View File

@ -410,6 +410,18 @@ static const char * const joycon_player_led_names[] = {
LED_FUNCTION_PLAYER4,
};
#define JC_NUM_LEDS ARRAY_SIZE(joycon_player_led_names)
#define JC_NUM_LED_PATTERNS 8
/* Taken from https://www.nintendo.com/my/support/qa/detail/33822 */
static const enum led_brightness joycon_player_led_patterns[JC_NUM_LED_PATTERNS][JC_NUM_LEDS] = {
{ 1, 0, 0, 0 },
{ 1, 1, 0, 0 },
{ 1, 1, 1, 0 },
{ 1, 1, 1, 1 },
{ 1, 0, 0, 1 },
{ 1, 0, 1, 0 },
{ 1, 0, 1, 1 },
{ 0, 1, 1, 0 },
};
/* Each physical controller is associated with a joycon_ctlr struct */
struct joycon_ctlr {
@ -699,6 +711,25 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
return joycon_send_subcmd(ctlr, req, 1, HZ/4);
}
static int joycon_set_home_led(struct joycon_ctlr *ctlr, enum led_brightness brightness)
{
struct joycon_subcmd_request *req;
u8 buffer[sizeof(*req) + 5] = { 0 };
u8 *data;
req = (struct joycon_subcmd_request *)buffer;
req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
data = req->data;
data[0] = 0x01;
data[1] = brightness << 4;
data[2] = brightness | (brightness << 4);
data[3] = 0x11;
data[4] = 0x11;
hid_dbg(ctlr->hdev, "setting home led brightness\n");
return joycon_send_subcmd(ctlr, req, 5, HZ/4);
}
static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
u32 start_addr, u8 size, u8 **reply)
{
@ -1840,6 +1871,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
return 0;
}
/* Because the subcommand sets all the leds at once, the brightness argument is ignored */
static int joycon_player_led_brightness_set(struct led_classdev *led,
enum led_brightness brightness)
{
@ -1849,7 +1881,6 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
int val = 0;
int i;
int ret;
int num;
ctlr = hid_get_drvdata(hdev);
if (!ctlr) {
@ -1857,21 +1888,10 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
return -ENODEV;
}
/* determine which player led this is */
for (num = 0; num < JC_NUM_LEDS; num++) {
if (&ctlr->leds[num] == led)
break;
}
if (num >= JC_NUM_LEDS)
return -EINVAL;
for (i = 0; i < JC_NUM_LEDS; i++)
val |= ctlr->leds[i].brightness << i;
mutex_lock(&ctlr->output_mutex);
for (i = 0; i < JC_NUM_LEDS; i++) {
if (i == num)
val |= brightness << i;
else
val |= ctlr->leds[i].brightness << i;
}
ret = joycon_set_player_leds(ctlr, 0, val);
mutex_unlock(&ctlr->output_mutex);
@ -1884,9 +1904,6 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
struct device *dev = led->dev->parent;
struct hid_device *hdev = to_hid_device(dev);
struct joycon_ctlr *ctlr;
struct joycon_subcmd_request *req;
u8 buffer[sizeof(*req) + 5] = { 0 };
u8 *data;
int ret;
ctlr = hid_get_drvdata(hdev);
@ -1894,43 +1911,35 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
hid_err(hdev, "No controller data\n");
return -ENODEV;
}
req = (struct joycon_subcmd_request *)buffer;
req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
data = req->data;
data[0] = 0x01;
data[1] = brightness << 4;
data[2] = brightness | (brightness << 4);
data[3] = 0x11;
data[4] = 0x11;
hid_dbg(hdev, "setting home led brightness\n");
mutex_lock(&ctlr->output_mutex);
ret = joycon_send_subcmd(ctlr, req, 5, HZ/4);
ret = joycon_set_home_led(ctlr, brightness);
mutex_unlock(&ctlr->output_mutex);
return ret;
}
static DEFINE_MUTEX(joycon_input_num_mutex);
static DEFINE_SPINLOCK(joycon_input_num_spinlock);
static int joycon_leds_create(struct joycon_ctlr *ctlr)
{
struct hid_device *hdev = ctlr->hdev;
struct device *dev = &hdev->dev;
const char *d_name = dev_name(dev);
struct led_classdev *led;
int led_val = 0;
char *name;
int ret = 0;
int ret;
int i;
static int input_num = 1;
unsigned long flags;
int player_led_pattern;
static int input_num;
/* Set the default controller player leds based on controller number */
mutex_lock(&joycon_input_num_mutex);
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
if (ret)
hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
mutex_unlock(&ctlr->output_mutex);
/*
* Set the player leds based on controller number
* Because there is no standard concept of "player number", the pattern
* number will simply increase by 1 every time a controller is connected.
*/
spin_lock_irqsave(&joycon_input_num_spinlock, flags);
player_led_pattern = input_num++ % JC_NUM_LED_PATTERNS;
spin_unlock_irqrestore(&joycon_input_num_spinlock, flags);
/* configure the player LEDs */
for (i = 0; i < JC_NUM_LEDS; i++) {
@ -1938,31 +1947,37 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
d_name,
"green",
joycon_player_led_names[i]);
if (!name) {
mutex_unlock(&joycon_input_num_mutex);
if (!name)
return -ENOMEM;
}
led = &ctlr->leds[i];
led->name = name;
led->brightness = ((i + 1) <= input_num) ? 1 : 0;
led->brightness = joycon_player_led_patterns[player_led_pattern][i];
led->max_brightness = 1;
led->brightness_set_blocking =
joycon_player_led_brightness_set;
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
led_val |= joycon_player_led_patterns[player_led_pattern][i] << i;
}
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_player_leds(ctlr, 0, led_val);
mutex_unlock(&ctlr->output_mutex);
if (ret) {
hid_warn(hdev, "Failed to set players LEDs, skipping registration; ret=%d\n", ret);
goto home_led;
}
for (i = 0; i < JC_NUM_LEDS; i++) {
led = &ctlr->leds[i];
ret = devm_led_classdev_register(&hdev->dev, led);
if (ret) {
hid_err(hdev, "Failed registering %s LED\n", led->name);
mutex_unlock(&joycon_input_num_mutex);
hid_err(hdev, "Failed to register player %d LED; ret=%d\n", i + 1, ret);
return ret;
}
}
if (++input_num > 4)
input_num = 1;
mutex_unlock(&joycon_input_num_mutex);
home_led:
/* configure the home LED */
if (jc_type_has_right(ctlr)) {
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
@ -1978,17 +1993,21 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
led->max_brightness = 0xF;
led->brightness_set_blocking = joycon_home_led_brightness_set;
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
/* Set the home LED to 0 as default state */
mutex_lock(&ctlr->output_mutex);
ret = joycon_set_home_led(ctlr, 0);
mutex_unlock(&ctlr->output_mutex);
if (ret) {
hid_warn(hdev, "Failed to set home LED, skipping registration; ret=%d\n", ret);
return 0;
}
ret = devm_led_classdev_register(&hdev->dev, led);
if (ret) {
hid_err(hdev, "Failed registering home led\n");
hid_err(hdev, "Failed to register home LED; ret=%d\n", ret);
return ret;
}
/* Set the home LED to 0 as default state */
ret = joycon_home_led_brightness_set(led, 0);
if (ret) {
hid_warn(hdev, "Failed to set home LED default, unregistering home LED");
devm_led_classdev_unregister(&hdev->dev, led);
}
}
return 0;

View File

@ -915,6 +915,15 @@ err_id:
return ERR_PTR(ret);
}
static void thunderstrike_destroy(struct thunderstrike *ts)
{
led_classdev_unregister(&ts->led_dev);
power_supply_unregister(ts->base.battery_dev.psy);
if (ts->haptics_dev)
input_unregister_device(ts->haptics_dev);
ida_free(&thunderstrike_ida, ts->id);
}
static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage, unsigned long **bit,
@ -1074,11 +1083,7 @@ static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id)
err_stop:
hid_hw_stop(hdev);
err_ts_create:
power_supply_unregister(ts->base.battery_dev.psy);
if (ts->haptics_dev)
input_unregister_device(ts->haptics_dev);
led_classdev_unregister(&ts->led_dev);
ida_free(&thunderstrike_ida, ts->id);
thunderstrike_destroy(ts);
return ret;
}
@ -1090,11 +1095,7 @@ static void shield_remove(struct hid_device *hdev)
ts = container_of(dev, struct thunderstrike, base);
hid_hw_close(hdev);
power_supply_unregister(dev->battery_dev.psy);
if (ts->haptics_dev)
input_unregister_device(ts->haptics_dev);
led_classdev_unregister(&ts->led_dev);
ida_free(&thunderstrike_ida, ts->id);
thunderstrike_destroy(ts);
del_timer_sync(&ts->psy_stats_timer);
cancel_work_sync(&ts->hostcmd_req_work);
hid_hw_stop(hdev);

View File

@ -66,6 +66,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_PRO_WIRELESS_KM5221W), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES), HID_QUIRK_MULTI_INPUT },

View File

@ -436,7 +436,6 @@ static void rmi_report(struct hid_device *hid, struct hid_report *report)
input_sync(field->hidinput->input);
}
#ifdef CONFIG_PM
static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
{
struct rmi_data *data = hid_get_drvdata(hdev);
@ -483,7 +482,6 @@ out:
hid_hw_close(hdev);
return ret;
}
#endif /* CONFIG_PM */
static int rmi_hid_reset(struct rmi_transport_dev *xport, u16 reset_addr)
{
@ -774,11 +772,9 @@ static struct hid_driver rmi_driver = {
.report = rmi_report,
.input_mapping = rmi_input_mapping,
.input_configured = rmi_input_configured,
#ifdef CONFIG_PM
.suspend = rmi_suspend,
.resume = rmi_post_resume,
.reset_resume = rmi_post_resume,
#endif
.suspend = pm_ptr(rmi_suspend),
.resume = pm_ptr(rmi_post_resume),
.reset_resume = pm_ptr(rmi_post_resume),
};
module_hid_driver(rmi_driver);

View File

@ -56,6 +56,11 @@ static struct uclogic_raw_event_hook_test test_events[] = {
},
};
static void fake_work(struct work_struct *work)
{
}
static void hid_test_uclogic_exec_event_hook_test(struct kunit *test)
{
struct uclogic_params p = {0, };
@ -77,6 +82,8 @@ static void hid_test_uclogic_exec_event_hook_test(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filter->event);
memcpy(filter->event, &hook_events[n].event[0], filter->size);
INIT_WORK(&filter->work, fake_work);
list_add_tail(&filter->list, &p.event_hooks->list);
}

View File

@ -174,12 +174,26 @@ static void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test)
KUNIT_EXPECT_EQ(test, params->frame_type, frame_type);
}
struct fake_device {
unsigned long quirks;
};
static void hid_test_uclogic_params_cleanup_event_hooks(struct kunit *test)
{
int res, n;
struct hid_device *hdev;
struct fake_device *fake_dev;
struct uclogic_params p = {0, };
res = uclogic_params_ugee_v2_init_event_hooks(NULL, &p);
hdev = kunit_kzalloc(test, sizeof(struct hid_device), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdev);
fake_dev = kunit_kzalloc(test, sizeof(struct fake_device), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_dev);
hid_set_drvdata(hdev, fake_dev);
res = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);
KUNIT_ASSERT_EQ(test, res, 0);
/* Check that the function can be called repeatedly */

View File

@ -1562,7 +1562,6 @@ static int hid_post_reset(struct usb_interface *intf)
return 0;
}
#ifdef CONFIG_PM
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
{
int status = 0;
@ -1654,8 +1653,6 @@ static int hid_reset_resume(struct usb_interface *intf)
return status;
}
#endif /* CONFIG_PM */
static const struct usb_device_id hid_usb_ids[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
@ -1668,11 +1665,9 @@ static struct usb_driver hid_driver = {
.name = "usbhid",
.probe = usbhid_probe,
.disconnect = usbhid_disconnect,
#ifdef CONFIG_PM
.suspend = hid_suspend,
.resume = hid_resume,
.reset_resume = hid_reset_resume,
#endif
.suspend = pm_ptr(hid_suspend),
.resume = pm_ptr(hid_resume),
.reset_resume = pm_ptr(hid_reset_resume),
.pre_reset = hid_pre_reset,
.post_reset = hid_post_reset,
.id_table = hid_usb_ids,

View File

@ -833,11 +833,11 @@ struct hid_driver {
void (*feature_mapping)(struct hid_device *hdev,
struct hid_field *field,
struct hid_usage *usage);
#ifdef CONFIG_PM
int (*suspend)(struct hid_device *hdev, pm_message_t message);
int (*resume)(struct hid_device *hdev);
int (*reset_resume)(struct hid_device *hdev);
#endif
/* private: */
struct device_driver driver;
};

View File

@ -21,7 +21,9 @@ CXX ?= $(CROSS_COMPILE)g++
HOSTPKG_CONFIG := pkg-config
CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(KHDR_INCLUDES) -I$(OUTPUT)
CFLAGS += -g -O0 -rdynamic -Wall -Werror -I$(OUTPUT)
CFLAGS += -I$(OUTPUT)/tools/include
LDLIBS += -lelf -lz -lrt -lpthread
# Silence some warnings when compiled with clang
@ -65,7 +67,6 @@ BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
SCRATCH_DIR := $(OUTPUT)/tools
BUILD_DIR := $(SCRATCH_DIR)/build
INCLUDE_DIR := $(SCRATCH_DIR)/include
KHDR_INCLUDES := $(SCRATCH_DIR)/uapi/include
BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a
ifneq ($(CROSS_COMPILE),)
HOST_BUILD_DIR := $(BUILD_DIR)/host
@ -151,9 +152,6 @@ else
$(Q)cp "$(VMLINUX_H)" $@
endif
$(KHDR_INCLUDES)/linux/hid.h: $(top_srcdir)/include/uapi/linux/hid.h
$(MAKE) -C $(top_srcdir) INSTALL_HDR_PATH=$(SCRATCH_DIR)/uapi headers_install
$(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids \
$(TOOLSDIR)/bpf/resolve_btfids/main.c \
$(TOOLSDIR)/lib/rbtree.c \
@ -231,7 +229,7 @@ $(BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(OUTPUT)
$(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $<
$(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked1.o) name $(notdir $(<:.bpf.o=)) > $@
$(OUTPUT)/%.o: %.c $(BPF_SKELS) $(KHDR_INCLUDES)/linux/hid.h
$(OUTPUT)/%.o: %.c $(BPF_SKELS)
$(call msg,CC,,$@)
$(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@

View File

@ -1,8 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Red hat */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "hid_bpf_helpers.h"
char _license[] SEC("license") = "GPL";

View File

@ -5,6 +5,83 @@
#ifndef __HID_BPF_HELPERS_H
#define __HID_BPF_HELPERS_H
/* "undefine" structs and enums in vmlinux.h, because we "override" them below */
#define hid_bpf_ctx hid_bpf_ctx___not_used
#define hid_report_type hid_report_type___not_used
#define hid_class_request hid_class_request___not_used
#define hid_bpf_attach_flags hid_bpf_attach_flags___not_used
#define HID_INPUT_REPORT HID_INPUT_REPORT___not_used
#define HID_OUTPUT_REPORT HID_OUTPUT_REPORT___not_used
#define HID_FEATURE_REPORT HID_FEATURE_REPORT___not_used
#define HID_REPORT_TYPES HID_REPORT_TYPES___not_used
#define HID_REQ_GET_REPORT HID_REQ_GET_REPORT___not_used
#define HID_REQ_GET_IDLE HID_REQ_GET_IDLE___not_used
#define HID_REQ_GET_PROTOCOL HID_REQ_GET_PROTOCOL___not_used
#define HID_REQ_SET_REPORT HID_REQ_SET_REPORT___not_used
#define HID_REQ_SET_IDLE HID_REQ_SET_IDLE___not_used
#define HID_REQ_SET_PROTOCOL HID_REQ_SET_PROTOCOL___not_used
#define HID_BPF_FLAG_NONE HID_BPF_FLAG_NONE___not_used
#define HID_BPF_FLAG_INSERT_HEAD HID_BPF_FLAG_INSERT_HEAD___not_used
#define HID_BPF_FLAG_MAX HID_BPF_FLAG_MAX___not_used
#include "vmlinux.h"
#undef hid_bpf_ctx
#undef hid_report_type
#undef hid_class_request
#undef hid_bpf_attach_flags
#undef HID_INPUT_REPORT
#undef HID_OUTPUT_REPORT
#undef HID_FEATURE_REPORT
#undef HID_REPORT_TYPES
#undef HID_REQ_GET_REPORT
#undef HID_REQ_GET_IDLE
#undef HID_REQ_GET_PROTOCOL
#undef HID_REQ_SET_REPORT
#undef HID_REQ_SET_IDLE
#undef HID_REQ_SET_PROTOCOL
#undef HID_BPF_FLAG_NONE
#undef HID_BPF_FLAG_INSERT_HEAD
#undef HID_BPF_FLAG_MAX
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/const.h>
enum hid_report_type {
HID_INPUT_REPORT = 0,
HID_OUTPUT_REPORT = 1,
HID_FEATURE_REPORT = 2,
HID_REPORT_TYPES,
};
struct hid_bpf_ctx {
__u32 index;
const struct hid_device *hid;
__u32 allocated_size;
enum hid_report_type report_type;
union {
__s32 retval;
__s32 size;
};
} __attribute__((preserve_access_index));
enum hid_class_request {
HID_REQ_GET_REPORT = 0x01,
HID_REQ_GET_IDLE = 0x02,
HID_REQ_GET_PROTOCOL = 0x03,
HID_REQ_SET_REPORT = 0x09,
HID_REQ_SET_IDLE = 0x0A,
HID_REQ_SET_PROTOCOL = 0x0B,
};
enum hid_bpf_attach_flags {
HID_BPF_FLAG_NONE = 0,
HID_BPF_FLAG_INSERT_HEAD = _BITUL(0),
HID_BPF_FLAG_MAX,
};
/* following are kfuncs exported by HID for HID-BPF */
extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx,
unsigned int offset,