| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * PS/2 driver library | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 1999-2002 Vojtech Pavlik | 
 | 5 |  * Copyright (c) 2004 Dmitry Torokhov | 
 | 6 |  */ | 
 | 7 |  | 
 | 8 | /* | 
 | 9 |  * This program is free software; you can redistribute it and/or modify it | 
 | 10 |  * under the terms of the GNU General Public License version 2 as published by | 
 | 11 |  * the Free Software Foundation. | 
 | 12 |  */ | 
 | 13 |  | 
 | 14 | #include <linux/delay.h> | 
 | 15 | #include <linux/module.h> | 
| Alexey Dobriyan | d43c36d | 2009-10-07 17:09:06 +0400 | [diff] [blame] | 16 | #include <linux/sched.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 | #include <linux/interrupt.h> | 
 | 18 | #include <linux/input.h> | 
 | 19 | #include <linux/serio.h> | 
| Dmitry Torokhov | 181d683 | 2009-09-16 01:06:43 -0700 | [diff] [blame] | 20 | #include <linux/i8042.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | #include <linux/init.h> | 
 | 22 | #include <linux/libps2.h> | 
 | 23 |  | 
 | 24 | #define DRIVER_DESC	"PS/2 driver library" | 
 | 25 |  | 
 | 26 | MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>"); | 
 | 27 | MODULE_DESCRIPTION("PS/2 driver library"); | 
 | 28 | MODULE_LICENSE("GPL"); | 
 | 29 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | /* | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 31 |  * ps2_sendbyte() sends a byte to the device and waits for acknowledge. | 
 | 32 |  * It doesn't handle retransmission, though it could - because if there | 
 | 33 |  * is a need for retransmissions device has to be replaced anyway. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 |  * | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 35 |  * ps2_sendbyte() can only be called from a process context. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 |  */ | 
 | 37 |  | 
 | 38 | int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout) | 
 | 39 | { | 
 | 40 | 	serio_pause_rx(ps2dev->serio); | 
 | 41 | 	ps2dev->nak = 1; | 
 | 42 | 	ps2dev->flags |= PS2_FLAG_ACK; | 
 | 43 | 	serio_continue_rx(ps2dev->serio); | 
 | 44 |  | 
 | 45 | 	if (serio_write(ps2dev->serio, byte) == 0) | 
 | 46 | 		wait_event_timeout(ps2dev->wait, | 
 | 47 | 				   !(ps2dev->flags & PS2_FLAG_ACK), | 
 | 48 | 				   msecs_to_jiffies(timeout)); | 
 | 49 |  | 
 | 50 | 	serio_pause_rx(ps2dev->serio); | 
 | 51 | 	ps2dev->flags &= ~PS2_FLAG_ACK; | 
 | 52 | 	serio_continue_rx(ps2dev->serio); | 
 | 53 |  | 
 | 54 | 	return -ps2dev->nak; | 
 | 55 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 56 | EXPORT_SYMBOL(ps2_sendbyte); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 |  | 
| Dmitry Torokhov | 181d683 | 2009-09-16 01:06:43 -0700 | [diff] [blame] | 58 | void 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 | } | 
 | 65 | EXPORT_SYMBOL(ps2_begin_command); | 
 | 66 |  | 
 | 67 | void 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 | } | 
 | 74 | EXPORT_SYMBOL(ps2_end_command); | 
 | 75 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 76 | /* | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 77 |  * ps2_drain() waits for device to transmit requested number of bytes | 
 | 78 |  * and discards them. | 
 | 79 |  */ | 
 | 80 |  | 
 | 81 | void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) | 
 | 82 | { | 
 | 83 | 	if (maxbytes > sizeof(ps2dev->cmdbuf)) { | 
 | 84 | 		WARN_ON(1); | 
 | 85 | 		maxbytes = sizeof(ps2dev->cmdbuf); | 
 | 86 | 	} | 
 | 87 |  | 
| Dmitry Torokhov | 181d683 | 2009-09-16 01:06:43 -0700 | [diff] [blame] | 88 | 	ps2_begin_command(ps2dev); | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 89 |  | 
 | 90 | 	serio_pause_rx(ps2dev->serio); | 
 | 91 | 	ps2dev->flags = PS2_FLAG_CMD; | 
 | 92 | 	ps2dev->cmdcnt = maxbytes; | 
 | 93 | 	serio_continue_rx(ps2dev->serio); | 
 | 94 |  | 
 | 95 | 	wait_event_timeout(ps2dev->wait, | 
 | 96 | 			   !(ps2dev->flags & PS2_FLAG_CMD), | 
 | 97 | 			   msecs_to_jiffies(timeout)); | 
| Dmitry Torokhov | 181d683 | 2009-09-16 01:06:43 -0700 | [diff] [blame] | 98 |  | 
 | 99 | 	ps2_end_command(ps2dev); | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 100 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 101 | EXPORT_SYMBOL(ps2_drain); | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 102 |  | 
 | 103 | /* | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 104 |  * ps2_is_keyboard_id() checks received ID byte against the list of | 
 | 105 |  * known keyboard IDs. | 
 | 106 |  */ | 
 | 107 |  | 
| Dmitry Torokhov | 9807879 | 2006-09-14 01:31:27 -0400 | [diff] [blame] | 108 | int ps2_is_keyboard_id(char id_byte) | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 109 | { | 
| Tobias Klauser | c5a69d5 | 2007-02-17 20:11:19 +0100 | [diff] [blame] | 110 | 	static const char keyboard_ids[] = { | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 111 | 		0xab,	/* Regular keyboards		*/ | 
 | 112 | 		0xac,	/* NCD Sun keyboard		*/ | 
 | 113 | 		0x2b,	/* Trust keyboard, translated	*/ | 
 | 114 | 		0x5d,	/* Trust keyboard		*/ | 
 | 115 | 		0x60,	/* NMB SGI keyboard, translated */ | 
 | 116 | 		0x47,	/* NMB SGI keyboard		*/ | 
 | 117 | 	}; | 
 | 118 |  | 
 | 119 | 	return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL; | 
 | 120 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 121 | EXPORT_SYMBOL(ps2_is_keyboard_id); | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 122 |  | 
 | 123 | /* | 
 | 124 |  * ps2_adjust_timeout() is called after receiving 1st byte of command | 
 | 125 |  * response and tries to reduce remaining timeout to speed up command | 
 | 126 |  * completion. | 
 | 127 |  */ | 
 | 128 |  | 
 | 129 | static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout) | 
 | 130 | { | 
 | 131 | 	switch (command) { | 
 | 132 | 		case PS2_CMD_RESET_BAT: | 
 | 133 | 			/* | 
 | 134 | 			 * Device has sent the first response byte after | 
 | 135 | 			 * reset command, reset is thus done, so we can | 
 | 136 | 			 * shorten the timeout. | 
 | 137 | 			 * The next byte will come soon (keyboard) or not | 
 | 138 | 			 * at all (mouse). | 
 | 139 | 			 */ | 
 | 140 | 			if (timeout > msecs_to_jiffies(100)) | 
 | 141 | 				timeout = msecs_to_jiffies(100); | 
 | 142 | 			break; | 
 | 143 |  | 
 | 144 | 		case PS2_CMD_GETID: | 
 | 145 | 			/* | 
| Dmitry Torokhov | 9807879 | 2006-09-14 01:31:27 -0400 | [diff] [blame] | 146 | 			 * Microsoft Natural Elite keyboard responds to | 
 | 147 | 			 * the GET ID command as it were a mouse, with | 
 | 148 | 			 * a single byte. Fail the command so atkbd will | 
 | 149 | 			 * use alternative probe to detect it. | 
 | 150 | 			 */ | 
 | 151 | 			if (ps2dev->cmdbuf[1] == 0xaa) { | 
 | 152 | 				serio_pause_rx(ps2dev->serio); | 
 | 153 | 				ps2dev->flags = 0; | 
 | 154 | 				serio_continue_rx(ps2dev->serio); | 
 | 155 | 				timeout = 0; | 
 | 156 | 			} | 
 | 157 |  | 
 | 158 | 			/* | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 159 | 			 * If device behind the port is not a keyboard there | 
 | 160 | 			 * won't be 2nd byte of ID response. | 
 | 161 | 			 */ | 
 | 162 | 			if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) { | 
 | 163 | 				serio_pause_rx(ps2dev->serio); | 
 | 164 | 				ps2dev->flags = ps2dev->cmdcnt = 0; | 
 | 165 | 				serio_continue_rx(ps2dev->serio); | 
 | 166 | 				timeout = 0; | 
 | 167 | 			} | 
 | 168 | 			break; | 
 | 169 |  | 
 | 170 | 		default: | 
 | 171 | 			break; | 
 | 172 | 	} | 
 | 173 |  | 
 | 174 | 	return timeout; | 
 | 175 | } | 
 | 176 |  | 
 | 177 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 178 |  * ps2_command() sends a command and its parameters to the mouse, | 
 | 179 |  * then waits for the response and puts it in the param array. | 
 | 180 |  * | 
 | 181 |  * ps2_command() can only be called from a process context | 
 | 182 |  */ | 
 | 183 |  | 
| Tai-hwa Liang | fc69f4a | 2009-05-10 18:15:39 -0700 | [diff] [blame] | 184 | int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 185 | { | 
 | 186 | 	int timeout; | 
 | 187 | 	int send = (command >> 12) & 0xf; | 
 | 188 | 	int receive = (command >> 8) & 0xf; | 
 | 189 | 	int rc = -1; | 
 | 190 | 	int i; | 
 | 191 |  | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 192 | 	if (receive > sizeof(ps2dev->cmdbuf)) { | 
 | 193 | 		WARN_ON(1); | 
 | 194 | 		return -1; | 
 | 195 | 	} | 
 | 196 |  | 
| Dmitry Torokhov | 95349fe | 2006-07-06 23:54:48 -0400 | [diff] [blame] | 197 | 	if (send && !param) { | 
 | 198 | 		WARN_ON(1); | 
 | 199 | 		return -1; | 
 | 200 | 	} | 
 | 201 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 202 | 	serio_pause_rx(ps2dev->serio); | 
 | 203 | 	ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; | 
 | 204 | 	ps2dev->cmdcnt = receive; | 
 | 205 | 	if (receive && param) | 
 | 206 | 		for (i = 0; i < receive; i++) | 
 | 207 | 			ps2dev->cmdbuf[(receive - 1) - i] = param[i]; | 
 | 208 | 	serio_continue_rx(ps2dev->serio); | 
 | 209 |  | 
 | 210 | 	/* | 
 | 211 | 	 * Some devices (Synaptics) peform the reset before | 
 | 212 | 	 * ACKing the reset command, and so it can take a long | 
| Justin P. Mattock | 83ae417 | 2011-06-22 23:29:19 -0700 | [diff] [blame] | 213 | 	 * time before the ACK arrives. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | 	 */ | 
| Dmitry Torokhov | c611763 | 2005-06-01 02:39:51 -0500 | [diff] [blame] | 215 | 	if (ps2_sendbyte(ps2dev, command & 0xff, | 
 | 216 | 			 command == PS2_CMD_RESET_BAT ? 1000 : 200)) | 
 | 217 | 		goto out; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 218 |  | 
 | 219 | 	for (i = 0; i < send; i++) | 
 | 220 | 		if (ps2_sendbyte(ps2dev, param[i], 200)) | 
 | 221 | 			goto out; | 
 | 222 |  | 
 | 223 | 	/* | 
 | 224 | 	 * The reset command takes a long time to execute. | 
 | 225 | 	 */ | 
 | 226 | 	timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500); | 
 | 227 |  | 
 | 228 | 	timeout = wait_event_timeout(ps2dev->wait, | 
 | 229 | 				     !(ps2dev->flags & PS2_FLAG_CMD1), timeout); | 
 | 230 |  | 
| Dmitry Torokhov | a3ce6ea | 2009-05-28 09:51:31 -0700 | [diff] [blame] | 231 | 	if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 |  | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 233 | 		timeout = ps2_adjust_timeout(ps2dev, command, timeout); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 | 		wait_event_timeout(ps2dev->wait, | 
 | 235 | 				   !(ps2dev->flags & PS2_FLAG_CMD), timeout); | 
 | 236 | 	} | 
 | 237 |  | 
 | 238 | 	if (param) | 
 | 239 | 		for (i = 0; i < receive; i++) | 
 | 240 | 			param[i] = ps2dev->cmdbuf[(receive - 1) - i]; | 
 | 241 |  | 
 | 242 | 	if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1)) | 
 | 243 | 		goto out; | 
 | 244 |  | 
 | 245 | 	rc = 0; | 
 | 246 |  | 
| Dmitry Torokhov | 905ab9d | 2005-06-01 02:39:53 -0500 | [diff] [blame] | 247 |  out: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 248 | 	serio_pause_rx(ps2dev->serio); | 
 | 249 | 	ps2dev->flags = 0; | 
 | 250 | 	serio_continue_rx(ps2dev->serio); | 
 | 251 |  | 
| Tai-hwa Liang | fc69f4a | 2009-05-10 18:15:39 -0700 | [diff] [blame] | 252 | 	return rc; | 
 | 253 | } | 
 | 254 | EXPORT_SYMBOL(__ps2_command); | 
 | 255 |  | 
 | 256 | int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) | 
 | 257 | { | 
 | 258 | 	int rc; | 
 | 259 |  | 
| Dmitry Torokhov | 181d683 | 2009-09-16 01:06:43 -0700 | [diff] [blame] | 260 | 	ps2_begin_command(ps2dev); | 
| Tai-hwa Liang | fc69f4a | 2009-05-10 18:15:39 -0700 | [diff] [blame] | 261 | 	rc = __ps2_command(ps2dev, param, command); | 
| Dmitry Torokhov | 181d683 | 2009-09-16 01:06:43 -0700 | [diff] [blame] | 262 | 	ps2_end_command(ps2dev); | 
| Tai-hwa Liang | fc69f4a | 2009-05-10 18:15:39 -0700 | [diff] [blame] | 263 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 264 | 	return rc; | 
 | 265 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 266 | EXPORT_SYMBOL(ps2_command); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 267 |  | 
 | 268 | /* | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 269 |  * ps2_init() initializes ps2dev structure | 
 | 270 |  */ | 
 | 271 |  | 
 | 272 | void ps2_init(struct ps2dev *ps2dev, struct serio *serio) | 
 | 273 | { | 
| Arjan van de Ven | c4e32e9 | 2006-02-19 00:21:55 -0500 | [diff] [blame] | 274 | 	mutex_init(&ps2dev->cmd_mutex); | 
| Jiri Kosina | 88aa010 | 2006-10-11 01:45:31 -0400 | [diff] [blame] | 275 | 	lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 276 | 	init_waitqueue_head(&ps2dev->wait); | 
 | 277 | 	ps2dev->serio = serio; | 
 | 278 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 279 | EXPORT_SYMBOL(ps2_init); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 280 |  | 
 | 281 | /* | 
 | 282 |  * ps2_handle_ack() is supposed to be used in interrupt handler | 
 | 283 |  * to properly process ACK/NAK of a command from a PS/2 device. | 
 | 284 |  */ | 
 | 285 |  | 
 | 286 | int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data) | 
 | 287 | { | 
 | 288 | 	switch (data) { | 
 | 289 | 		case PS2_RET_ACK: | 
 | 290 | 			ps2dev->nak = 0; | 
 | 291 | 			break; | 
 | 292 |  | 
 | 293 | 		case PS2_RET_NAK: | 
| Dmitry Torokhov | a2d781f | 2008-11-19 17:02:24 -0500 | [diff] [blame] | 294 | 			ps2dev->flags |= PS2_FLAG_NAK; | 
 | 295 | 			ps2dev->nak = PS2_RET_NAK; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | 			break; | 
 | 297 |  | 
| Dmitry Torokhov | a2d781f | 2008-11-19 17:02:24 -0500 | [diff] [blame] | 298 | 		case PS2_RET_ERR: | 
 | 299 | 			if (ps2dev->flags & PS2_FLAG_NAK) { | 
 | 300 | 				ps2dev->flags &= ~PS2_FLAG_NAK; | 
 | 301 | 				ps2dev->nak = PS2_RET_ERR; | 
 | 302 | 				break; | 
 | 303 | 			} | 
 | 304 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 | 		/* | 
 | 306 | 		 * Workaround for mice which don't ACK the Get ID command. | 
 | 307 | 		 * These are valid mouse IDs that we recognize. | 
 | 308 | 		 */ | 
 | 309 | 		case 0x00: | 
 | 310 | 		case 0x03: | 
 | 311 | 		case 0x04: | 
 | 312 | 			if (ps2dev->flags & PS2_FLAG_WAITID) { | 
 | 313 | 				ps2dev->nak = 0; | 
 | 314 | 				break; | 
 | 315 | 			} | 
 | 316 | 			/* Fall through */ | 
 | 317 | 		default: | 
 | 318 | 			return 0; | 
 | 319 | 	} | 
 | 320 |  | 
 | 321 |  | 
| Dmitry Torokhov | a2d781f | 2008-11-19 17:02:24 -0500 | [diff] [blame] | 322 | 	if (!ps2dev->nak) { | 
 | 323 | 		ps2dev->flags &= ~PS2_FLAG_NAK; | 
 | 324 | 		if (ps2dev->cmdcnt) | 
 | 325 | 			ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1; | 
 | 326 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 327 |  | 
 | 328 | 	ps2dev->flags &= ~PS2_FLAG_ACK; | 
 | 329 | 	wake_up(&ps2dev->wait); | 
 | 330 |  | 
 | 331 | 	if (data != PS2_RET_ACK) | 
 | 332 | 		ps2_handle_response(ps2dev, data); | 
 | 333 |  | 
 | 334 | 	return 1; | 
 | 335 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 336 | EXPORT_SYMBOL(ps2_handle_ack); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 |  | 
 | 338 | /* | 
 | 339 |  * ps2_handle_response() is supposed to be used in interrupt handler | 
 | 340 |  * to properly store device's response to a command and notify process | 
 | 341 |  * waiting for completion of the command. | 
 | 342 |  */ | 
 | 343 |  | 
 | 344 | int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data) | 
 | 345 | { | 
 | 346 | 	if (ps2dev->cmdcnt) | 
 | 347 | 		ps2dev->cmdbuf[--ps2dev->cmdcnt] = data; | 
 | 348 |  | 
 | 349 | 	if (ps2dev->flags & PS2_FLAG_CMD1) { | 
 | 350 | 		ps2dev->flags &= ~PS2_FLAG_CMD1; | 
 | 351 | 		if (ps2dev->cmdcnt) | 
 | 352 | 			wake_up(&ps2dev->wait); | 
 | 353 | 	} | 
 | 354 |  | 
 | 355 | 	if (!ps2dev->cmdcnt) { | 
 | 356 | 		ps2dev->flags &= ~PS2_FLAG_CMD; | 
 | 357 | 		wake_up(&ps2dev->wait); | 
 | 358 | 	} | 
 | 359 |  | 
 | 360 | 	return 1; | 
 | 361 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 362 | EXPORT_SYMBOL(ps2_handle_response); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 363 |  | 
 | 364 | void ps2_cmd_aborted(struct ps2dev *ps2dev) | 
 | 365 | { | 
 | 366 | 	if (ps2dev->flags & PS2_FLAG_ACK) | 
 | 367 | 		ps2dev->nak = 1; | 
 | 368 |  | 
 | 369 | 	if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD)) | 
 | 370 | 		wake_up(&ps2dev->wait); | 
 | 371 |  | 
| Dmitry Torokhov | a2d781f | 2008-11-19 17:02:24 -0500 | [diff] [blame] | 372 | 	/* reset all flags except last nack */ | 
 | 373 | 	ps2dev->flags &= PS2_FLAG_NAK; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 | } | 
| Dmitry Torokhov | 5206c0d | 2006-09-14 01:31:40 -0400 | [diff] [blame] | 375 | EXPORT_SYMBOL(ps2_cmd_aborted); |