mirror of
https://github.com/torvalds/linux.git
synced 2025-11-03 10:10:33 +02:00
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)
This commit is contained in:
commit
ff18ab5a45
1 changed files with 70 additions and 67 deletions
|
|
@ -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;
|
||||
dev_err(&ihid->client->dev,
|
||||
"failed to reset device: %d\n", ret);
|
||||
goto err_clear_reset;
|
||||
}
|
||||
|
||||
if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
|
||||
msleep(100);
|
||||
goto out;
|
||||
}
|
||||
return 0;
|
||||
|
||||
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__);
|
||||
|
||||
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;
|
||||
int ret = 0;
|
||||
|
||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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,23 +760,34 @@ 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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_hid_start(struct hid_device *hid)
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue