Username: Password: Remember me
diff -ur old/drivers/input/mouse/Kconfig new/drivers/input/mouse/Kconfig
--- old/drivers/input/mouse/Kconfig	2017-08-30 11:32:30.000000000 +0300
+++ new/drivers/input/mouse/Kconfig	2017-09-01 08:14:12.460494857 +0300
@@ -100,6 +100,15 @@
 
          If unsure, say Y.
 
+config MOUSE_PS2_SYNAPTICS_LED
+	bool "Support embedded LED on Synaptics devices"
+	default y
+	depends on MOUSE_PS2_SYNAPTICS
+	depends on LEDS_CLASS=y || LEDS_CLASS=MOUSE_PS2
+	help
+	  Say Y here if you have a Synaptics device with an embedded LED.
+	  This will enable LED class driver to control the LED device.
+
 config MOUSE_PS2_LIFEBOOK
 	bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT
 	default y
diff -ur old/drivers/input/mouse/synaptics.c new/drivers/input/mouse/synaptics.c
--- old/drivers/input/mouse/synaptics.c
+++ new/drivers/input/mouse/synaptics.c
@@ -29,6 +29,7 @@
 #include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
+#include <linux/leds.h>
 #include <linux/rmi.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
@@ -716,6 +717,141 @@
 	serio_register_port(serio);
 }
 
+#if 1
+/*
+ * LED handling:
+ * Some Synaptics devices have an embeded LED at the top-left corner.
+ */
+
+struct synaptics_led {
+	struct psmouse *psmouse;
+	struct work_struct work;
+	struct led_classdev cdev;
+};
+
+static void synaptics_set_led(struct psmouse *psmouse, int on)
+{
+	int i;
+	unsigned char cmd = on ? 0x88 : 0x10;
+
+	ps2_begin_command(&psmouse->ps2dev);
+	if (__ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11))
+		goto out;
+	for (i = 6; i >= 0; i -= 2) {
+		unsigned char d = (cmd >> i) & 3;
+		if (__ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES))
+			goto out;
+	}
+	cmd = 0x0a;
+	__ps2_command(&psmouse->ps2dev, &cmd, PSMOUSE_CMD_SETRATE);
+ out:
+	ps2_end_command(&psmouse->ps2dev);
+}
+
+static void synaptics_led_work(struct work_struct *work)
+{
+	struct synaptics_led *led;
+
+	led = container_of(work, struct synaptics_led, work);
+	synaptics_set_led(led->psmouse, led->cdev.brightness);
+}
+
+static void synaptics_led_cdev_brightness_set(struct led_classdev *cdev,
+					      enum led_brightness value)
+{
+	struct synaptics_led *led;
+
+	led = container_of(cdev, struct synaptics_led, cdev);
+	schedule_work(&led->work);
+}
+
+static void synaptics_sync_led(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (priv->led)
+		synaptics_set_led(psmouse, priv->led->cdev.brightness);
+}
+
+static bool synaptics_has_led(struct synaptics_data *priv)
+{
+	printk(KERN_NOTICE "synaptics: led: checking for led\n");
+
+	if (!priv->info.ext_cap_0c) {
+		printk(KERN_NOTICE "synaptics: led: !priv->ext_cap_0c is true... no led\n");
+		return false;
+	}
+
+	printk(KERN_NOTICE "synaptics: led: your product ID is %2lx\n", SYN_CAP_PRODUCT_ID(priv->info.ext_cap));
+
+	/* FIXME: LED is supposedly detectable in cap0c[1] 0x20, but it seems
+	 * not working on real machines.
+	 * So we check the product id to be sure.
+	 * Added ID 0x24 experimental.
+	 */
+
+	if (SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0x24 &&
+       SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0xe4 &&
+	    SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0x64 &&
+	    SYN_CAP_PRODUCT_ID(priv->info.ext_cap) != 0x84) {
+		printk(KERN_NOTICE "synaptics: led: your product ID is not in the whitelist\n");
+		return false;
+	}
+
+	if (!(priv->info.ext_cap_0c & 0x2000) &&
+	    (priv->info.capabilities & 0xd000fd) != 0xd00071) {
+		printk(KERN_NOTICE "synaptics: led: failed capabilities check\n");
+		return false;
+	}
+
+	printk(KERN_NOTICE "synaptics: led: looks like you have one\n");
+
+	return true;
+}
+
+static int synaptics_init_led(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+	struct synaptics_led *led;
+	int err;
+
+	if (!synaptics_has_led(priv))
+		return 0;
+	printk(KERN_INFO "synaptics: support LED control\n");
+	led = kzalloc(sizeof(struct synaptics_led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+	led->psmouse = psmouse;
+	INIT_WORK(&led->work, synaptics_led_work);
+	led->cdev.name = "psmouse::synaptics";
+	led->cdev.brightness_set = synaptics_led_cdev_brightness_set;
+	led->cdev.flags = LED_CORE_SUSPENDRESUME;
+	err = led_classdev_register(NULL, &led->cdev);
+	if (err < 0) {
+		kfree(led);
+		return err;
+	}
+	priv->led = led;
+	return 0;
+}
+
+static void synaptics_free_led(struct psmouse *psmouse)
+{
+	struct synaptics_data *priv = psmouse->private;
+
+	if (!priv->led)
+		return;
+	cancel_work_sync(&priv->led->work);
+	synaptics_set_led(psmouse, 0);
+	led_classdev_unregister(&priv->led->cdev);
+	kfree(priv->led);
+}
+#else
+#define synaptics_init_led(ps)	0
+#define synaptics_free_led(ps)	do {} while (0)
+#define synaptics_sync_led(ps)	do {} while (0)
+#endif
+
 /*****************************************************************************
  *	Functions to interpret the absolute mode packets
  ****************************************************************************/
@@ -1400,6 +1536,7 @@
 		device_remove_file(&psmouse->ps2dev.serio->dev,
 				   &psmouse_attr_disable_gesture.dattr);
 
+	synaptics_free_led(psmouse);
 	synaptics_reset(psmouse);
 	kfree(priv);
 	psmouse->private = NULL;
@@ -1459,6 +1596,8 @@
 			    priv->info.ext_cap, info.ext_cap);
 		return -ENXIO;
 	}
+
+	synaptics_sync_led(psmouse);
 
 	return 0;
 }
@@ -1578,6 +1717,9 @@
 		     info->capabilities, info->ext_cap, info->ext_cap_0c,
 		     info->ext_cap_10, info->board_id, info->firmware_id);
 
+	if (synaptics_init_led(psmouse) < 0)
+		goto init_fail;
+
	err = set_input_params(psmouse, priv);
	if (err) {
		psmouse_err(psmouse,
 
diff -ur old/drivers/input/mouse/synaptics.h new/drivers/input/mouse/synaptics.h
--- old/drivers/input/mouse/synaptics.h	2017-08-30 11:32:30.000000000 +0300
+++ new/drivers/input/mouse/synaptics.h	2017-09-01 08:27:07.849088154 +0300
@@ -180,6 +180,8 @@
 	u32 x_min, y_min;	/* Min coordinates (from FW) */
 };
 
+struct synaptics_led;
+
 struct synaptics_data {
 	struct synaptics_device_info info;
 
@@ -204,6 +206,7 @@
 	bool					press;
 	bool					report_press;
 	bool					is_forcepad;
+	struct synaptics_led *led;
 };
 
 void synaptics_module_init(void);