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

Commit c6400e5c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull HID updates from Jiri Kosina:

 - support for Pro Pen slim, from Jason Gerecke

 - power management improvements to Intel-ISH driver, from Song Hongyan

 - UCLogic driver revamp in order to be able to support wider range of
   Huion tablets, from Nikolai Kondrashov

 - Asus Transbook support, from NOGUCHI Hiroshi

 - other assorted small bugfixes / cleanups and device ID additions

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (46 commits)
  HID: Remove Waltop tablets from hid_have_special_driver
  HID: Remove KYE tablets from hid_have_special_driver
  HID: Remove hid-uclogic entries from hid_have_special_driver
  HID: uclogic: Do not initialize non-USB devices
  HID: uclogic: Add support for Ugee G5
  HID: uclogic: Support Gray-coded rotary encoders
  HID: uclogic: Support faking Wacom pad device ID
  HID: uclogic: Add support for XP-Pen Deco 01
  HID: uclogic: Add support for XP-Pen Star G640
  HID: uclogic: Add support for XP-Pen Star G540
  HID: uclogic: Add support for Ugee EX07S frame controls
  HID: uclogic: Add support for Ugee M540
  HID: uclogic: Add support for Ugee 2150
  HID: uclogic: Support v2 protocol
  HID: uclogic: Support fragmented high-res reports
  HID: uclogic: Support in-range reporting emulation
  HID: uclogic: Designate current protocol v1
  HID: uclogic: Re-initialize tablets on resume
  HID: uclogic: Extract tablet parameter discovery into a module
  HID: uclogic: Extract report descriptors to a module
  ...
parents b7af27bf 49374f00
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -412,6 +412,12 @@ config HID_WALTOP
	---help---
	Support for Waltop tablets.

config HID_VIEWSONIC
	tristate "ViewSonic/Signotec"
	depends on HID
	help
	  Support for ViewSonic/Signotec PD1011 signature pad.

config HID_GYRATION
	tristate "Gyration remote control"
	depends on HID
@@ -590,6 +596,13 @@ config HID_MAGICMOUSE
	Say Y here if you want support for the multi-touch features of the
	Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.

config HID_MALTRON
	tristate "Maltron L90 keyboard"
	depends on HID
	---help---
	Adds support for the volume up, volume down, mute, and play/pause buttons
	of the Maltron L90 keyboard.

config HID_MAYFLASH
	tristate "Mayflash game controller adapter force feedback"
	depends on HID
+5 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_LOGITECH_DJ)	+= hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP)	+= hid-logitech-hidpp.o
obj-$(CONFIG_HID_MAGICMOUSE)	+= hid-magicmouse.o
obj-$(CONFIG_HID_MALTRON)	+= hid-maltron.o
obj-$(CONFIG_HID_MAYFLASH)	+= hid-mf.o
obj-$(CONFIG_HID_MICROSOFT)	+= hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY)	+= hid-monterey.o
@@ -108,12 +109,16 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TIVO)		+= hid-tivo.o
obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
obj-$(CONFIG_HID_TWINHAN)	+= hid-twinhan.o
hid-uclogic-objs		:= hid-uclogic-core.o \
				   hid-uclogic-rdesc.o \
				   hid-uclogic-params.o
obj-$(CONFIG_HID_UCLOGIC)	+= hid-uclogic.o
obj-$(CONFIG_HID_UDRAW_PS3)	+= hid-udraw-ps3.o
obj-$(CONFIG_HID_LED)		+= hid-led.o
obj-$(CONFIG_HID_XINMO)		+= hid-xinmo.o
obj-$(CONFIG_HID_ZEROPLUS)	+= hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON)	+= hid-zydacron.o
obj-$(CONFIG_HID_VIEWSONIC)	+= hid-viewsonic.o

wacom-objs			:= wacom_wac.o wacom_sys.o
obj-$(CONFIG_HID_WACOM)		+= wacom.o
+231 −16
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <linux/platform_data/x86/asus-wmi.h>
#include <linux/input/mt.h>
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
#include <linux/power_supply.h>

#include "hid-ids.h"

@@ -61,6 +62,13 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define CONTACT_TOUCH_MAJOR_MASK 0x07
#define CONTACT_PRESSURE_MASK 0x7f

#define	BATTERY_REPORT_ID	(0x03)
#define	BATTERY_REPORT_SIZE	(1 + 8)
#define	BATTERY_LEVEL_MAX	((u8)255)
#define	BATTERY_STAT_DISCONNECT	(0)
#define	BATTERY_STAT_CHARGING	(1)
#define	BATTERY_STAT_FULL	(2)

#define QUIRK_FIX_NOTEBOOK_REPORT	BIT(0)
#define QUIRK_NO_INIT_REPORTS		BIT(1)
#define QUIRK_SKIP_INPUT_MAPPING	BIT(2)
@@ -71,6 +79,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_T100CHI			BIT(7)
#define QUIRK_G752_KEYBOARD		BIT(8)
#define QUIRK_T101HA_DOCK		BIT(9)
#define QUIRK_T90CHI			BIT(10)

#define I2C_KEYBOARD_QUIRKS			(QUIRK_FIX_NOTEBOOK_REPORT | \
						 QUIRK_NO_INIT_REPORTS | \
@@ -100,12 +109,21 @@ struct asus_touchpad_info {

struct asus_drvdata {
	unsigned long quirks;
	struct hid_device *hdev;
	struct input_dev *input;
	struct asus_kbd_leds *kbd_backlight;
	const struct asus_touchpad_info *tp;
	bool enable_backlight;
	struct power_supply *battery;
	struct power_supply_desc battery_desc;
	int battery_capacity;
	int battery_stat;
	bool battery_in_query;
	unsigned long battery_next_query;
};

static int asus_report_battery(struct asus_drvdata *, u8 *, int);

static const struct asus_touchpad_info asus_i2c_tp = {
	.max_x = 2794,
	.max_y = 1758,
@@ -259,6 +277,9 @@ static int asus_raw_event(struct hid_device *hdev,
{
	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);

	if (drvdata->battery && data[0] == BATTERY_REPORT_ID)
		return asus_report_battery(drvdata, data, size);

	if (drvdata->tp && data[0] == INPUT_REPORT_ID)
		return asus_report_input(drvdata, data, size);

@@ -428,6 +449,164 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
	return ret;
}

/*
 * [0]       REPORT_ID (same value defined in report descriptor)
 * [1]	     rest battery level. range [0..255]
 * [2]..[7]  Bluetooth hardware address (MAC address)
 * [8]       charging status
 *            = 0 : AC offline / discharging
 *            = 1 : AC online  / charging
 *            = 2 : AC online  / fully charged
 */
static int asus_parse_battery(struct asus_drvdata *drvdata, u8 *data, int size)
{
	u8 sts;
	u8 lvl;
	int val;

	lvl = data[1];
	sts = data[8];

	drvdata->battery_capacity = ((int)lvl * 100) / (int)BATTERY_LEVEL_MAX;

	switch (sts) {
	case BATTERY_STAT_CHARGING:
		val = POWER_SUPPLY_STATUS_CHARGING;
		break;
	case BATTERY_STAT_FULL:
		val = POWER_SUPPLY_STATUS_FULL;
		break;
	case BATTERY_STAT_DISCONNECT:
	default:
		val = POWER_SUPPLY_STATUS_DISCHARGING;
		break;
	}
	drvdata->battery_stat = val;

	return 0;
}

static int asus_report_battery(struct asus_drvdata *drvdata, u8 *data, int size)
{
	/* notify only the autonomous event by device */
	if ((drvdata->battery_in_query == false) &&
			 (size == BATTERY_REPORT_SIZE))
		power_supply_changed(drvdata->battery);

	return 0;
}

static int asus_battery_query(struct asus_drvdata *drvdata)
{
	u8 *buf;
	int ret = 0;

	buf = kmalloc(BATTERY_REPORT_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	drvdata->battery_in_query = true;
	ret = hid_hw_raw_request(drvdata->hdev, BATTERY_REPORT_ID,
				buf, BATTERY_REPORT_SIZE,
				HID_INPUT_REPORT, HID_REQ_GET_REPORT);
	drvdata->battery_in_query = false;
	if (ret == BATTERY_REPORT_SIZE)
		ret = asus_parse_battery(drvdata, buf, BATTERY_REPORT_SIZE);
	else
		ret = -ENODATA;

	kfree(buf);

	return ret;
}

static enum power_supply_property asus_battery_props[] = {
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_PRESENT,
	POWER_SUPPLY_PROP_CAPACITY,
	POWER_SUPPLY_PROP_SCOPE,
	POWER_SUPPLY_PROP_MODEL_NAME,
};

#define	QUERY_MIN_INTERVAL	(60 * HZ)	/* 60[sec] */

static int asus_battery_get_property(struct power_supply *psy,
				enum power_supply_property psp,
				union power_supply_propval *val)
{
	struct asus_drvdata *drvdata = power_supply_get_drvdata(psy);
	int ret = 0;

	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
	case POWER_SUPPLY_PROP_CAPACITY:
		if (time_before(drvdata->battery_next_query, jiffies)) {
			drvdata->battery_next_query =
					 jiffies + QUERY_MIN_INTERVAL;
			ret = asus_battery_query(drvdata);
			if (ret)
				return ret;
		}
		if (psp == POWER_SUPPLY_PROP_STATUS)
			val->intval = drvdata->battery_stat;
		else
			val->intval = drvdata->battery_capacity;
		break;
	case POWER_SUPPLY_PROP_PRESENT:
		val->intval = 1;
		break;
	case POWER_SUPPLY_PROP_SCOPE:
		val->intval = POWER_SUPPLY_SCOPE_DEVICE;
		break;
	case POWER_SUPPLY_PROP_MODEL_NAME:
		val->strval = drvdata->hdev->name;
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static int asus_battery_probe(struct hid_device *hdev)
{
	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
	struct power_supply_config pscfg = { .drv_data = drvdata };
	int ret = 0;

	drvdata->battery_capacity = 0;
	drvdata->battery_stat = POWER_SUPPLY_STATUS_UNKNOWN;
	drvdata->battery_in_query = false;

	drvdata->battery_desc.properties = asus_battery_props;
	drvdata->battery_desc.num_properties = ARRAY_SIZE(asus_battery_props);
	drvdata->battery_desc.get_property = asus_battery_get_property;
	drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
	drvdata->battery_desc.use_for_apm = 0;
	drvdata->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
					"asus-keyboard-%s-battery",
					strlen(hdev->uniq) ?
					hdev->uniq : dev_name(&hdev->dev));
	if (!drvdata->battery_desc.name)
		return -ENOMEM;

	drvdata->battery_next_query = jiffies;

	drvdata->battery = devm_power_supply_register(&hdev->dev,
				&(drvdata->battery_desc), &pscfg);
	if (IS_ERR(drvdata->battery)) {
		ret = PTR_ERR(drvdata->battery);
		drvdata->battery = NULL;
		hid_err(hdev, "Unable to register battery device\n");
		return ret;
	}

	power_supply_powers(drvdata->battery, &hdev->dev);

	return ret;
}

static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
	struct input_dev *input = hi->input;
@@ -500,7 +679,7 @@ static int asus_input_mapping(struct hid_device *hdev,
	 * This avoids a bunch of non-functional hid_input devices getting
	 * created because of the T100CHI using HID_QUIRK_MULTI_INPUT.
	 */
	if (drvdata->quirks & QUIRK_T100CHI) {
	if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
		if (field->application == (HID_UP_GENDESK | 0x0080) ||
		    usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) ||
		    usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) ||
@@ -660,6 +839,15 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)

	drvdata->quirks = id->driver_data;

	/*
	 * T90CHI's keyboard dock returns same ID values as T100CHI's dock.
	 * Thus, identify T90CHI dock with product name string.
	 */
	if (strstr(hdev->name, "T90CHI")) {
		drvdata->quirks &= ~QUIRK_T100CHI;
		drvdata->quirks |= QUIRK_T90CHI;
	}

	if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
		drvdata->tp = &asus_i2c_tp;

@@ -694,6 +882,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
	if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
		hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;

	drvdata->hdev = hdev;

	if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
		ret = asus_battery_probe(hdev);
		if (ret) {
			hid_err(hdev,
			    "Asus hid battery_probe failed: %d\n", ret);
			return ret;
		}
	}

	ret = hid_parse(hdev);
	if (ret) {
		hid_err(hdev, "Asus hid parse failed: %d\n", ret);
@@ -769,28 +968,44 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
		hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n");
		rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT;
	}
	/* For the T100CHI keyboard dock */
	if (drvdata->quirks & QUIRK_T100CHI &&
		 *rsize == 403 && rdesc[388] == 0x09 && rdesc[389] == 0x76) {
	/* For the T100CHI/T90CHI keyboard dock */
	if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
		int rsize_orig;
		int offs;

		if (drvdata->quirks & QUIRK_T100CHI) {
			rsize_orig = 403;
			offs = 388;
		} else {
			rsize_orig = 306;
			offs = 291;
		}

		/*
		 * Change Usage (76h) to Usage Minimum (00h), Usage Maximum
		 * (FFh) and clear the flags in the Input() byte.
		 * Note the descriptor has a bogus 0 byte at the end so we
		 * only need 1 extra byte.
		 */
		*rsize = 404;
		if (*rsize == rsize_orig &&
			rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) {
			*rsize = rsize_orig + 1;
			rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL);
			if (!rdesc)
				return NULL;

		hid_info(hdev, "Fixing up T100CHI keyb report descriptor\n");
		memmove(rdesc + 392, rdesc + 390, 12);
		rdesc[388] = 0x19;
		rdesc[389] = 0x00;
		rdesc[390] = 0x29;
		rdesc[391] = 0xff;
		rdesc[402] = 0x00;
			hid_info(hdev, "Fixing up %s keyb report descriptor\n",
				drvdata->quirks & QUIRK_T100CHI ?
				"T100CHI" : "T90CHI");
			memmove(rdesc + offs + 4, rdesc + offs + 2, 12);
			rdesc[offs] = 0x19;
			rdesc[offs + 1] = 0x00;
			rdesc[offs + 2] = 0x29;
			rdesc[offs + 3] = 0xff;
			rdesc[offs + 14] = 0x00;
		}
	}

	if (drvdata->quirks & QUIRK_G752_KEYBOARD &&
		 *rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) {
		/* report is missing usage mninum and maximum */
+1 −1
Original line number Diff line number Diff line
@@ -393,7 +393,7 @@ static int elan_start_multitouch(struct hid_device *hdev)
	 * This byte sequence will enable multitouch mode and disable
	 * mouse emulation
	 */
	const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 };
	static const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 };
	unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);

	if (!dmabuf)
+18 −2
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@

#define USB_VENDOR_ID_ALCOR		0x058f
#define USB_DEVICE_ID_ALCOR_USBRS232	0x9720
#define USB_DEVICE_ID_ALCOR_MALTRON_KB 0x9410

#define USB_VENDOR_ID_ALPS		0x0433
#define USB_DEVICE_ID_IBM_GAMEPAD	0x1101
@@ -273,6 +274,7 @@
#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE	0x1053
#define USB_DEVICE_ID_CHICONY_WIRELESS2	0x1123
#define USB_DEVICE_ID_ASUS_AK1D		0x1125
#define USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A	0x1408
#define USB_DEVICE_ID_CHICONY_ACER_SWITCH12	0x1421

#define USB_VENDOR_ID_CHUNGHWAT		0x2247
@@ -661,6 +663,7 @@
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2	0x501a
#define USB_DEVICE_ID_KYE_EASYPEN_M610X	0x5013
#define USB_DEVICE_ID_KYE_PENSKETCH_M912	0x5015
#define USB_DEVICE_ID_KYE_EASYPEN_M406XE	0x5019

#define USB_VENDOR_ID_LABTEC		0x1020
#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD	0x0006
@@ -714,6 +717,7 @@
#define USB_DEVICE_ID_LENOVO_TPPRODOCK	0x6067
#define USB_DEVICE_ID_LENOVO_X1_COVER	0x6085
#define USB_DEVICE_ID_LENOVO_X1_TAB	0x60a3
#define USB_DEVICE_ID_LENOVO_X1_TAB3	0x60b5

#define USB_VENDOR_ID_LG		0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH	0x0064
@@ -744,6 +748,7 @@
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D	0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO	0xc286
#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940	0xc287
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FG	0xc20e
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG	0xc293
#define USB_DEVICE_ID_LOGITECH_WHEEL	0xc294
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL	0xc295
@@ -1134,11 +1139,16 @@
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850	0x0522
#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60	0x0781
#define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3	0x3031
#define USB_DEVICE_ID_UGEE_TABLET_81		0x0081
#define USB_DEVICE_ID_UGEE_TABLET_45		0x0045
#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81	0x0081
#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45	0x0045
#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47	0x0047
#define USB_DEVICE_ID_YIYNOVA_TABLET		0x004d

#define USB_VENDOR_ID_UGEE		0x28bd
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540	0x0075
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640	0x0094
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01	0x0042
#define USB_DEVICE_ID_UGEE_TABLET_G5		0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S		0x0071

#define USB_VENDOR_ID_UNITEC	0x227d
@@ -1240,4 +1250,10 @@
#define USB_VENDOR_ID_UGTIZER			0x2179
#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610	0x0053

#define USB_VENDOR_ID_VIEWSONIC			0x0543
#define USB_DEVICE_ID_VIEWSONIC_PD1011		0xe621

#define USB_VENDOR_ID_SIGNOTEC			0x2133
#define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011	0x0018

#endif
Loading