Commit 4ea4ed22 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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()
  ...
parents 27beb3ca 1372e73a
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -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,8 +1166,12 @@ static void cp2112_gpio_irq_shutdown(struct irq_data *d)
	struct cp2112_device *dev = gpiochip_get_data(gc);

	cp2112_gpio_irq_mask(d);

	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");
+1 −0
Original line number Diff line number Diff line
@@ -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
+79 −39
Original line number Diff line number Diff line
@@ -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,6 +686,22 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
{
	struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);

	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)) {
@@ -694,6 +728,7 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
			}
			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 @@ static int lenovo_probe(struct hid_device *hdev,
	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);

+63 −111
Original line number Diff line number Diff line
@@ -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,25 +4117,23 @@ 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);
	}
}

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);

	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;
		}

		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;
	}

	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);
	else
		hidpp_non_unifying_init(hidpp);

	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);
	hid_device_io_stop(hdev);
	ret = hid_connect(hdev, connect_mask);
	if (ret) {
			hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
			goto hid_hw_start_fail;
		}
		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 (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
		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:
+3 −7
Original line number Diff line number Diff line
@@ -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);
Loading