Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71af01a8 authored by HungNien Chen's avatar HungNien Chen Committed by Jiri Kosina
Browse files

HID: i2c-hid: add a simple quirk to fix device defects



Certain devices produced by Weida Tech need to have a wakeup command sent to
them before powering on. The call itself will come back with error, but the
device can be powered on afterwards.

[jkosina@suse.cz: rewrite changelog]
[jkosina@suse.cz: remove unused device ID addition]
Signed-off-by: default avatarHungNien Chen <hn.chen@weidahitech.com>
Reviewed-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 93d26aea
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -1030,6 +1030,10 @@
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH	0x0500
#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET	0x0502

#define	USB_VENDOR_ID_WEIDA		0x2575
#define	USB_DEVICE_ID_WEIDA_8752	0xC300
#define	USB_DEVICE_ID_WEIDA_8755	0xC301

#define USB_VENDOR_ID_WISEGROUP		0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS	0x0005
#define USB_DEVICE_ID_SUPER_JOY_BOX_3	0x8888
+57 −0
Original line number Diff line number Diff line
@@ -40,6 +40,11 @@

#include <linux/i2c/i2c-hid.h>

#include "../hid-ids.h"

/* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV	BIT(0)

/* flags */
#define I2C_HID_STARTED		0
#define I2C_HID_RESET_PENDING	1
@@ -142,6 +147,7 @@ struct i2c_hid {
	char			*argsbuf;	/* Command arguments buffer */

	unsigned long		flags;		/* device flags */
	unsigned long		quirks;		/* Various quirks */

	wait_queue_head_t	wait;		/* For waiting the interrupt */

@@ -151,6 +157,39 @@ struct i2c_hid {
	struct mutex		reset_lock;
};

static const struct i2c_hid_quirks {
	__u16 idVendor;
	__u16 idProduct;
	__u32 quirks;
} i2c_hid_quirks[] = {
	{ USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8752,
		I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
	{ USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755,
		I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
	{ 0, 0 }
};

/*
 * i2c_hid_lookup_quirk: return any quirks associated with a I2C HID device
 * @idVendor: the 16-bit vendor ID
 * @idProduct: the 16-bit product ID
 *
 * Returns: a u32 quirks value.
 */
static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct)
{
	u32 quirks = 0;
	int n;

	for (n = 0; i2c_hid_quirks[n].idVendor; n++)
		if (i2c_hid_quirks[n].idVendor == idVendor &&
		    (i2c_hid_quirks[n].idProduct == (__u16)HID_ANY_ID ||
		     i2c_hid_quirks[n].idProduct == idProduct))
			quirks = i2c_hid_quirks[n].quirks;

	return quirks;
}

static int __i2c_hid_command(struct i2c_client *client,
		const struct i2c_hid_cmd *command, u8 reportID,
		u8 reportType, u8 *args, int args_len,
@@ -343,11 +382,27 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)

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

	/*
	 * Some devices require to send a command to wakeup before power on.
	 * 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) {
		ret = i2c_hid_command(client, &hid_set_power_cmd, NULL, 0);

		/* Device was already activated */
		if (!ret)
			goto set_pwr_exit;
	}

	ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
		0, NULL, 0, NULL, 0);

	if (ret)
		dev_err(&client->dev, "failed to change power setting.\n");

set_pwr_exit:
	return ret;
}

@@ -1032,6 +1087,8 @@ static int i2c_hid_probe(struct i2c_client *client,
		 client->name, hid->vendor, hid->product);
	strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys));

	ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product);

	ret = hid_add_device(hid);
	if (ret) {
		if (ret != -ENODEV)