1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
From b39dcb00fbece6da346b6581c3ec6c6a2ba133ca Mon Sep 17 00:00:00 2001
Message-Id: <b39dcb00fbece6da346b6581c3ec6c6a2ba133ca.1380721797.git.matthew.monaco@0x01b.net>
From: Takashi Iwai <tiwai@suse.de>
Date: Sun, 16 Sep 2012 14:19:41 -0600
Subject: [PATCH 1/2] input: Add LED support to Synaptics device
The new Synaptics devices have an LED on the top-left corner.
This patch adds a new LED class device to control it. It's created
dynamically upon synaptics device probing.
The LED is controlled via the command 0x0a with parameters 0x88 or 0x10.
This seems only on/off control although other value might be accepted.
The detection of the LED isn't clear yet. It should have been the new
capability bits that indicate the presence, but on real machines, it
doesn't fit. So, for the time being, the driver checks the product id
in the ext capability bits and assumes that LED exists on the known
devices.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
References: bnc#547370,bnc#582529,bnc#589014
Conflicts:
drivers/input/mouse/Kconfig
---
drivers/input/mouse/Kconfig | 8 +++
drivers/input/mouse/synaptics.c | 124 ++++++++++++++++++++++++++++++++++++++++
drivers/input/mouse/synaptics.h | 3 +
3 files changed, 135 insertions(+)
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index effa9c5..39d1578 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -78,6 +78,14 @@ config MOUSE_PS2_CYPRESS
If unsure, say Y.
+config MOUSE_PS2_SYNAPTICS_LED
+ bool "Support embedded LED on Synaptics devices"
+ 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 --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index b2420ae..44051fe9 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/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/slab.h>
#include "psmouse.h"
#include "synaptics.h"
@@ -485,6 +486,123 @@ static void synaptics_pt_create(struct psmouse *psmouse)
serio_register_port(serio);
}
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_LED
+/*
+ * 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)
+{
+ if (!priv->ext_cap_0c)
+ return false;
+ /* 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.
+ */
+ if (SYN_CAP_PRODUCT_ID(priv->ext_cap) != 0xe4 &&
+ SYN_CAP_PRODUCT_ID(priv->ext_cap) != 0x64 &&
+ SYN_CAP_PRODUCT_ID(priv->ext_cap) != 0x84)
+ return false;
+ if (!(priv->ext_cap_0c & 0x2000) &&
+ (priv->capabilities & 0xd000ff) != 0xd00073)
+ return false;
+ 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
****************************************************************************/
@@ -1370,6 +1488,7 @@ static void synaptics_disconnect(struct psmouse *psmouse)
device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_disable_gesture.dattr);
+ synaptics_free_led(psmouse);
synaptics_reset(psmouse);
kfree(priv);
psmouse->private = NULL;
@@ -1428,6 +1547,8 @@ static int synaptics_reconnect(struct psmouse *psmouse)
return -1;
}
+ synaptics_sync_led(psmouse);
+
return 0;
}
@@ -1538,6 +1659,9 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
priv->board_id, priv->firmware_id);
+ if (synaptics_init_led(psmouse) < 0)
+ goto init_fail;
+
set_input_params(psmouse->dev, priv);
/*
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index e594af0..c747925 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -146,6 +146,8 @@ struct synaptics_hw_state {
struct synaptics_mt_state mt_state;
};
+struct synaptics_led;
+
struct synaptics_data {
/* Data read from the touchpad */
unsigned long int model_id; /* Model-ID */
@@ -177,6 +179,7 @@ struct synaptics_data {
*/
struct synaptics_hw_state agm;
bool agm_pending; /* new AGM packet received */
+ struct synaptics_led *led;
};
void synaptics_module_init(void);
--
1.8.4
|