Commit ff18ab5a authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.8/i2c-hid' into for-linus

- rework of wait-for-reset in order to reduce the need
  for I2C_HID_QUIRK_NO_IRQ_AFTER_RESET qurk; the success rate is now
  50% better, but there are still further improvements to be made (Hans de Goede)
parents 82a18fc3 7d7a2528
Loading
Loading
Loading
Loading
+70 −67
Original line number Diff line number Diff line
@@ -44,12 +44,11 @@
#include "i2c-hid.h"

/* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV	BIT(0)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET	BIT(1)
#define I2C_HID_QUIRK_BOGUS_IRQ			BIT(4)
#define I2C_HID_QUIRK_RESET_ON_RESUME		BIT(5)
#define I2C_HID_QUIRK_BAD_INPUT_SIZE		BIT(6)
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET	BIT(7)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET	BIT(0)
#define I2C_HID_QUIRK_BOGUS_IRQ			BIT(1)
#define I2C_HID_QUIRK_RESET_ON_RESUME		BIT(2)
#define I2C_HID_QUIRK_BAD_INPUT_SIZE		BIT(3)
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET	BIT(4)

/* Command opcodes */
#define I2C_HID_OPCODE_RESET			0x01
@@ -120,8 +119,6 @@ static const struct i2c_hid_quirks {
	__u16 idProduct;
	__u32 quirks;
} i2c_hid_quirks[] = {
	{ USB_VENDOR_ID_WEIDA, HID_ANY_ID,
		I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
	{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
		I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
	{ I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15,
@@ -395,8 +392,7 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
	 * The call will get a return value (EREMOTEIO) but device will be
	 * triggered and activated. After that, it goes like a normal device.
	 */
	if (power_state == I2C_HID_PWR_ON &&
	    ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
	if (power_state == I2C_HID_PWR_ON) {
		ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON);

		/* Device was already activated */
@@ -426,12 +422,23 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
	return ret;
}

static int i2c_hid_execute_reset(struct i2c_hid *ihid)
static int i2c_hid_start_hwreset(struct i2c_hid *ihid)
{
	size_t length = 0;
	int ret;

	i2c_hid_dbg(ihid, "resetting...\n");
	i2c_hid_dbg(ihid, "%s\n", __func__);

	/*
	 * This prevents sending feature reports while the device is
	 * being reset. Otherwise we may lose the reset complete
	 * interrupt.
	 */
	lockdep_assert_held(&ihid->reset_lock);

	ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
	if (ret)
		return ret;

	/* Prepare reset command. Command register goes first. */
	*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
@@ -444,60 +451,40 @@ static int i2c_hid_execute_reset(struct i2c_hid *ihid)

	ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
	if (ret) {
		dev_err(&ihid->client->dev, "failed to reset device.\n");
		goto out;
	}

	if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
		msleep(100);
		goto out;
		dev_err(&ihid->client->dev,
			"failed to reset device: %d\n", ret);
		goto err_clear_reset;
	}

	i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
	if (!wait_event_timeout(ihid->wait,
				!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
				msecs_to_jiffies(5000))) {
		ret = -ENODATA;
		goto out;
	}
	i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
	return 0;

out:
err_clear_reset:
	clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
	i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
	return ret;
}

static int i2c_hid_hwreset(struct i2c_hid *ihid)
static int i2c_hid_finish_hwreset(struct i2c_hid *ihid)
{
	int ret;

	i2c_hid_dbg(ihid, "%s\n", __func__);
	int ret = 0;

	/*
	 * This prevents sending feature reports while the device is
	 * being reset. Otherwise we may lose the reset complete
	 * interrupt.
	 */
	mutex_lock(&ihid->reset_lock);

	ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
	if (ret)
		goto out_unlock;
	i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);

	ret = i2c_hid_execute_reset(ihid);
	if (ret) {
		dev_err(&ihid->client->dev,
			"failed to reset device: %d\n", ret);
		i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
		goto out_unlock;
	if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
		msleep(100);
		clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
	} else if (!wait_event_timeout(ihid->wait,
				       !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
				       msecs_to_jiffies(1000))) {
		dev_warn(&ihid->client->dev, "device did not ack reset within 1000 ms\n");
		clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
	}
	i2c_hid_dbg(ihid, "%s: finished.\n", __func__);

	/* At least some SIS devices need this after reset */
	if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET))
		ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);

out_unlock:
	mutex_unlock(&ihid->reset_lock);
	return ret;
}

@@ -729,11 +716,10 @@ static int i2c_hid_parse(struct hid_device *hid)
	struct i2c_client *client = hid->driver_data;
	struct i2c_hid *ihid = i2c_get_clientdata(client);
	struct i2c_hid_desc *hdesc = &ihid->hdesc;
	char *rdesc = NULL, *use_override = NULL;
	unsigned int rsize;
	char *rdesc;
	int ret;
	int tries = 3;
	char *use_override;

	i2c_hid_dbg(ihid, "entering %s\n", __func__);

@@ -743,14 +729,15 @@ static int i2c_hid_parse(struct hid_device *hid)
		return -EINVAL;
	}

	mutex_lock(&ihid->reset_lock);
	do {
		ret = i2c_hid_hwreset(ihid);
		ret = i2c_hid_start_hwreset(ihid);
		if (ret)
			msleep(1000);
	} while (tries-- > 0 && ret);

	if (ret)
		return ret;
		goto abort_reset;

	use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name,
								&rsize);
@@ -762,8 +749,8 @@ static int i2c_hid_parse(struct hid_device *hid)
		rdesc = kzalloc(rsize, GFP_KERNEL);

		if (!rdesc) {
			dbg_hid("couldn't allocate rdesc memory\n");
			return -ENOMEM;
			ret = -ENOMEM;
			goto abort_reset;
		}

		i2c_hid_dbg(ihid, "asking HID report descriptor\n");
@@ -773,25 +760,36 @@ static int i2c_hid_parse(struct hid_device *hid)
					    rdesc, rsize);
		if (ret) {
			hid_err(hid, "reading report descriptor failed\n");
			kfree(rdesc);
			return -EIO;
			goto abort_reset;
		}
	}

	/*
	 * Windows directly reads the report-descriptor after sending reset
	 * and then waits for resets completion afterwards. Some touchpads
	 * actually wait for the report-descriptor to be read before signalling
	 * reset completion.
	 */
	ret = i2c_hid_finish_hwreset(ihid);
abort_reset:
	clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
	mutex_unlock(&ihid->reset_lock);
	if (ret)
		goto out;

	i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);

	ret = hid_parse_report(hid, rdesc, rsize);
	if (ret)
		dbg_hid("parsing report descriptor failed\n");

out:
	if (!use_override)
		kfree(rdesc);

	if (ret) {
		dbg_hid("parsing report descriptor failed\n");
	return ret;
}

	return 0;
}

static int i2c_hid_start(struct hid_device *hid)
{
	struct i2c_client *client = hid->driver_data;
@@ -987,10 +985,15 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid)
	 * However some ALPS touchpads generate IRQ storm without reset, so
	 * let's still reset them here.
	 */
	if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME)
		ret = i2c_hid_hwreset(ihid);
	else
	if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) {
		mutex_lock(&ihid->reset_lock);
		ret = i2c_hid_start_hwreset(ihid);
		if (ret == 0)
			ret = i2c_hid_finish_hwreset(ihid);
		mutex_unlock(&ihid->reset_lock);
	} else {
		ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
	}

	if (ret)
		return ret;