Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+117 -12
+10 -8
drivers/input/mouse/sentelic.c
··· 92 92 */ 93 93 ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); 94 94 psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); 95 - mutex_lock(&ps2dev->cmd_mutex); 95 + 96 + ps2_begin_command(ps2dev); 96 97 97 98 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 98 99 goto out; ··· 127 126 rc = 0; 128 127 129 128 out: 130 - mutex_unlock(&ps2dev->cmd_mutex); 129 + ps2_end_command(ps2dev); 131 130 ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); 132 131 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 133 132 dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", ··· 141 140 unsigned char v; 142 141 int rc = -1; 143 142 144 - mutex_lock(&ps2dev->cmd_mutex); 143 + ps2_begin_command(ps2dev); 145 144 146 145 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 147 146 goto out; ··· 180 179 rc = 0; 181 180 182 181 out: 183 - mutex_unlock(&ps2dev->cmd_mutex); 182 + ps2_end_command(ps2dev); 184 183 dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", 185 184 reg_addr, reg_val, rc); 186 185 return rc; ··· 215 214 216 215 ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); 217 216 psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); 218 - mutex_lock(&ps2dev->cmd_mutex); 217 + 218 + ps2_begin_command(ps2dev); 219 219 220 220 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 221 221 goto out; ··· 238 236 rc = 0; 239 237 240 238 out: 241 - mutex_unlock(&ps2dev->cmd_mutex); 239 + ps2_end_command(ps2dev); 242 240 ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); 243 241 psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 244 242 dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", ··· 252 250 unsigned char v; 253 251 int rc = -1; 254 252 255 - mutex_lock(&ps2dev->cmd_mutex); 253 + ps2_begin_command(ps2dev); 256 254 257 255 if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) 258 256 goto out; ··· 277 275 rc = 0; 278 276 279 277 out: 280 - mutex_unlock(&ps2dev->cmd_mutex); 278 + ps2_end_command(ps2dev); 281 279 dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", 282 280 reg_val, rc); 283 281 return rc;
+41
drivers/input/serio/i8042.c
··· 87 87 88 88 #include "i8042.h" 89 89 90 + /* 91 + * i8042_lock protects serialization between i8042_command and 92 + * the interrupt handler. 93 + */ 90 94 static DEFINE_SPINLOCK(i8042_lock); 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 + */ 104 + static DEFINE_MUTEX(i8042_mutex); 91 105 92 106 struct i8042_port { 93 107 struct serio *serio; ··· 126 112 static struct platform_device *i8042_platform_device; 127 113 128 114 static irqreturn_t i8042_interrupt(int irq, void *dev_id); 115 + 116 + void i8042_lock_chip(void) 117 + { 118 + mutex_lock(&i8042_mutex); 119 + } 120 + EXPORT_SYMBOL(i8042_lock_chip); 121 + 122 + void i8042_unlock_chip(void) 123 + { 124 + mutex_unlock(&i8042_mutex); 125 + } 126 + EXPORT_SYMBOL(i8042_unlock_chip); 129 127 130 128 /* 131 129 * The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to ··· 1186 1160 } 1187 1161 } 1188 1162 } 1163 + 1164 + /* 1165 + * Checks whether port belongs to i8042 controller. 1166 + */ 1167 + bool i8042_check_port_owner(const struct serio *port) 1168 + { 1169 + int i; 1170 + 1171 + for (i = 0; i < I8042_NUM_PORTS; i++) 1172 + if (i8042_ports[i].serio == port) 1173 + return true; 1174 + 1175 + return false; 1176 + } 1177 + EXPORT_SYMBOL(i8042_check_port_owner); 1189 1178 1190 1179 static void i8042_free_irqs(void) 1191 1180 {
+24 -4
drivers/input/serio/libps2.c
··· 17 17 #include <linux/interrupt.h> 18 18 #include <linux/input.h> 19 19 #include <linux/serio.h> 20 + #include <linux/i8042.h> 20 21 #include <linux/init.h> 21 22 #include <linux/libps2.h> 22 23 ··· 55 54 } 56 55 EXPORT_SYMBOL(ps2_sendbyte); 57 56 57 + void ps2_begin_command(struct ps2dev *ps2dev) 58 + { 59 + mutex_lock(&ps2dev->cmd_mutex); 60 + 61 + if (i8042_check_port_owner(ps2dev->serio)) 62 + i8042_lock_chip(); 63 + } 64 + EXPORT_SYMBOL(ps2_begin_command); 65 + 66 + void ps2_end_command(struct ps2dev *ps2dev) 67 + { 68 + if (i8042_check_port_owner(ps2dev->serio)) 69 + i8042_unlock_chip(); 70 + 71 + mutex_unlock(&ps2dev->cmd_mutex); 72 + } 73 + EXPORT_SYMBOL(ps2_end_command); 74 + 58 75 /* 59 76 * ps2_drain() waits for device to transmit requested number of bytes 60 77 * and discards them. ··· 85 66 maxbytes = sizeof(ps2dev->cmdbuf); 86 67 } 87 68 88 - mutex_lock(&ps2dev->cmd_mutex); 69 + ps2_begin_command(ps2dev); 89 70 90 71 serio_pause_rx(ps2dev->serio); 91 72 ps2dev->flags = PS2_FLAG_CMD; ··· 95 76 wait_event_timeout(ps2dev->wait, 96 77 !(ps2dev->flags & PS2_FLAG_CMD), 97 78 msecs_to_jiffies(timeout)); 98 - mutex_unlock(&ps2dev->cmd_mutex); 79 + 80 + ps2_end_command(ps2dev); 99 81 } 100 82 EXPORT_SYMBOL(ps2_drain); 101 83 ··· 257 237 { 258 238 int rc; 259 239 260 - mutex_lock(&ps2dev->cmd_mutex); 240 + ps2_begin_command(ps2dev); 261 241 rc = __ps2_command(ps2dev, param, command); 262 - mutex_unlock(&ps2dev->cmd_mutex); 242 + ps2_end_command(ps2dev); 263 243 264 244 return rc; 265 245 }
+8
drivers/leds/leds-clevo-mail.c
··· 93 93 static void clevo_mail_led_set(struct led_classdev *led_cdev, 94 94 enum led_brightness value) 95 95 { 96 + i8042_lock_chip(); 97 + 96 98 if (value == LED_OFF) 97 99 i8042_command(NULL, CLEVO_MAIL_LED_OFF); 98 100 else if (value <= LED_HALF) 99 101 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_0_5HZ); 100 102 else 101 103 i8042_command(NULL, CLEVO_MAIL_LED_BLINK_1HZ); 104 + 105 + i8042_unlock_chip(); 102 106 103 107 } 104 108 ··· 111 107 unsigned long *delay_off) 112 108 { 113 109 int status = -EINVAL; 110 + 111 + i8042_lock_chip(); 114 112 115 113 if (*delay_on == 0 /* ms */ && *delay_off == 0 /* ms */) { 116 114 /* Special case: the leds subsystem requested us to ··· 140 134 " returning -EINVAL (unsupported)\n", 141 135 *delay_on, *delay_off); 142 136 } 137 + 138 + i8042_unlock_chip(); 143 139 144 140 return status; 145 141 }
+2
drivers/platform/x86/acer-wmi.c
··· 746 746 return AE_BAD_PARAMETER; 747 747 if (quirks->mailled == 1) { 748 748 param = value ? 0x92 : 0x93; 749 + i8042_lock_chip(); 749 750 i8042_command(&param, 0x1059); 751 + i8042_unlock_chip(); 750 752 return 0; 751 753 } 752 754 break;
+30
include/linux/i8042.h
··· 7 7 * the Free Software Foundation. 8 8 */ 9 9 10 + #include <linux/types.h> 10 11 11 12 /* 12 13 * Standard commands. ··· 31 30 #define I8042_CMD_MUX_PFX 0x0090 32 31 #define I8042_CMD_MUX_SEND 0x1090 33 32 33 + struct serio; 34 + 35 + #if defined(CONFIG_SERIO_I8042) || defined(CONFIG_SERIO_I8042_MODULE) 36 + 37 + void i8042_lock_chip(void); 38 + void i8042_unlock_chip(void); 34 39 int i8042_command(unsigned char *param, int command); 40 + bool i8042_check_port_owner(const struct serio *); 41 + 42 + #else 43 + 44 + void i8042_lock_chip(void) 45 + { 46 + } 47 + 48 + void i8042_unlock_chip(void) 49 + { 50 + } 51 + 52 + int i8042_command(unsigned char *param, int command) 53 + { 54 + return -ENOSYS; 55 + } 56 + 57 + bool i8042_check_port_owner(const struct serio *serio) 58 + { 59 + return false; 60 + } 61 + 62 + #endif 35 63 36 64 #endif
+2
include/linux/libps2.h
··· 44 44 void ps2_init(struct ps2dev *ps2dev, struct serio *serio); 45 45 int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); 46 46 void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); 47 + void ps2_begin_command(struct ps2dev *ps2dev); 48 + void ps2_end_command(struct ps2dev *ps2dev); 47 49 int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); 48 50 int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); 49 51 int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);