aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/serio
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 /drivers/input/serio
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>
Diffstat (limited to 'drivers/input/serio')
-rw-r--r--drivers/input/serio/i8042.c41
-rw-r--r--drivers/input/serio/libps2.c28
2 files changed, 65 insertions, 4 deletions
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index eb3ff94af58..bc56e52b945 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 3a95b508bf2..769ba65a585 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}