@@ -28,6 +28,7 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/libps2.h>
+#include <linux/workqueue.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "synaptics.h"
@@ -328,6 +329,37 @@ static void synaptics_pt_create(struct psmouse *psmouse)
* Functions to interpret the absolute mode packets
****************************************************************************/
+static void synaptics_set_led(struct psmouse *psmouse, int on)
+{
+ unsigned char param[1];
+
+ if (psmouse_sliced_command(psmouse, on ? 0x88 : 0x10))
+ return;
+ param[0] = 0x0a;
+ ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE);
+}
+
+static void synaptics_led_work(struct work_struct *work)
+{
+ struct synaptics_data *priv = container_of(work, struct synaptics_data, led_work);
+ synaptics_set_led(priv->psmouse, priv->led_status);
+}
+
+/* input event handler: changed by x11 synaptics driver */
+static int synaptics_led_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct synaptics_data *priv = dev->dev.platform_data;
+
+ if (!priv)
+ return 0;
+ if (type == EV_LED && code == LED_MUTE) {
+ priv->led_status = !!value;
+ schedule_work(&priv->led_work);
+ }
+ return 0;
+}
+
static void synaptics_check_clickpad(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
@@ -344,6 +376,12 @@ static void synaptics_check_clickpad(struct psmouse *psmouse)
if (priv->clickpad)
printk(KERN_INFO "Synaptics: Clickpad device detected: %d\n",
priv->clickpad);
+ /* FIXME: this should be ncap[1] 0x20, but it's not really... */
+ priv->has_led = 1;
+ if (priv->has_led) {
+ printk(KERN_INFO "Synaptics: support LED control\n");
+ INIT_WORK(&priv->led_work, synaptics_led_work);
+ }
}
static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw)
@@ -623,10 +661,22 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
__clear_bit(BTN_RIGHT, dev->keybit); /* only left-button */
__clear_bit(BTN_MIDDLE, dev->keybit);
}
+ if (priv->has_led) {
+ __set_bit(EV_LED, dev->evbit);
+ __set_bit(LED_MUTE, dev->ledbit);
+ dev->event = synaptics_led_event;
+ dev->dev.platform_data = priv;
+ }
}
static void synaptics_disconnect(struct psmouse *psmouse)
{
+ struct synaptics_data *priv = psmouse->private;
+
+ if (priv->has_led) {
+ cancel_work_sync(&priv->led_work);
+ synaptics_set_led(psmouse, 0);
+ }
synaptics_reset(psmouse);
kfree(psmouse->private);
psmouse->private = NULL;
@@ -658,6 +708,9 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return -1;
}
+ if (priv->has_led)
+ synaptics_set_led(psmouse, priv->led_status);
+
return 0;
}
@@ -713,6 +766,7 @@ int synaptics_init(struct psmouse *psmouse)
if (!priv)
return -1;
+ priv->psmouse = psmouse;
psmouse_reset(psmouse);
if (synaptics_query_hardware(psmouse)) {
@@ -107,6 +107,10 @@ struct synaptics_data {
int scroll;
unsigned char clickpad;
+ unsigned char has_led;
+ unsigned char led_status;
+ struct psmouse *psmouse;
+ struct work_struct led_work;
};
void synaptics_module_init(void);