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

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

 - Wacom driver fixes/updates (device name generation improvements,
   touch ring status support) from Jason Gerecke

 - T100 touchpad support from Hans de Goede

 - support for batteries driven by HID input reports, from Dmitry
   Torokhov

 - Arnd pointed out that driver_lock semaphore is superfluous, as driver
   core already provides all the necessary concurency protection.
   Removal patch from Binoy Jayan

 - logical minimum numbering improvements in sensor-hub driver, from
   Srinivas Pandruvada

 - support for Microsoft Win8 Wireless Radio Controls extensions from
   João Paulo Rechi Vita

 - assorted small fixes and device ID additions

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (28 commits)
  HID: prodikeys: constify snd_rawmidi_ops structures
  HID: sensor: constify platform_device_id
  HID: input: throttle battery uevents
  HID: usbmouse: constify usb_device_id and fix space before '[' error
  HID: usbkbd: constify usb_device_id and fix space before '[' error.
  HID: hid-sensor-hub: Force logical minimum to 1 for power and report state
  HID: wacom: Do not completely map WACOM_HID_WD_TOUCHRINGSTATUS usage
  HID: asus: Add T100CHI bluetooth keyboard dock touchpad support
  HID: ntrig: constify attribute_group structures.
  HID: logitech-hidpp: constify attribute_group structures.
  HID: sensor: constify attribute_group structures.
  HID: multitouch: constify attribute_group structures.
  HID: multitouch: use proper symbolic constant for 0xff310076 application
  HID: multitouch: Support Asus T304UA media keys
  HID: multitouch: Support HID_GD_WIRELESS_RADIO_CTLS
  HID: input: optionally use device id in battery name
  HID: input: map digitizer battery usage
  HID: Remove the semaphore driver_lock
  HID: wacom: add USB_HID dependency
  HID: add ALWAYS_POLL quirk for Logitech 0xc077
  ...
parents 70b8e9eb de6c5070
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -924,7 +924,7 @@ config HID_UDRAW_PS3

config HID_WACOM
	tristate "Wacom Intuos/Graphire tablet support (USB)"
	depends on HID
	depends on USB_HID
	select POWER_SUPPLY
	select NEW_LEDS
	select LEDS_CLASS
+173 −45
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/input/mt.h>
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */

#include "hid-ids.h"

@@ -38,24 +39,19 @@ MODULE_AUTHOR("Victor Vlasenko <victor.vlasenko@sysgears.com>");
MODULE_AUTHOR("Frederik Wenigwieser <frederik.wenigwieser@gmail.com>");
MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");

#define T100_TPAD_INTF 2

#define T100CHI_MOUSE_REPORT_ID 0x06
#define FEATURE_REPORT_ID 0x0d
#define INPUT_REPORT_ID 0x5d
#define FEATURE_KBD_REPORT_ID 0x5a

#define INPUT_REPORT_SIZE 28
#define FEATURE_KBD_REPORT_SIZE 16

#define SUPPORT_KBD_BACKLIGHT BIT(0)

#define MAX_CONTACTS 5

#define MAX_X 2794
#define MAX_Y 1758
#define MAX_TOUCH_MAJOR 8
#define MAX_PRESSURE 128

#define CONTACT_DATA_SIZE 5

#define BTN_LEFT_MASK 0x01
#define CONTACT_TOOL_TYPE_MASK 0x80
#define CONTACT_X_MSB_MASK 0xf0
@@ -70,6 +66,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_NO_CONSUMER_USAGES	BIT(4)
#define QUIRK_USE_KBD_BACKLIGHT		BIT(5)
#define QUIRK_T100_KEYBOARD		BIT(6)
#define QUIRK_T100CHI			BIT(7)

#define I2C_KEYBOARD_QUIRKS			(QUIRK_FIX_NOTEBOOK_REPORT | \
						 QUIRK_NO_INIT_REPORTS | \
@@ -88,19 +85,62 @@ struct asus_kbd_leds {
	bool removed;
};

struct asus_touchpad_info {
	int max_x;
	int max_y;
	int res_x;
	int res_y;
	int contact_size;
	int max_contacts;
};

struct asus_drvdata {
	unsigned long quirks;
	struct input_dev *input;
	struct asus_kbd_leds *kbd_backlight;
	const struct asus_touchpad_info *tp;
	bool enable_backlight;
};

static void asus_report_contact_down(struct input_dev *input,
static const struct asus_touchpad_info asus_i2c_tp = {
	.max_x = 2794,
	.max_y = 1758,
	.contact_size = 5,
	.max_contacts = 5,
};

static const struct asus_touchpad_info asus_t100ta_tp = {
	.max_x = 2240,
	.max_y = 1120,
	.res_x = 30, /* units/mm */
	.res_y = 27, /* units/mm */
	.contact_size = 5,
	.max_contacts = 5,
};

static const struct asus_touchpad_info asus_t100chi_tp = {
	.max_x = 2640,
	.max_y = 1320,
	.res_x = 31, /* units/mm */
	.res_y = 29, /* units/mm */
	.contact_size = 3,
	.max_contacts = 4,
};

static void asus_report_contact_down(struct asus_drvdata *drvdat,
		int toolType, u8 *data)
{
	int touch_major, pressure;
	int x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1];
	int y = MAX_Y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]);
	struct input_dev *input = drvdat->input;
	int touch_major, pressure, x, y;

	x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1];
	y = drvdat->tp->max_y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]);

	input_report_abs(input, ABS_MT_POSITION_X, x);
	input_report_abs(input, ABS_MT_POSITION_Y, y);

	if (drvdat->tp->contact_size < 5)
		return;

	if (toolType == MT_TOOL_PALM) {
		touch_major = MAX_TOUCH_MAJOR;
@@ -110,19 +150,20 @@ static void asus_report_contact_down(struct input_dev *input,
		pressure = data[4] & CONTACT_PRESSURE_MASK;
	}

	input_report_abs(input, ABS_MT_POSITION_X, x);
	input_report_abs(input, ABS_MT_POSITION_Y, y);
	input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major);
	input_report_abs(input, ABS_MT_PRESSURE, pressure);
}

/* Required for Synaptics Palm Detection */
static void asus_report_tool_width(struct input_dev *input)
static void asus_report_tool_width(struct asus_drvdata *drvdat)
{
	struct input_mt *mt = input->mt;
	struct input_mt *mt = drvdat->input->mt;
	struct input_mt_slot *oldest;
	int oldid, count, i;

	if (drvdat->tp->contact_size < 5)
		return;

	oldest = NULL;
	oldid = mt->trkid;
	count = 0;
@@ -141,35 +182,42 @@ static void asus_report_tool_width(struct input_dev *input)
	}

	if (oldest) {
		input_report_abs(input, ABS_TOOL_WIDTH,
		input_report_abs(drvdat->input, ABS_TOOL_WIDTH,
			input_mt_get_value(oldest, ABS_MT_TOUCH_MAJOR));
	}
}

static void asus_report_input(struct input_dev *input, u8 *data)
static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size)
{
	int i;
	int i, toolType = MT_TOOL_FINGER;
	u8 *contactData = data + 2;

	for (i = 0; i < MAX_CONTACTS; i++) {
	if (size != 3 + drvdat->tp->contact_size * drvdat->tp->max_contacts)
		return 0;

	for (i = 0; i < drvdat->tp->max_contacts; i++) {
		bool down = !!(data[1] & BIT(i+3));
		int toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ?

		if (drvdat->tp->contact_size >= 5)
			toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ?
						MT_TOOL_PALM : MT_TOOL_FINGER;

		input_mt_slot(input, i);
		input_mt_report_slot_state(input, toolType, down);
		input_mt_slot(drvdat->input, i);
		input_mt_report_slot_state(drvdat->input, toolType, down);

		if (down) {
			asus_report_contact_down(input, toolType, contactData);
			contactData += CONTACT_DATA_SIZE;
			asus_report_contact_down(drvdat, toolType, contactData);
			contactData += drvdat->tp->contact_size;
		}
	}

	input_report_key(input, BTN_LEFT, data[1] & BTN_LEFT_MASK);
	asus_report_tool_width(input);
	input_report_key(drvdat->input, BTN_LEFT, data[1] & BTN_LEFT_MASK);
	asus_report_tool_width(drvdat);

	input_mt_sync_frame(drvdat->input);
	input_sync(drvdat->input);

	input_mt_sync_frame(input);
	input_sync(input);
	return 1;
}

static int asus_raw_event(struct hid_device *hdev,
@@ -177,12 +225,8 @@ static int asus_raw_event(struct hid_device *hdev,
{
	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);

	if (drvdata->quirks & QUIRK_IS_MULTITOUCH &&
					 data[0] == INPUT_REPORT_ID &&
						size == INPUT_REPORT_SIZE) {
		asus_report_input(drvdata->input, data);
		return 1;
	}
	if (drvdata->tp && data[0] == INPUT_REPORT_ID)
		return asus_report_input(drvdata, data, size);

	return 0;
}
@@ -334,19 +378,35 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
	struct input_dev *input = hi->input;
	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);

	if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
	/* T100CHI uses MULTI_INPUT, bind the touchpad to the mouse hid_input */
	if (drvdata->quirks & QUIRK_T100CHI &&
	    hi->report->id != T100CHI_MOUSE_REPORT_ID)
		return 0;

	if (drvdata->tp) {
		int ret;

		input_set_abs_params(input, ABS_MT_POSITION_X, 0, MAX_X, 0, 0);
		input_set_abs_params(input, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0);
		input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MAX_TOUCH_MAJOR, 0, 0);
		input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0);
		input_set_abs_params(input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
		input_set_abs_params(input, ABS_MT_POSITION_X, 0,
				     drvdata->tp->max_x, 0, 0);
		input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
				     drvdata->tp->max_y, 0, 0);
		input_abs_set_res(input, ABS_MT_POSITION_X, drvdata->tp->res_x);
		input_abs_set_res(input, ABS_MT_POSITION_Y, drvdata->tp->res_y);

		if (drvdata->tp->contact_size >= 5) {
			input_set_abs_params(input, ABS_TOOL_WIDTH, 0,
					     MAX_TOUCH_MAJOR, 0, 0);
			input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
					     MAX_TOUCH_MAJOR, 0, 0);
			input_set_abs_params(input, ABS_MT_PRESSURE, 0,
					      MAX_PRESSURE, 0, 0);
		}

		__set_bit(BTN_LEFT, input->keybit);
		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);

		ret = input_mt_init_slots(input, MAX_CONTACTS, INPUT_MT_POINTER);
		ret = input_mt_init_slots(input, drvdata->tp->max_contacts,
					  INPUT_MT_POINTER);

		if (ret) {
			hid_err(hdev, "Asus input mt init slots failed: %d\n", ret);
@@ -378,6 +438,26 @@ static int asus_input_mapping(struct hid_device *hdev,
		return -1;
	}

	/*
	 * Ignore a bunch of bogus collections in the T100CHI descriptor.
	 * 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 (field->application == (HID_UP_GENDESK | 0x0080) ||
		    usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) ||
		    usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) ||
		    usage->hid == (HID_UP_GENDEVCTRLS | 0x0026))
			return -1;
		/*
		 * We use the hid_input for the mouse report for the touchpad,
		 * keep the left button, to avoid the core removing it.
		 */
		if (field->application == HID_GD_MOUSE &&
		    usage->hid != (HID_UP_BUTTON | 1))
			return -1;
	}

	/* ASUS-specific keyboard hotkeys */
	if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) {
		set_bit(EV_REP, hi->input->evbit);
@@ -496,7 +576,7 @@ static int __maybe_unused asus_reset_resume(struct hid_device *hdev)
{
	struct asus_drvdata *drvdata = hid_get_drvdata(hdev);

	if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
	if (drvdata->tp)
		return asus_start_multitouch(hdev);

	return 0;
@@ -517,6 +597,28 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)

	drvdata->quirks = id->driver_data;

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

	if (drvdata->quirks & QUIRK_T100_KEYBOARD) {
		struct usb_interface *intf = to_usb_interface(hdev->dev.parent);

		if (intf->altsetting->desc.bInterfaceNumber == T100_TPAD_INTF) {
			drvdata->quirks = QUIRK_SKIP_INPUT_MAPPING;
			drvdata->tp = &asus_t100ta_tp;
		}
	}

	if (drvdata->quirks & QUIRK_T100CHI) {
		/*
		 * All functionality is on a single HID interface and for
		 * userspace the touchpad must be a separate input_dev.
		 */
		hdev->quirks |= HID_QUIRK_MULTI_INPUT |
				HID_QUIRK_NO_EMPTY_INPUT;
		drvdata->tp = &asus_t100chi_tp;
	}

	if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
		hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;

@@ -538,13 +640,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
		goto err_stop_hw;
	}

	if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
	if (drvdata->tp) {
		drvdata->input->name = "Asus TouchPad";
	} else {
		drvdata->input->name = "Asus Keyboard";
	}

	if (drvdata->quirks & QUIRK_IS_MULTITOUCH) {
	if (drvdata->tp) {
		ret = asus_start_multitouch(hdev);
		if (ret)
			goto err_stop_hw;
@@ -578,11 +680,34 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
		hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
		rdesc[55] = 0xdd;
	}
	/* For the T100TA keyboard dock */
	if (drvdata->quirks & QUIRK_T100_KEYBOARD &&
		 *rsize == 76 && rdesc[73] == 0x81 && rdesc[74] == 0x01) {
		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) {
		/*
		 * 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;
		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;
	}

	return rdesc;
}
@@ -602,6 +727,9 @@ static const struct hid_device_id asus_devices[] = {
	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) },
	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK,
		USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), QUIRK_T100CHI },

	{ }
};
MODULE_DEVICE_TABLE(hid, asus_devices);
+5 −11
Original line number Diff line number Diff line
@@ -1982,6 +1982,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) },
	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD) },
#endif
#if IS_ENABLED(CONFIG_HID_AUREAL)
	{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
@@ -2487,11 +2488,9 @@ static int hid_device_probe(struct device *dev)
	const struct hid_device_id *id;
	int ret = 0;

	if (down_interruptible(&hdev->driver_lock))
		return -EINTR;
	if (down_interruptible(&hdev->driver_input_lock)) {
		ret = -EINTR;
		goto unlock_driver_lock;
		goto end;
	}
	hdev->io_started = false;

@@ -2518,8 +2517,7 @@ static int hid_device_probe(struct device *dev)
unlock:
	if (!hdev->io_started)
		up(&hdev->driver_input_lock);
unlock_driver_lock:
	up(&hdev->driver_lock);
end:
	return ret;
}

@@ -2529,11 +2527,9 @@ static int hid_device_remove(struct device *dev)
	struct hid_driver *hdrv;
	int ret = 0;

	if (down_interruptible(&hdev->driver_lock))
		return -EINTR;
	if (down_interruptible(&hdev->driver_input_lock)) {
		ret = -EINTR;
		goto unlock_driver_lock;
		goto end;
	}
	hdev->io_started = false;

@@ -2549,8 +2545,7 @@ static int hid_device_remove(struct device *dev)

	if (!hdev->io_started)
		up(&hdev->driver_input_lock);
unlock_driver_lock:
	up(&hdev->driver_lock);
end:
	return ret;
}

@@ -3008,7 +3003,6 @@ struct hid_device *hid_allocate_device(void)
	init_waitqueue_head(&hdev->debug_wait);
	INIT_LIST_HEAD(&hdev->debug_list);
	spin_lock_init(&hdev->debug_list_lock);
	sema_init(&hdev->driver_lock, 1);
	sema_init(&hdev->driver_input_lock, 1);
	mutex_init(&hdev->ll_open_lock);

+4 −1
Original line number Diff line number Diff line
@@ -176,6 +176,8 @@
#define USB_DEVICE_ID_ASUSTEK_LCM	0x1726
#define USB_DEVICE_ID_ASUSTEK_LCM2	0x175b
#define USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD	0x17e0
#define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD	0x8502
#define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD	0x184a
#define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD	0x8585
#define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD	0x0101
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
@@ -666,7 +668,8 @@
#define USB_VENDOR_ID_LOGITECH		0x046d
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
#define USB_DEVICE_ID_LOGITECH_T651	0xb00c
#define USB_DEVICE_ID_LOGITECH_C077	0xc007
#define USB_DEVICE_ID_LOGITECH_C007	0xc007
#define USB_DEVICE_ID_LOGITECH_C077	0xc077
#define USB_DEVICE_ID_LOGITECH_RECEIVER	0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+135 −61
Original line number Diff line number Diff line
@@ -340,13 +340,45 @@ static unsigned find_battery_quirk(struct hid_device *hdev)
	return quirks;
}

static int hidinput_scale_battery_capacity(struct hid_device *dev,
					   int value)
{
	if (dev->battery_min < dev->battery_max &&
	    value >= dev->battery_min && value <= dev->battery_max)
		value = ((value - dev->battery_min) * 100) /
			(dev->battery_max - dev->battery_min);

	return value;
}

static int hidinput_query_battery_capacity(struct hid_device *dev)
{
	u8 *buf;
	int ret;

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

	ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
				 dev->battery_report_type, HID_REQ_GET_REPORT);
	if (ret != 2) {
		kfree(buf);
		return -ENODATA;
	}

	ret = hidinput_scale_battery_capacity(dev, buf[1]);
	kfree(buf);
	return ret;
}

static int hidinput_get_battery_property(struct power_supply *psy,
					 enum power_supply_property prop,
					 union power_supply_propval *val)
{
	struct hid_device *dev = power_supply_get_drvdata(psy);
	int value;
	int ret = 0;
	__u8 *buf;

	switch (prop) {
	case POWER_SUPPLY_PROP_PRESENT:
@@ -355,29 +387,15 @@ static int hidinput_get_battery_property(struct power_supply *psy,
		break;

	case POWER_SUPPLY_PROP_CAPACITY:

		buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL);
		if (!buf) {
			ret = -ENOMEM;
			break;
		}
		ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
					 dev->battery_report_type,
					 HID_REQ_GET_REPORT);

		if (ret != 2) {
			ret = -ENODATA;
			kfree(buf);
			break;
		if (dev->battery_report_type == HID_FEATURE_REPORT) {
			value = hidinput_query_battery_capacity(dev);
			if (value < 0)
				return value;
		} else  {
			value = dev->battery_capacity;
		}
		ret = 0;

		if (dev->battery_min < dev->battery_max &&
		    buf[1] >= dev->battery_min &&
		    buf[1] <= dev->battery_max)
			val->intval = (100 * (buf[1] - dev->battery_min)) /
				(dev->battery_max - dev->battery_min);
		kfree(buf);
		val->intval = value;
		break;

	case POWER_SUPPLY_PROP_MODEL_NAME:
@@ -385,6 +403,21 @@ static int hidinput_get_battery_property(struct power_supply *psy,
		break;

	case POWER_SUPPLY_PROP_STATUS:
		if (!dev->battery_reported &&
		    dev->battery_report_type == HID_FEATURE_REPORT) {
			value = hidinput_query_battery_capacity(dev);
			if (value < 0)
				return value;

			dev->battery_capacity = value;
			dev->battery_reported = true;
		}

		if (!dev->battery_reported)
			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
		else if (dev->battery_capacity == 100)
			val->intval = POWER_SUPPLY_STATUS_FULL;
		else
			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
		break;

@@ -400,18 +433,16 @@ static int hidinput_get_battery_property(struct power_supply *psy,
	return ret;
}

static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
{
	struct power_supply_desc *psy_desc = NULL;
	struct power_supply_desc *psy_desc;
	struct power_supply_config psy_cfg = { .drv_data = dev, };
	unsigned quirks;
	s32 min, max;
	int error;

	if (field->usage->hid != HID_DC_BATTERYSTRENGTH)
		return false;	/* no match */

	if (dev->battery != NULL)
		goto out;	/* already initialized? */
	if (dev->battery)
		return 0;	/* already initialized? */

	quirks = find_battery_quirk(dev);

@@ -419,16 +450,18 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
		dev->bus, dev->vendor, dev->product, dev->version, quirks);

	if (quirks & HID_BATTERY_QUIRK_IGNORE)
		goto out;
		return 0;

	psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
	if (psy_desc == NULL)
		goto out;
	if (!psy_desc)
		return -ENOMEM;

	psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
	if (psy_desc->name == NULL) {
		kfree(psy_desc);
		goto out;
	psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery",
				   strlen(dev->uniq) ?
					dev->uniq : dev_name(&dev->dev));
	if (!psy_desc->name) {
		error = -ENOMEM;
		goto err_free_mem;
	}

	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
@@ -455,17 +488,20 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,

	dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
	if (IS_ERR(dev->battery)) {
		hid_warn(dev, "can't register power supply: %ld\n",
				PTR_ERR(dev->battery));
		error = PTR_ERR(dev->battery);
		hid_warn(dev, "can't register power supply: %d\n", error);
		goto err_free_name;
	}

	power_supply_powers(dev->battery, &dev->dev);
	return 0;

err_free_name:
	kfree(psy_desc->name);
err_free_mem:
	kfree(psy_desc);
	dev->battery = NULL;
	} else {
		power_supply_powers(dev->battery, &dev->dev);
	}

out:
	return true;
	return error;
}

static void hidinput_cleanup_battery(struct hid_device *dev)
@@ -481,16 +517,39 @@ static void hidinput_cleanup_battery(struct hid_device *dev)
	kfree(psy_desc);
	dev->battery = NULL;
}

static void hidinput_update_battery(struct hid_device *dev, int value)
{
	int capacity;

	if (!dev->battery)
		return;

	if (value == 0 || value < dev->battery_min || value > dev->battery_max)
		return;

	capacity = hidinput_scale_battery_capacity(dev, value);

	if (!dev->battery_reported || capacity != dev->battery_capacity) {
		dev->battery_capacity = capacity;
		dev->battery_reported = true;
		power_supply_changed(dev->battery);
	}
}
#else  /* !CONFIG_HID_BATTERY_STRENGTH */
static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
				  struct hid_field *field)
{
	return false;
	return 0;
}

static void hidinput_cleanup_battery(struct hid_device *dev)
{
}

static void hidinput_update_battery(struct hid_device *dev, int value)
{
}
#endif	/* CONFIG_HID_BATTERY_STRENGTH */

static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
@@ -710,6 +769,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
			}
			break;

		case 0x3b: /* Battery Strength */
			hidinput_setup_battery(device, HID_INPUT_REPORT, field);
			usage->type = EV_PWR;
			goto ignore;

		case 0x3c: /* Invert */
			map_key_clear(BTN_TOOL_RUBBER);
			break;
@@ -944,11 +1008,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
		break;

	case HID_UP_GENDEVCTRLS:
		if (hidinput_setup_battery(device, HID_INPUT_REPORT, field))
		switch (usage->hid) {
		case HID_DC_BATTERYSTRENGTH:
			hidinput_setup_battery(device, HID_INPUT_REPORT, field);
			usage->type = EV_PWR;
			goto ignore;
		else
		}
		goto unknown;
		break;

	case HID_UP_HPVENDOR:	/* Reported on a Dutch layout HP5308 */
		set_bit(EV_REP, input->evbit);
@@ -1031,7 +1097,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
	if (usage->code > max)
		goto ignore;


	if (usage->type == EV_ABS) {

		int a = field->logical_minimum;
@@ -1090,14 +1155,19 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
	struct input_dev *input;
	unsigned *quirks = &hid->quirks;

	if (!field->hidinput)
	if (!usage->type)
		return;

	input = field->hidinput->input;
	if (usage->type == EV_PWR) {
		hidinput_update_battery(hid, value);
		return;
	}

	if (!usage->type)
	if (!field->hidinput)
		return;

	input = field->hidinput->input;

	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
		int hat_dir = usage->hat_dir;
		if (!hat_dir)
@@ -1373,6 +1443,7 @@ static void report_features(struct hid_device *hid)
	struct hid_driver *drv = hid->driver;
	struct hid_report_enum *rep_enum;
	struct hid_report *rep;
	struct hid_usage *usage;
	int i, j;

	rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
@@ -1383,12 +1454,15 @@ static void report_features(struct hid_device *hid)
				continue;

			for (j = 0; j < rep->field[i]->maxusage; j++) {
				usage = &rep->field[i]->usage[j];

				/* Verify if Battery Strength feature is available */
				hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
				if (usage->hid == HID_DC_BATTERYSTRENGTH)
					hidinput_setup_battery(hid, HID_FEATURE_REPORT,
							       rep->field[i]);

				if (drv->feature_mapping)
					drv->feature_mapping(hid, rep->field[i],
							     rep->field[i]->usage + j);
					drv->feature_mapping(hid, rep->field[i], usage);
			}
		}
}
Loading