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

 - Support for Logitech G15 (Hans de Goede)

 - HID parser improvements, improving support for some devices; e.g.
   Windows Precision Touchpad, products from Primax, etc. (Blaž
   Hrastnik, Candle Sun)

 - robustification of tablet mode support in google-whiskers driver
   (Dmitry Torokhov)

 - assorted small fixes, device-specific quirks and device ID additions

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (23 commits)
  HID: rmi: Check that the RMI_STARTED bit is set before unregistering the RMI transport device
  HID: quirks: remove hid-led devices from hid_have_special_driver
  HID: Improve Windows Precision Touchpad detection.
  HID: i2c-hid: Reset ALPS touchpads on resume
  HID: i2c-hid: fix no irq after reset on raydium 3118
  HID: logitech-hidpp: Silence intermittent get_battery_capacity errors
  HID: i2c-hid: remove orphaned member sleep_delay
  HID: quirks: Add quirk for HP MSU1465 PIXART OEM mouse
  HID: core: check whether Usage Page item is after Usage ID items
  HID: intel-ish-hid: Spelling s/diconnect/disconnect/
  HID: google: Detect base folded usage instead of hard-coding whiskers
  HID: logitech: Add depends on LEDS_CLASS to Logitech Kconfig entry
  HID: lg-g15: Add support for the G510's M1-M3 and MR LEDs
  HID: lg-g15: Add support for controlling the G510's RGB backlight
  HID: lg-g15: Add support for the G510 keyboards' gaming keys
  HID: lg-g15: Add support for the M1-M3 and MR LEDs
  HID: lg-g15: Add keyboard and LCD backlight control
  HID: Add driver for Logitech gaming keyboards (G15, G15 v2)
  Input: Add event-codes for macro keys found on various keyboards
  HID: hidraw: replace printk() with corresponding pr_xx() variant
  ...
parents 4a08fe57 d8d04708
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -9709,6 +9709,13 @@ S: Maintained
F:	Documentation/admin-guide/ldm.rst
F:	block/partitions/ldm.*
LOGITECH HID GAMING KEYBOARDS
M:	Hans de Goede <hdegoede@redhat.com>
L:	linux-input@vger.kernel.org
T:	git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
S:	Maintained
F:	drivers/hid/hid-lg-g15.c
LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI)
M:	Sathya Prakash <sathya.prakash@broadcom.com>
M:	Chaitra P B <chaitra.basappa@broadcom.com>
+1 −0
Original line number Diff line number Diff line
@@ -525,6 +525,7 @@ config HID_LENOVO
config HID_LOGITECH
	tristate "Logitech devices"
	depends on HID
	depends on LEDS_CLASS
	default !EXPERT
	---help---
	Support for Logitech devices that are not fully compliant with HID standard.
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ obj-$(CONFIG_HID_KYE) += hid-kye.o
obj-$(CONFIG_HID_LCPOWER)	+= hid-lcpower.o
obj-$(CONFIG_HID_LENOVO)	+= hid-lenovo.o
obj-$(CONFIG_HID_LOGITECH)	+= hid-logitech.o
obj-$(CONFIG_HID_LOGITECH)	+= hid-lg-g15.o
obj-$(CONFIG_HID_LOGITECH_DJ)	+= hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP)	+= hid-logitech-hidpp.o
obj-$(CONFIG_HID_MACALLY)	+= hid-macally.o
+49 −6
Original line number Diff line number Diff line
@@ -211,6 +211,18 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type)
	return 0; /* we know nothing about this usage type */
}

/*
 * Concatenate usage which defines 16 bits or less with the
 * currently defined usage page to form a 32 bit usage
 */

static void complete_usage(struct hid_parser *parser, unsigned int index)
{
	parser->local.usage[index] &= 0xFFFF;
	parser->local.usage[index] |=
		(parser->global.usage_page & 0xFFFF) << 16;
}

/*
 * Add a usage to the temporary parser table.
 */
@@ -222,6 +234,14 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size)
		return -1;
	}
	parser->local.usage[parser->local.usage_index] = usage;

	/*
	 * If Usage item only includes usage id, concatenate it with
	 * currently defined usage page
	 */
	if (size <= 2)
		complete_usage(parser, parser->local.usage_index);

	parser->local.usage_size[parser->local.usage_index] = size;
	parser->local.collection_index[parser->local.usage_index] =
		parser->collection_stack_ptr ?
@@ -543,13 +563,32 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item)
 * usage value."
 */

static void hid_concatenate_usage_page(struct hid_parser *parser)
static void hid_concatenate_last_usage_page(struct hid_parser *parser)
{
	int i;
	unsigned int usage_page;
	unsigned int current_page;

	for (i = 0; i < parser->local.usage_index; i++)
		if (parser->local.usage_size[i] <= 2)
			parser->local.usage[i] += parser->global.usage_page << 16;
	if (!parser->local.usage_index)
		return;

	usage_page = parser->global.usage_page;

	/*
	 * Concatenate usage page again only if last declared Usage Page
	 * has not been already used in previous usages concatenation
	 */
	for (i = parser->local.usage_index - 1; i >= 0; i--) {
		if (parser->local.usage_size[i] > 2)
			/* Ignore extended usages */
			continue;

		current_page = parser->local.usage[i] >> 16;
		if (current_page == usage_page)
			break;

		complete_usage(parser, i);
	}
}

/*
@@ -561,7 +600,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item)
	__u32 data;
	int ret;

	hid_concatenate_usage_page(parser);
	hid_concatenate_last_usage_page(parser);

	data = item_udata(item);

@@ -742,6 +781,10 @@ static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage)
	if (usage == 0xff0000c5 && parser->global.report_count == 256 &&
	    parser->global.report_size == 8)
		parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8;

	if (usage == 0xff0000c6 && parser->global.report_count == 1 &&
	    parser->global.report_size == 8)
		parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8;
}

static void hid_scan_collection(struct hid_parser *parser, unsigned type)
@@ -772,7 +815,7 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
	__u32 data;
	int i;

	hid_concatenate_usage_page(parser);
	hid_concatenate_last_usage_page(parser);

	data = item_udata(item);

+103 −43
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ struct cbas_ec {
	struct device *dev;	/* The platform device (EC) */
	struct input_dev *input;
	bool base_present;
	bool base_folded;
	struct notifier_block notifier;
};

@@ -208,7 +209,14 @@ static int __cbas_ec_probe(struct platform_device *pdev)
		return error;
	}

	input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present);
	if (!cbas_ec.base_present)
		cbas_ec.base_folded = false;

	dev_dbg(&pdev->dev, "%s: base: %d, folded: %d\n", __func__,
		cbas_ec.base_present, cbas_ec.base_folded);

	input_report_switch(input, SW_TABLET_MODE,
			    !cbas_ec.base_present || cbas_ec.base_folded);

	cbas_ec_set_input(input);

@@ -322,10 +330,9 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev,
static int hammer_register_leds(struct hid_device *hdev)
{
	struct hammer_kbd_leds *kbd_backlight;
	int error;

	kbd_backlight = devm_kzalloc(&hdev->dev,
				     sizeof(*kbd_backlight),
				     GFP_KERNEL);
	kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL);
	if (!kbd_backlight)
		return -ENOMEM;

@@ -339,12 +346,31 @@ static int hammer_register_leds(struct hid_device *hdev)
	/* Set backlight to 0% initially. */
	hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0);

	return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
	error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev);
	if (error)
		goto err_free_mem;

	hid_set_drvdata(hdev, kbd_backlight);
	return 0;

err_free_mem:
	kfree(kbd_backlight);
	return error;
}

static void hammer_unregister_leds(struct hid_device *hdev)
{
	struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev);

	if (kbd_backlight) {
		led_classdev_unregister(&kbd_backlight->cdev);
		kfree(kbd_backlight);
	}
}

#define HID_UP_GOOGLEVENDOR	0xffd10000
#define HID_VD_KBD_FOLDED	0x00000019
#define WHISKERS_KBD_FOLDED	(HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)
#define HID_USAGE_KBD_FOLDED	(HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED)

/* HID usage for keyboard backlight (Alphanumeric display brightness) */
#define HID_AD_BRIGHTNESS	0x00140046
@@ -354,8 +380,7 @@ static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi,
				struct hid_usage *usage,
				unsigned long **bit, int *max)
{
	if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
	    usage->hid == WHISKERS_KBD_FOLDED) {
	if (usage->hid == HID_USAGE_KBD_FOLDED) {
		/*
		 * We do not want to have this usage mapped as it will get
		 * mixed in with "base attached" signal and delivered over
@@ -372,19 +397,19 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field,
{
	unsigned long flags;

	if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
	    usage->hid == WHISKERS_KBD_FOLDED) {
	if (usage->hid == HID_USAGE_KBD_FOLDED) {
		spin_lock_irqsave(&cbas_ec_lock, flags);

		hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
			cbas_ec.base_present, value);

		/*
		 * We should not get event if base is detached, but in case
		 * we happen to service HID and EC notifications out of order
		 * let's still check the "base present" flag.
		 * If we are getting events from Whiskers that means that it
		 * is attached to the lid.
		 */
		if (cbas_ec.input && cbas_ec.base_present) {
		cbas_ec.base_present = true;
		cbas_ec.base_folded = value;
		hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__,
			cbas_ec.base_present, cbas_ec.base_folded);

		if (cbas_ec.input) {
			input_report_switch(cbas_ec.input,
					    SW_TABLET_MODE, value);
			input_sync(cbas_ec.input);
@@ -397,33 +422,22 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field,
	return 0;
}

static bool hammer_is_keyboard_interface(struct hid_device *hdev)
{
	struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT];
	struct hid_report *report;

	list_for_each_entry(report, &re->report_list, list)
		if (report->application == HID_GD_KEYBOARD)
			return true;

	return false;
}

static bool hammer_has_backlight_control(struct hid_device *hdev)
static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type,
			unsigned application, unsigned usage)
{
	struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT];
	struct hid_report_enum *re = &hdev->report_enum[report_type];
	struct hid_report *report;
	int i, j;

	list_for_each_entry(report, &re->report_list, list) {
		if (report->application != HID_GD_KEYBOARD)
		if (report->application != application)
			continue;

		for (i = 0; i < report->maxfield; i++) {
			struct hid_field *field = report->field[i];

			for (j = 0; j < field->maxusage; j++)
				if (field->usage[j].hid == HID_AD_BRIGHTNESS)
				if (field->usage[j].hid == usage)
					return true;
		}
	}
@@ -431,21 +445,23 @@ static bool hammer_has_backlight_control(struct hid_device *hdev)
	return false;
}

static bool hammer_has_folded_event(struct hid_device *hdev)
{
	return hammer_has_usage(hdev, HID_INPUT_REPORT,
				HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED);
}

static bool hammer_has_backlight_control(struct hid_device *hdev)
{
	return hammer_has_usage(hdev, HID_OUTPUT_REPORT,
				HID_GD_KEYBOARD, HID_AD_BRIGHTNESS);
}

static int hammer_probe(struct hid_device *hdev,
			const struct hid_device_id *id)
{
	int error;

	/*
	 * We always want to poll for, and handle tablet mode events from
	 * Whiskers, even when nobody has opened the input device. This also
	 * prevents the hid core from dropping early tablet mode events from
	 * the device.
	 */
	if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS &&
			hammer_is_keyboard_interface(hdev))
		hdev->quirks |= HID_QUIRK_ALWAYS_POLL;

	error = hid_parse(hdev);
	if (error)
		return error;
@@ -454,6 +470,19 @@ static int hammer_probe(struct hid_device *hdev,
	if (error)
		return error;

	/*
	 * We always want to poll for, and handle tablet mode events from
	 * devices that have folded usage, even when nobody has opened the input
	 * device. This also prevents the hid core from dropping early tablet
	 * mode events from the device.
	 */
	if (hammer_has_folded_event(hdev)) {
		hdev->quirks |= HID_QUIRK_ALWAYS_POLL;
		error = hid_hw_open(hdev);
		if (error)
			return error;
	}

	if (hammer_has_backlight_control(hdev)) {
		error = hammer_register_leds(hdev);
		if (error)
@@ -465,6 +494,36 @@ static int hammer_probe(struct hid_device *hdev,
	return 0;
}

static void hammer_remove(struct hid_device *hdev)
{
	unsigned long flags;

	if (hammer_has_folded_event(hdev)) {
		hid_hw_close(hdev);

		/*
		 * If we are disconnecting then most likely Whiskers is
		 * being removed. Even if it is not removed, without proper
		 * keyboard we should not stay in clamshell mode.
		 *
		 * The reason for doing it here and not waiting for signal
		 * from EC, is that on some devices there are high leakage
		 * on Whiskers pins and we do not detect disconnect reliably,
		 * resulting in devices being stuck in clamshell mode.
		 */
		spin_lock_irqsave(&cbas_ec_lock, flags);
		if (cbas_ec.input && cbas_ec.base_present) {
			input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1);
			input_sync(cbas_ec.input);
		}
		cbas_ec.base_present = false;
		spin_unlock_irqrestore(&cbas_ec_lock, flags);
	}

	hammer_unregister_leds(hdev);

	hid_hw_stop(hdev);
}

static const struct hid_device_id hammer_devices[] = {
	{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
@@ -487,6 +546,7 @@ static struct hid_driver hammer_driver = {
	.name = "hammer",
	.id_table = hammer_devices,
	.probe = hammer_probe,
	.remove = hammer_remove,
	.input_mapping = hammer_input_mapping,
	.event = hammer_event,
};
Loading