aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2009-09-16 04:06:43 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-09-18 02:23:45 -0400
commit181d683d752c432635eda0f182ee71548c1f1820 (patch)
tree1fa397811ea6bcc73001fe1ab5b53525f2b88f74
parent0cb583fd2862f19ea88b02eb307d11c09e51e2f8 (diff)
Input: libps2 - additional locking for i8042 ports
The serio ports on i8042 are not completely isolated; while we provide enough locking to ensure proper serialization when accessing control and data registers AUX and KBD ports can still have an effect on each other on PS/2 protocol level. The most prominent effect is that issuing a command for the device connected to one port may cause abort of the command currently executing by the device connected to another port. Since i8042 nor serio subsystem are not aware of the details of the PS/2 protocol (length of the commands and their replies and so on) the locking should be done on libps2 level by adding special handling when we see that we are dealing with serio port on i8042. Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--drivers/input/mouse/sentelic.c18
-rw-r--r--drivers/input/serio/i8042.c41
-rw-r--r--drivers/input/serio/libps2.c28
-rw-r--r--drivers/leds/leds-clevo-mail.c8
-rw-r--r--drivers/platform/x86/acer-wmi.c2
-rw-r--r--include/linux/i8042.h30
-rw-r--r--include/linux/libps2.h2
7 files changed, 117 insertions, 12 deletions
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 84e2fc04d11b..f84cbd97c884 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -92,7 +92,8 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
92 */ 92 */
93 ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); 93 ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
94 psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); 94 psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
95 mutex_lock(&ps2dev->cmd_mutex); 95
96 ps2_begin_command(ps2dev);
96 97
97 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 98 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
98 goto out; 99 goto out;
@@ -126,7 +127,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
126 rc = 0; 127 rc = 0;
127 128
128 out: 129 out:
129 mutex_unlock(&ps2dev->cmd_mutex); 130 ps2_end_command(ps2dev);
130 ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); 131 ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
131 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 132 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
132 dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", 133 dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
@@ -140,7 +141,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
140 unsigned char v; 141 unsigned char v;
141 int rc = -1; 142 int rc = -1;
142 143
143 mutex_lock(&ps2dev->cmd_mutex); 144 ps2_begin_command(ps2dev);
144 145
145 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 146 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
146 goto out; 147 goto out;
@@ -179,7 +180,7 @@ static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
179 rc = 0; 180 rc = 0;
180 181
181 out: 182 out:
182 mutex_unlock(&ps2dev->cmd_mutex); 183 ps2_end_command(ps2dev);
183 dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", 184 dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
184 reg_addr, reg_val, rc); 185 reg_addr, reg_val, rc);
185 return rc; 186 return rc;
@@ -214,7 +215,8 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
214 215
215 ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); 216 ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
216 psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); 217 psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
217 mutex_lock(&ps2dev->cmd_mutex); 218
219 ps2_begin_command(ps2dev);
218 220
219 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 221 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
220 goto out; 222 goto out;
@@ -236,7 +238,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
236 rc = 0; 238 rc = 0;
237 239
238 out: 240 out:
239 mutex_unlock(&ps2dev->cmd_mutex); 241 ps2_end_command(ps2dev);
240 ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); 242 ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
241 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 243 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
242 dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", 244 dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
@@ -250,7 +252,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
250 unsigned char v; 252 unsigned char v;
251 int rc = -1; 253 int rc = -1;
252 254
253 mutex_lock(&ps2dev->cmd_mutex); 255 ps2_begin_command(ps2dev);
254 256
255 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 257 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
256 goto out; 258 goto out;
@@ -275,7 +277,7 @@ static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
275 rc = 0; 277 rc = 0;
276 278
277 out: 279 out:
278 mutex_unlock(&ps2dev->cmd_mutex); 280 ps2_end_command(ps2dev);
279 dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", 281 dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
280 reg_val, rc); 282 reg_val, rc);
281 return rc; 283 return rc;
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index eb3ff94af58c..bc56e52b945f 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -87,8 +87,22 @@ static bool i8042_bypass_aux_irq_test;
87 87
88#include "i8042.h" 88#include "i8042.h"
89 89
90/*
91 * i8042_lock protects serialization between i8042_command and
92 * the interrupt handler.
93 */
90static DEFINE_SPINLOCK(i8042_lock); 94static DEFINE_SPINLOCK(i8042_lock);
91 95
96/*
97 * Writers to AUX and KBD ports as well as users issuing i8042_command
98 * directly should acquire i8042_mutex (by means of calling
99 * i8042_lock_chip() and i8042_unlock_ship() helpers) to ensure that
100 * they do not disturb each other (unfortunately in many i8042
101 * implementations write to one of the ports will immediately abort
102 * command that is being processed by another port).
103 */
104static DEFINE_MUTEX(i8042_mutex);
105
92struct i8042_port { 106struct i8042_port {
93 struct serio *serio; 107 struct serio *serio;
94 int irq; 108 int irq;
@@ -113,6 +127,18 @@ static struct platform_device *i8042_platform_device;
113 127
114static irqreturn_t i8042_interrupt(int irq, void *dev_id); 128static irqreturn_t i8042_interrupt(int irq, void *dev_id);
115 129
130void i8042_lock_chip(void)
131{
132 mutex_lock(&i8042_mutex);
133}
134EXPORT_SYMBOL(i8042_lock_chip);
135
136void i8042_unlock_chip(void)
137{
138 mutex_unlock(&i8042_mutex);
139}
140EXPORT_SYMBOL(i8042_unlock_chip);
141
116/* 142/*
117 * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to 143 * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
118 * be ready for reading values from it / writing values to it. 144 * be ready for reading values from it / writing values to it.
@@ -1161,6 +1187,21 @@ static void __devexit i8042_unregister_ports(void)
1161 } 1187 }
1162} 1188}
1163 1189
1190/*
1191 * Checks whether port belongs to i8042 controller.
1192 */
1193bool i8042_check_port_owner(const struct serio *port)
1194{
1195 int i;
1196
1197 for (i = 0; i < I8042_NUM_PORTS; i++)
1198 if (i8042_ports[i].serio == port)
1199 return true;
1200
1201 return false;
1202}
1203EXPORT_SYMBOL(i8042_check_port_owner);
1204
1164static void i8042_free_irqs(void) 1205static void i8042_free_irqs(void)
1165{ 1206{
1166 if (i8042_aux_irq_registered) 1207 if (i8042_aux_irq_registered)
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 3a95b508bf27..769ba65a585a 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -17,6 +17,7 @@
17#include <linux/interrupt.h> 17#include <linux/interrupt.h>
18#include <linux/input.h> 18#include <linux/input.h>
19#include <linux/serio.h> 19#include <linux/serio.h>
20#include <linux/i8042.h>
20#include <linux/init.h> 21#include <linux/init.h>
21#include <linux/libps2.h> 22#include <linux/libps2.h>
22 23
@@ -54,6 +55,24 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
54} 55}
55EXPORT_SYMBOL(ps2_sendbyte); 56EXPORT_SYMBOL(ps2_sendbyte);
56 57
58void ps2_begin_command(struct ps2dev *ps2dev)
59{
60 mutex_lock(&ps2dev->cmd_mutex);
61
62 if (i8042_check_port_owner(ps2dev->serio))
63 i8042_lock_chip();
64}
65EXPORT_SYMBOL(ps2_begin_command);
66
67void ps2_end_command(struct ps2dev *ps2dev)
68{
69 if (i8042_check_port_owner(ps2dev->serio))
70 i8042_unlock_chip();
71
72 mutex_unlock(&ps2dev->cmd_mutex);
73}
74EXPORT_SYMBOL(ps2_end_command);
75
57/* 76/*
58 * ps2_drain() waits for device to transmit requested number of bytes 77 * ps2_drain() waits for device to transmit requested number of bytes
59 * and discards them. 78 * and discards them.
@@ -66,7 +85,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
66 maxbytes = sizeof(ps2dev->cmdbuf); 85 maxbytes = sizeof(ps2dev->cmdbuf);
67 } 86 }
68 87
69 mutex_lock(&ps2dev->cmd_mutex); 88 ps2_begin_command(ps2dev);
70 89
71 serio_pause_rx(ps2dev->serio); 90 serio_pause_rx(ps2dev->serio);
72 ps2dev->flags = PS2_FLAG_CMD; 91 ps2dev->flags = PS2_FLAG_CMD;
@@ -76,7 +95,8 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
76 wait_event_timeout(ps2dev->wait, 95 wait_event_timeout(ps2dev->wait,
77 !(ps2dev->flags & PS2_FLAG_CMD), 96 !(ps2dev->flags & PS2_FLAG_CMD),
78 msecs_to_jiffies(timeout)); 97 msecs_to_jiffies(timeout));
79 mutex_unlock(&ps2dev->cmd_mutex); 98
99 ps2_end_command(ps2dev);
80} 100}
81EXPORT_SYMBOL(ps2_drain); 101EXPORT_SYMBOL(ps2_drain);
82 102
@@ -237,9 +257,9 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
237{ 257{
238 int rc; 258 int rc;
239 259
240 mutex_lock(&ps2dev->cmd_mutex); 260 ps2_begin_command(ps2dev);
241 rc = __ps2_command(ps2dev, param, command); 261 rc = __ps2_command(ps2dev, param, command);
242 mutex_unlock(&ps2dev->cmd_mutex); 262 ps2_end_command(ps2dev);
243 263
244 return rc; 264 return rc;
245} 265}
diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c
index 1813c84ea5fc..f2242db54016 100644
--- a/drivers/leds/leds-clevo-mail.c
+++ b/drivers/leds/leds-clevo-mail.c
@@ -93,6 +93,8 @@ static struct dmi_system_id __initdata mail_led_whitelist[] = {
93static void clevo_mail_led_set(struct led_classdev *led_cdev, 93static void clevo_mail_led_set(struct led_classdev *led_cdev,
94 enum led_brightness value) 94 enum led_brightness value)
95{ 95{
96 i8042_lock_chip();
97
96 if (value == LED_OFF) 98 if (value == LED_OFF)
97 i8042_command(NULL, CLEVO_MAIL_LED_OFF); 99 i8042_command(NULL, CLEVO_MAIL_LED_OFF);
98 else if (value <= LED_HALF) 100 else if (value <= LED_HALF)
@@ -100,6 +102,8 @@ static void clevo_mail_led_set(struct led_classdev *led_cdev,
100 else 102 else
101 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ); 103 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ);
102 104
105 i8042_unlock_chip();
106
103} 107}
104 108
105static int clevo_mail_led_blink(struct led_classdev *led_cdev, 109static int clevo_mail_led_blink(struct led_classdev *led_cdev,
@@ -108,6 +112,8 @@ static int clevo_mail_led_blink(struct led_classdev *led_cdev,
108{ 112{
109 int status = -EINVAL; 113 int status = -EINVAL;
110 114
115 i8042_lock_chip();
116
111 if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) { 117 if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) {
112 /* Special case: the leds subsystem requested us to 118 /* Special case: the leds subsystem requested us to
113 * chose one user friendly blinking of the LED, and 119 * chose one user friendly blinking of the LED, and
@@ -135,6 +141,8 @@ static int clevo_mail_led_blink(struct led_classdev *led_cdev,
135 *delay_on, *delay_off); 141 *delay_on, *delay_off);
136 } 142 }
137 143
144 i8042_unlock_chip();
145
138 return status; 146 return status;
139} 147}
140 148
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index fb45f5ee8df1..454970d2d701 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -746,7 +746,9 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
746 return AE_BAD_PARAMETER; 746 return AE_BAD_PARAMETER;
747 if (quirks->mailled == 1) { 747 if (quirks->mailled == 1) {
748 param = value ? 0x92 : 0x93; 748 param = value ? 0x92 : 0x93;
749 i8042_lock_chip();
749 i8042_command(&param, 0x1059); 750 i8042_command(&param, 0x1059);
751 i8042_unlock_chip();
750 return 0; 752 return 0;
751 } 753 }
752 break; 754 break;
diff --git a/include/linux/i8042.h b/include/linux/i8042.h
index 7907a72403ee..60c3360ef6ad 100644
--- a/include/linux/i8042.h
+++ b/include/linux/i8042.h
@@ -7,6 +7,7 @@
7 * the Free Software Foundation. 7 * the Free Software Foundation.
8 */ 8 */
9 9
10#include <linux/types.h>
10 11
11/* 12/*
12 * Standard commands. 13 * Standard commands.
@@ -30,6 +31,35 @@
30#define I8042_CMD_MUX_PFX 0x0090 31#define I8042_CMD_MUX_PFX 0x0090
31#define I8042_CMD_MUX_SEND 0x1090 32#define I8042_CMD_MUX_SEND 0x1090
32 33
34struct serio;
35
36#if defined(CONFIG_SERIO_I8042) || defined(CONFIG_SERIO_I8042_MODULE)
37
38void i8042_lock_chip(void);
39void i8042_unlock_chip(void);
33int i8042_command(unsigned char *param, int command); 40int i8042_command(unsigned char *param, int command);
41bool i8042_check_port_owner(const struct serio *);
42
43#else
44
45void i8042_lock_chip(void)
46{
47}
48
49void i8042_unlock_chip(void)
50{
51}
52
53int i8042_command(unsigned char *param, int command)
54{
55 return -ENOSYS;
56}
57
58bool i8042_check_port_owner(const struct serio *serio)
59{
60 return false;
61}
62
63#endif
34 64
35#endif 65#endif
diff --git a/include/linux/libps2.h b/include/linux/libps2.h
index fcf5fbe6a50c..79603a6c356f 100644
--- a/include/linux/libps2.h
+++ b/include/linux/libps2.h
@@ -44,6 +44,8 @@ struct ps2dev {
44void ps2_init(struct ps2dev *ps2dev, struct serio *serio); 44void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
45int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); 45int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout);
46void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); 46void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout);
47void ps2_begin_command(struct ps2dev *ps2dev);
48void ps2_end_command(struct ps2dev *ps2dev);
47int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); 49int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
48int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); 50int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
49int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); 51int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);