11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * PS/2 driver library
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 1999-2002 Vojtech Pavlik
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (c) 2004 Dmitry Torokhov
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify it
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * under the terms of the GNU General Public License version 2 as published by
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the Free Software Foundation.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
16d43c36dc6b357fa1806800f18aa30123c747a6d1Alexey Dobriyan#include <linux/sched.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/input.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serio.h>
20181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov#include <linux/i8042.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/libps2.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DRIVER_DESC	"PS/2 driver library"
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("PS/2 driver library");
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
31c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * ps2_sendbyte() sends a byte to the device and waits for acknowledge.
32c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * It doesn't handle retransmission, though it could - because if there
33c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * is a need for retransmissions device has to be replaced anyway.
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
35c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * ps2_sendbyte() can only be called from a process context.
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_pause_rx(ps2dev->serio);
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->nak = 1;
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->flags |= PS2_FLAG_ACK;
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_continue_rx(ps2dev->serio);
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (serio_write(ps2dev->serio, byte) == 0)
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wait_event_timeout(ps2dev->wait,
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   !(ps2dev->flags & PS2_FLAG_ACK),
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   msecs_to_jiffies(timeout));
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_pause_rx(ps2dev->serio);
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->flags &= ~PS2_FLAG_ACK;
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_continue_rx(ps2dev->serio);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ps2dev->nak;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
565206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_sendbyte);
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
58181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhovvoid ps2_begin_command(struct ps2dev *ps2dev)
59181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov{
60181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	mutex_lock(&ps2dev->cmd_mutex);
61181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
62181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	if (i8042_check_port_owner(ps2dev->serio))
63181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov		i8042_lock_chip();
64181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov}
65181d683d752c432635eda0f182ee71548c1f1820Dmitry TorokhovEXPORT_SYMBOL(ps2_begin_command);
66181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
67181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhovvoid ps2_end_command(struct ps2dev *ps2dev)
68181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov{
69181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	if (i8042_check_port_owner(ps2dev->serio))
70181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov		i8042_unlock_chip();
71181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
72181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	mutex_unlock(&ps2dev->cmd_mutex);
73181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov}
74181d683d752c432635eda0f182ee71548c1f1820Dmitry TorokhovEXPORT_SYMBOL(ps2_end_command);
75181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
77c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * ps2_drain() waits for device to transmit requested number of bytes
78c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov * and discards them.
79c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov */
80c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov
81c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhovvoid ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
82c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov{
83c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	if (maxbytes > sizeof(ps2dev->cmdbuf)) {
84c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov		WARN_ON(1);
85c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov		maxbytes = sizeof(ps2dev->cmdbuf);
86c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	}
87c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov
88181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_begin_command(ps2dev);
89c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov
90c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	serio_pause_rx(ps2dev->serio);
91c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	ps2dev->flags = PS2_FLAG_CMD;
92c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	ps2dev->cmdcnt = maxbytes;
93c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	serio_continue_rx(ps2dev->serio);
94c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov
95c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	wait_event_timeout(ps2dev->wait,
96c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov			   !(ps2dev->flags & PS2_FLAG_CMD),
97c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov			   msecs_to_jiffies(timeout));
98181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov
99181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_end_command(ps2dev);
100c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov}
1015206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_drain);
102c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov
103c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov/*
104905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * ps2_is_keyboard_id() checks received ID byte against the list of
105905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * known keyboard IDs.
106905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov */
107905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
1089807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhovint ps2_is_keyboard_id(char id_byte)
109905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov{
110c5a69d57eb48e36f84c0737b5b24ec277d7dbfbaTobias Klauser	static const char keyboard_ids[] = {
111905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		0xab,	/* Regular keyboards		*/
112905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		0xac,	/* NCD Sun keyboard		*/
113905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		0x2b,	/* Trust keyboard, translated	*/
114905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		0x5d,	/* Trust keyboard		*/
115905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		0x60,	/* NMB SGI keyboard, translated */
116905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		0x47,	/* NMB SGI keyboard		*/
117905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov	};
118905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
119905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov	return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL;
120905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov}
1215206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_is_keyboard_id);
122905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
123905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov/*
124905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * ps2_adjust_timeout() is called after receiving 1st byte of command
125905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * response and tries to reduce remaining timeout to speed up command
126905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov * completion.
127905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov */
128905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
129905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhovstatic int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
130905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov{
131905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov	switch (command) {
132905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		case PS2_CMD_RESET_BAT:
133905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			/*
134905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * Device has sent the first response byte after
135905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * reset command, reset is thus done, so we can
136905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * shorten the timeout.
137905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * The next byte will come soon (keyboard) or not
138905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * at all (mouse).
139905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 */
140905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			if (timeout > msecs_to_jiffies(100))
141905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov				timeout = msecs_to_jiffies(100);
142905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			break;
143905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
144905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		case PS2_CMD_GETID:
145905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			/*
1469807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			 * Microsoft Natural Elite keyboard responds to
1479807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			 * the GET ID command as it were a mouse, with
1489807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			 * a single byte. Fail the command so atkbd will
1499807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			 * use alternative probe to detect it.
1509807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			 */
1519807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			if (ps2dev->cmdbuf[1] == 0xaa) {
1529807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov				serio_pause_rx(ps2dev->serio);
1539807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov				ps2dev->flags = 0;
1549807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov				serio_continue_rx(ps2dev->serio);
1559807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov				timeout = 0;
1569807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			}
1579807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov
1589807879bfdc0c2b5106b4b378f5475c6a333d853Dmitry Torokhov			/*
159905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * If device behind the port is not a keyboard there
160905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 * won't be 2nd byte of ID response.
161905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			 */
162905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			if (!ps2_is_keyboard_id(ps2dev->cmdbuf[1])) {
163905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov				serio_pause_rx(ps2dev->serio);
164905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov				ps2dev->flags = ps2dev->cmdcnt = 0;
165905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov				serio_continue_rx(ps2dev->serio);
166905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov				timeout = 0;
167905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			}
168905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			break;
169905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
170905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		default:
171905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov			break;
172905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov	}
173905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
174905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov	return timeout;
175905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov}
176905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov
177905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov/*
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_command() sends a command and its parameters to the mouse,
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * then waits for the response and puts it in the param array.
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_command() can only be called from a process context
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
184fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangint __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int timeout;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int send = (command >> 12) & 0xf;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int receive = (command >> 8) & 0xf;
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc = -1;
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
192c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	if (receive > sizeof(ps2dev->cmdbuf)) {
193c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov		WARN_ON(1);
194c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov		return -1;
195c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	}
196c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov
19795349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov	if (send && !param) {
19895349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov		WARN_ON(1);
19995349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov		return -1;
20095349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov	}
20195349fe8144b7d18f04bdca1c2d3fb85789de4fbDmitry Torokhov
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_pause_rx(ps2dev->serio);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->cmdcnt = receive;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (receive && param)
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < receive; i++)
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ps2dev->cmdbuf[(receive - 1) - i] = param[i];
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_continue_rx(ps2dev->serio);
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Some devices (Synaptics) peform the reset before
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * ACKing the reset command, and so it can take a long
21383ae417b857072d54c3c111067046283e3ee6b58Justin P. Mattock	 * time before the ACK arrives.
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
215c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov	if (ps2_sendbyte(ps2dev, command & 0xff,
216c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov			 command == PS2_CMD_RESET_BAT ? 1000 : 200))
217c611763d048990de5cdf848d97af6392f8fa7430Dmitry Torokhov		goto out;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < send; i++)
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ps2_sendbyte(ps2dev, param[i], 200))
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * The reset command takes a long time to execute.
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timeout = msecs_to_jiffies(command == PS2_CMD_RESET_BAT ? 4000 : 500);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timeout = wait_event_timeout(ps2dev->wait,
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     !(ps2dev->flags & PS2_FLAG_CMD1), timeout);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231a3ce6ea46cc0d6397d1b92b1a5983bb2935306edDmitry Torokhov	if (ps2dev->cmdcnt && !(ps2dev->flags & PS2_FLAG_CMD1)) {
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
233905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov		timeout = ps2_adjust_timeout(ps2dev, command, timeout);
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wait_event_timeout(ps2dev->wait,
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   !(ps2dev->flags & PS2_FLAG_CMD), timeout);
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (param)
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < receive; i++)
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			param[i] = ps2dev->cmdbuf[(receive - 1) - i];
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ps2dev->cmdcnt && (command != PS2_CMD_RESET_BAT || ps2dev->cmdcnt != 1))
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = 0;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
247905ab9d13694d0f75d1cb8c076ff2027538312ceDmitry Torokhov out:
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_pause_rx(ps2dev->serio);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->flags = 0;
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	serio_continue_rx(ps2dev->serio);
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
252fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	return rc;
253fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang}
254fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa LiangEXPORT_SYMBOL(__ps2_command);
255fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
256fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liangint ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
257fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang{
258fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	int rc;
259fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
260181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_begin_command(ps2dev);
261fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang	rc = __ps2_command(ps2dev, param, command);
262181d683d752c432635eda0f182ee71548c1f1820Dmitry Torokhov	ps2_end_command(ps2dev);
263fc69f4a6af49ee69475dc4217924d9edf77760e0Tai-hwa Liang
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return rc;
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2665206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_command);
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_init() initializes ps2dev structure
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid ps2_init(struct ps2dev *ps2dev, struct serio *serio)
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
274c4e32e9faaaa83340dbbc00e07c48d38f032b7dcArjan van de Ven	mutex_init(&ps2dev->cmd_mutex);
27588aa0103e408616e433c209e80169ab8d6eda99eJiri Kosina	lockdep_set_subclass(&ps2dev->cmd_mutex, serio->depth);
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&ps2dev->wait);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->serio = serio;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2795206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_init);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_handle_ack() is supposed to be used in interrupt handler
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to properly process ACK/NAK of a command from a PS/2 device.
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (data) {
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case PS2_RET_ACK:
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ps2dev->nak = 0;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case PS2_RET_NAK:
294a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov			ps2dev->flags |= PS2_FLAG_NAK;
295a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov			ps2dev->nak = PS2_RET_NAK;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
298a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov		case PS2_RET_ERR:
299a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov			if (ps2dev->flags & PS2_FLAG_NAK) {
300a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov				ps2dev->flags &= ~PS2_FLAG_NAK;
301a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov				ps2dev->nak = PS2_RET_ERR;
302a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov				break;
303a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov			}
304a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Workaround for mice which don't ACK the Get ID command.
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * These are valid mouse IDs that we recognize.
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x00:
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x03:
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x04:
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ps2dev->flags & PS2_FLAG_WAITID) {
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ps2dev->nak = 0;
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Fall through */
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
322a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov	if (!ps2dev->nak) {
323a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov		ps2dev->flags &= ~PS2_FLAG_NAK;
324a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov		if (ps2dev->cmdcnt)
325a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov			ps2dev->flags |= PS2_FLAG_CMD | PS2_FLAG_CMD1;
326a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov	}
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ps2dev->flags &= ~PS2_FLAG_ACK;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up(&ps2dev->wait);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (data != PS2_RET_ACK)
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ps2_handle_response(ps2dev, data);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 1;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3365206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_handle_ack);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ps2_handle_response() is supposed to be used in interrupt handler
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to properly store device's response to a command and notify process
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * waiting for completion of the command.
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ps2dev->cmdcnt)
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ps2dev->cmdbuf[--ps2dev->cmdcnt] = data;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ps2dev->flags & PS2_FLAG_CMD1) {
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ps2dev->flags &= ~PS2_FLAG_CMD1;
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ps2dev->cmdcnt)
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			wake_up(&ps2dev->wait);
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ps2dev->cmdcnt) {
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ps2dev->flags &= ~PS2_FLAG_CMD;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wake_up(&ps2dev->wait);
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 1;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3625206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_handle_response);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid ps2_cmd_aborted(struct ps2dev *ps2dev)
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ps2dev->flags & PS2_FLAG_ACK)
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ps2dev->nak = 1;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ps2dev->flags & (PS2_FLAG_ACK | PS2_FLAG_CMD))
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wake_up(&ps2dev->wait);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
372a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov	/* reset all flags except last nack */
373a2d781fc8d9b16113dd9440107d73c0f21d7cbefDmitry Torokhov	ps2dev->flags &= PS2_FLAG_NAK;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3755206c0d5ec514733dd098cf658d71327d199c7a0Dmitry TorokhovEXPORT_SYMBOL(ps2_cmd_aborted);
376