xref: /openbmc/linux/drivers/input/mouse/psmouse-base.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * PS/2 mouse driver
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (c) 1999-2002 Vojtech Pavlik
61da177e4SLinus Torvalds  * Copyright (c) 2003-2004 Dmitry Torokhov
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds 
10b5d21704SDmitry Torokhov #define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
11b5d21704SDmitry Torokhov #define psmouse_fmt(fmt)	fmt
12b5d21704SDmitry Torokhov 
13ba667650SDmitry Torokhov #include <linux/bitops.h>
141da177e4SLinus Torvalds #include <linux/delay.h>
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/slab.h>
171da177e4SLinus Torvalds #include <linux/interrupt.h>
181da177e4SLinus Torvalds #include <linux/input.h>
191da177e4SLinus Torvalds #include <linux/serio.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include <linux/libps2.h>
22c14471dcSIngo Molnar #include <linux/mutex.h>
23ba667650SDmitry Torokhov #include <linux/types.h>
24c14471dcSIngo Molnar 
251da177e4SLinus Torvalds #include "psmouse.h"
261da177e4SLinus Torvalds #include "synaptics.h"
271da177e4SLinus Torvalds #include "logips2pp.h"
281da177e4SLinus Torvalds #include "alps.h"
29df08ef27SAndres Salomon #include "hgpk.h"
3002d7f589SKenan Esau #include "lifebook.h"
31541e316aSStephen Evanchik #include "trackpoint.h"
3224bf10abSStefan Lucke #include "touchkit_ps2.h"
332a0bd75eSArjan Opmeer #include "elantech.h"
34fc69f4a6STai-hwa Liang #include "sentelic.h"
350799a924SDudley Du #include "cypress_ps2.h"
363ace3686SHans de Goede #include "focaltech.h"
378b8be51bSThomas Hellstrom #include "vmmouse.h"
3898ee3771SChris Diamand #include "byd.h"
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define DRIVER_DESC	"PS/2 mouse driver"
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
431da177e4SLinus Torvalds MODULE_DESCRIPTION(DRIVER_DESC);
441da177e4SLinus Torvalds MODULE_LICENSE("GPL");
451da177e4SLinus Torvalds 
46dbf4ccd6SDmitry Torokhov static unsigned int psmouse_max_proto = PSMOUSE_AUTO;
479bbb9e5aSRusty Russell static int psmouse_set_maxproto(const char *val, const struct kernel_param *);
489bbb9e5aSRusty Russell static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp);
499c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_proto_abbrev = {
509bbb9e5aSRusty Russell 	.set = psmouse_set_maxproto,
519bbb9e5aSRusty Russell 	.get = psmouse_get_maxproto,
529bbb9e5aSRusty Russell };
531da177e4SLinus Torvalds #define param_check_proto_abbrev(name, p)	__param_check(name, p, unsigned int)
541da177e4SLinus Torvalds module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644);
55dbf4ccd6SDmitry Torokhov MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches.");
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds static unsigned int psmouse_resolution = 200;
581da177e4SLinus Torvalds module_param_named(resolution, psmouse_resolution, uint, 0644);
591da177e4SLinus Torvalds MODULE_PARM_DESC(resolution, "Resolution, in dpi.");
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds static unsigned int psmouse_rate = 100;
621da177e4SLinus Torvalds module_param_named(rate, psmouse_rate, uint, 0644);
631da177e4SLinus Torvalds MODULE_PARM_DESC(rate, "Report rate, in reports per second.");
641da177e4SLinus Torvalds 
65feb9eba8SShailendra Verma static bool psmouse_smartscroll = true;
661da177e4SLinus Torvalds module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
671da177e4SLinus Torvalds MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");
681da177e4SLinus Torvalds 
694c711ef6SStephen Lyons static bool psmouse_a4tech_2wheels;
704c711ef6SStephen Lyons module_param_named(a4tech_workaround, psmouse_a4tech_2wheels, bool, 0644);
714c711ef6SStephen Lyons MODULE_PARM_DESC(a4tech_workaround, "A4Tech second scroll wheel workaround, 1 = enabled, 0 = disabled (default).");
724c711ef6SStephen Lyons 
73f0d5c6f4SDmitry Torokhov static unsigned int psmouse_resetafter = 5;
741da177e4SLinus Torvalds module_param_named(resetafter, psmouse_resetafter, uint, 0644);
751da177e4SLinus Torvalds MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
761da177e4SLinus Torvalds 
778bd0ee93SDmitry Torokhov static unsigned int psmouse_resync_time;
78f0d5c6f4SDmitry Torokhov module_param_named(resync_time, psmouse_resync_time, uint, 0644);
79f0d5c6f4SDmitry Torokhov MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never).");
80f0d5c6f4SDmitry Torokhov 
81cfe9e888SDmitry Torokhov PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO,
82cfe9e888SDmitry Torokhov 			NULL,
83cfe9e888SDmitry Torokhov 			psmouse_attr_show_protocol, psmouse_attr_set_protocol);
84cfe9e888SDmitry Torokhov PSMOUSE_DEFINE_ATTR(rate, S_IWUSR | S_IRUGO,
85cfe9e888SDmitry Torokhov 			(void *) offsetof(struct psmouse, rate),
86cfe9e888SDmitry Torokhov 			psmouse_show_int_attr, psmouse_attr_set_rate);
87cfe9e888SDmitry Torokhov PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO,
88cfe9e888SDmitry Torokhov 			(void *) offsetof(struct psmouse, resolution),
89cfe9e888SDmitry Torokhov 			psmouse_show_int_attr, psmouse_attr_set_resolution);
90cfe9e888SDmitry Torokhov PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO,
91cfe9e888SDmitry Torokhov 			(void *) offsetof(struct psmouse, resetafter),
92cfe9e888SDmitry Torokhov 			psmouse_show_int_attr, psmouse_set_int_attr);
93f0d5c6f4SDmitry Torokhov PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
94f0d5c6f4SDmitry Torokhov 			(void *) offsetof(struct psmouse, resync_time),
95f0d5c6f4SDmitry Torokhov 			psmouse_show_int_attr, psmouse_set_int_attr);
96cfe9e888SDmitry Torokhov 
97fd30a4baSDmitry Torokhov static struct attribute *psmouse_dev_attrs[] = {
98cfe9e888SDmitry Torokhov 	&psmouse_attr_protocol.dattr.attr,
99cfe9e888SDmitry Torokhov 	&psmouse_attr_rate.dattr.attr,
100cfe9e888SDmitry Torokhov 	&psmouse_attr_resolution.dattr.attr,
101cfe9e888SDmitry Torokhov 	&psmouse_attr_resetafter.dattr.attr,
102f0d5c6f4SDmitry Torokhov 	&psmouse_attr_resync_time.dattr.attr,
103cfe9e888SDmitry Torokhov 	NULL
104cfe9e888SDmitry Torokhov };
105cfe9e888SDmitry Torokhov 
106fd30a4baSDmitry Torokhov ATTRIBUTE_GROUPS(psmouse_dev);
1071da177e4SLinus Torvalds 
10804df1925SDmitry Torokhov /*
109c14471dcSIngo Molnar  * psmouse_mutex protects all operations changing state of mouse
11004df1925SDmitry Torokhov  * (connecting, disconnecting, changing rate or resolution via
11104df1925SDmitry Torokhov  * sysfs). We could use a per-device semaphore but since there
11204df1925SDmitry Torokhov  * rarely more than one PS/2 mouse connected and since semaphore
11304df1925SDmitry Torokhov  * is taken in "slow" paths it is not worth it.
11404df1925SDmitry Torokhov  */
115c14471dcSIngo Molnar static DEFINE_MUTEX(psmouse_mutex);
11604df1925SDmitry Torokhov 
117f0d5c6f4SDmitry Torokhov static struct workqueue_struct *kpsmoused_wq;
118f0d5c6f4SDmitry Torokhov 
psmouse_from_serio(struct serio * serio)119100e1695SDmitry Torokhov struct psmouse *psmouse_from_serio(struct serio *serio)
120100e1695SDmitry Torokhov {
121100e1695SDmitry Torokhov 	struct ps2dev *ps2dev = serio_get_drvdata(serio);
122100e1695SDmitry Torokhov 
123100e1695SDmitry Torokhov 	return container_of(ps2dev, struct psmouse, ps2dev);
124100e1695SDmitry Torokhov }
125100e1695SDmitry Torokhov 
psmouse_report_standard_buttons(struct input_dev * dev,u8 buttons)1261ef85805SDmitry Torokhov void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
12719ba1eb1SBenjamin Tissoires {
12819ba1eb1SBenjamin Tissoires 	input_report_key(dev, BTN_LEFT,   buttons & BIT(0));
12919ba1eb1SBenjamin Tissoires 	input_report_key(dev, BTN_MIDDLE, buttons & BIT(2));
13019ba1eb1SBenjamin Tissoires 	input_report_key(dev, BTN_RIGHT,  buttons & BIT(1));
13119ba1eb1SBenjamin Tissoires }
13219ba1eb1SBenjamin Tissoires 
psmouse_report_standard_motion(struct input_dev * dev,u8 * packet)1331ef85805SDmitry Torokhov void psmouse_report_standard_motion(struct input_dev *dev, u8 *packet)
1341ef85805SDmitry Torokhov {
1351ef85805SDmitry Torokhov 	int x, y;
1361ef85805SDmitry Torokhov 
1371ef85805SDmitry Torokhov 	x = packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0;
1381ef85805SDmitry Torokhov 	y = packet[2] ? packet[2] - ((packet[0] << 3) & 0x100) : 0;
1391ef85805SDmitry Torokhov 
1401ef85805SDmitry Torokhov 	input_report_rel(dev, REL_X, x);
1411ef85805SDmitry Torokhov 	input_report_rel(dev, REL_Y, -y);
1421ef85805SDmitry Torokhov }
1431ef85805SDmitry Torokhov 
psmouse_report_standard_packet(struct input_dev * dev,u8 * packet)1441ef85805SDmitry Torokhov void psmouse_report_standard_packet(struct input_dev *dev, u8 *packet)
1451ef85805SDmitry Torokhov {
1461ef85805SDmitry Torokhov 	psmouse_report_standard_buttons(dev, packet[0]);
1471ef85805SDmitry Torokhov 	psmouse_report_standard_motion(dev, packet);
1481ef85805SDmitry Torokhov }
1491ef85805SDmitry Torokhov 
1501da177e4SLinus Torvalds /*
1511da177e4SLinus Torvalds  * psmouse_process_byte() analyzes the PS/2 data stream and reports
1521da177e4SLinus Torvalds  * relevant events to the input module once full packet has arrived.
1531da177e4SLinus Torvalds  */
psmouse_process_byte(struct psmouse * psmouse)1547968a5ddSDaniel Drake psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
1551da177e4SLinus Torvalds {
1562e5b636bSDmitry Torokhov 	struct input_dev *dev = psmouse->dev;
157ba667650SDmitry Torokhov 	u8 *packet = psmouse->packet;
1584c711ef6SStephen Lyons 	int wheel;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	if (psmouse->pktcnt < psmouse->pktsize)
1611da177e4SLinus Torvalds 		return PSMOUSE_GOOD_DATA;
1621da177e4SLinus Torvalds 
1630a88d607SDmitry Torokhov 	/* Full packet accumulated, process it */
1641da177e4SLinus Torvalds 
165085fa80dSDmitry Torokhov 	switch (psmouse->protocol->type) {
1660a88d607SDmitry Torokhov 	case PSMOUSE_IMPS:
1670a88d607SDmitry Torokhov 		/* IntelliMouse has scroll wheel */
168ba667650SDmitry Torokhov 		input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
1690a88d607SDmitry Torokhov 		break;
1701da177e4SLinus Torvalds 
1710a88d607SDmitry Torokhov 	case PSMOUSE_IMEX:
1720a88d607SDmitry Torokhov 		/* Scroll wheel and buttons on IntelliMouse Explorer */
173b0c9ad8eSPozsar Balazs 		switch (packet[3] & 0xC0) {
174b0c9ad8eSPozsar Balazs 		case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
175ba667650SDmitry Torokhov 			input_report_rel(dev, REL_WHEEL,
176ba667650SDmitry Torokhov 					 -sign_extend32(packet[3], 5));
177b0c9ad8eSPozsar Balazs 			break;
178b0c9ad8eSPozsar Balazs 		case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
179ba667650SDmitry Torokhov 			input_report_rel(dev, REL_HWHEEL,
180ba667650SDmitry Torokhov 					 -sign_extend32(packet[3], 5));
181b0c9ad8eSPozsar Balazs 			break;
182b0c9ad8eSPozsar Balazs 		case 0x00:
183b0c9ad8eSPozsar Balazs 		case 0xC0:
1844c711ef6SStephen Lyons 			wheel = sign_extend32(packet[3], 3);
1854c711ef6SStephen Lyons 
1864c711ef6SStephen Lyons 			/*
1874c711ef6SStephen Lyons 			 * Some A4Tech mice have two scroll wheels, with first
1884c711ef6SStephen Lyons 			 * one reporting +/-1 in the lower nibble, and second
1894c711ef6SStephen Lyons 			 * one reporting +/-2.
1904c711ef6SStephen Lyons 			 */
1914c711ef6SStephen Lyons 			if (psmouse_a4tech_2wheels && abs(wheel) > 1)
1924c711ef6SStephen Lyons 				input_report_rel(dev, REL_HWHEEL, wheel / 2);
1934c711ef6SStephen Lyons 			else
1944c711ef6SStephen Lyons 				input_report_rel(dev, REL_WHEEL, -wheel);
1954c711ef6SStephen Lyons 
19603ae3a9cSDmitry Torokhov 			input_report_key(dev, BTN_SIDE,  packet[3] & BIT(4));
19703ae3a9cSDmitry Torokhov 			input_report_key(dev, BTN_EXTRA, packet[3] & BIT(5));
198b0c9ad8eSPozsar Balazs 			break;
199b0c9ad8eSPozsar Balazs 		}
2000a88d607SDmitry Torokhov 		break;
2011da177e4SLinus Torvalds 
2020a88d607SDmitry Torokhov 	case PSMOUSE_GENPS:
2030a88d607SDmitry Torokhov 		/* Report scroll buttons on NetMice */
204ba667650SDmitry Torokhov 		input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
2051da177e4SLinus Torvalds 
2060a88d607SDmitry Torokhov 		/* Extra buttons on Genius NewNet 3D */
20703ae3a9cSDmitry Torokhov 		input_report_key(dev, BTN_SIDE,  packet[0] & BIT(6));
20803ae3a9cSDmitry Torokhov 		input_report_key(dev, BTN_EXTRA, packet[0] & BIT(7));
2090a88d607SDmitry Torokhov 		break;
2101da177e4SLinus Torvalds 
2110a88d607SDmitry Torokhov 	case PSMOUSE_THINKPS:
2120a88d607SDmitry Torokhov 		/* Extra button on ThinkingMouse */
21303ae3a9cSDmitry Torokhov 		input_report_key(dev, BTN_EXTRA, packet[0] & BIT(3));
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 		/*
2160a88d607SDmitry Torokhov 		 * Without this bit of weirdness moving up gives wildly
2170a88d607SDmitry Torokhov 		 * high Y changes.
218aea6a461SAristeu Rozanski 		 */
2190a88d607SDmitry Torokhov 		packet[1] |= (packet[0] & 0x40) << 1;
2200a88d607SDmitry Torokhov 		break;
2210a88d607SDmitry Torokhov 
2220a88d607SDmitry Torokhov 	case PSMOUSE_CORTRON:
2230a88d607SDmitry Torokhov 		/*
2240a88d607SDmitry Torokhov 		 * Cortron PS2 Trackball reports SIDE button in the
2250a88d607SDmitry Torokhov 		 * 4th bit of the first byte.
2260a88d607SDmitry Torokhov 		 */
22703ae3a9cSDmitry Torokhov 		input_report_key(dev, BTN_SIDE, packet[0] & BIT(3));
228ba667650SDmitry Torokhov 		packet[0] |= BIT(3);
2290a88d607SDmitry Torokhov 		break;
2300a88d607SDmitry Torokhov 
2310a88d607SDmitry Torokhov 	default:
2320a88d607SDmitry Torokhov 		break;
233aea6a461SAristeu Rozanski 	}
234aea6a461SAristeu Rozanski 
2350a88d607SDmitry Torokhov 	/* Generic PS/2 Mouse */
2361ef85805SDmitry Torokhov 	packet[0] |= psmouse->extra_buttons;
2371ef85805SDmitry Torokhov 	psmouse_report_standard_packet(dev, packet);
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds 	input_sync(dev);
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	return PSMOUSE_FULL_PACKET;
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
psmouse_queue_work(struct psmouse * psmouse,struct delayed_work * work,unsigned long delay)2448bf020eeSAndres Salomon void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
2458bf020eeSAndres Salomon 		unsigned long delay)
2468bf020eeSAndres Salomon {
2478bf020eeSAndres Salomon 	queue_delayed_work(kpsmoused_wq, work, delay);
2488bf020eeSAndres Salomon }
2498bf020eeSAndres Salomon 
2501da177e4SLinus Torvalds /*
251f0d5c6f4SDmitry Torokhov  * __psmouse_set_state() sets new psmouse state and resets all flags.
252f0d5c6f4SDmitry Torokhov  */
__psmouse_set_state(struct psmouse * psmouse,enum psmouse_state new_state)253f0d5c6f4SDmitry Torokhov static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
254f0d5c6f4SDmitry Torokhov {
255f0d5c6f4SDmitry Torokhov 	psmouse->state = new_state;
256b7802c5cSDmitry Torokhov 	psmouse->pktcnt = psmouse->out_of_sync_cnt = 0;
257f0d5c6f4SDmitry Torokhov 	psmouse->ps2dev.flags = 0;
258f0d5c6f4SDmitry Torokhov 	psmouse->last = jiffies;
259f0d5c6f4SDmitry Torokhov }
260f0d5c6f4SDmitry Torokhov 
261f0d5c6f4SDmitry Torokhov /*
262f0d5c6f4SDmitry Torokhov  * psmouse_set_state() sets new psmouse state and resets all flags and
263f0d5c6f4SDmitry Torokhov  * counters while holding serio lock so fighting with interrupt handler
264f0d5c6f4SDmitry Torokhov  * is not a concern.
265f0d5c6f4SDmitry Torokhov  */
psmouse_set_state(struct psmouse * psmouse,enum psmouse_state new_state)266a48cf5f3SAndres Salomon void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
267f0d5c6f4SDmitry Torokhov {
268f0d5c6f4SDmitry Torokhov 	serio_pause_rx(psmouse->ps2dev.serio);
269f0d5c6f4SDmitry Torokhov 	__psmouse_set_state(psmouse, new_state);
270f0d5c6f4SDmitry Torokhov 	serio_continue_rx(psmouse->ps2dev.serio);
271f0d5c6f4SDmitry Torokhov }
272f0d5c6f4SDmitry Torokhov 
273f0d5c6f4SDmitry Torokhov /*
274f0d5c6f4SDmitry Torokhov  * psmouse_handle_byte() processes one byte of the input data stream
275f0d5c6f4SDmitry Torokhov  * by calling corresponding protocol handler.
276f0d5c6f4SDmitry Torokhov  */
psmouse_handle_byte(struct psmouse * psmouse)2777d12e780SDavid Howells static int psmouse_handle_byte(struct psmouse *psmouse)
278f0d5c6f4SDmitry Torokhov {
2797d12e780SDavid Howells 	psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
280f0d5c6f4SDmitry Torokhov 
281f0d5c6f4SDmitry Torokhov 	switch (rc) {
282f0d5c6f4SDmitry Torokhov 	case PSMOUSE_BAD_DATA:
283f0d5c6f4SDmitry Torokhov 		if (psmouse->state == PSMOUSE_ACTIVATED) {
284b5d21704SDmitry Torokhov 			psmouse_warn(psmouse,
285b5d21704SDmitry Torokhov 				     "%s at %s lost sync at byte %d\n",
286b5d21704SDmitry Torokhov 				     psmouse->name, psmouse->phys,
287b5d21704SDmitry Torokhov 				     psmouse->pktcnt);
288b7802c5cSDmitry Torokhov 			if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {
289f0d5c6f4SDmitry Torokhov 				__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
290b5d21704SDmitry Torokhov 				psmouse_notice(psmouse,
291b5d21704SDmitry Torokhov 						"issuing reconnect request\n");
292f0d5c6f4SDmitry Torokhov 				serio_reconnect(psmouse->ps2dev.serio);
293ba667650SDmitry Torokhov 				return -EIO;
294f0d5c6f4SDmitry Torokhov 			}
295f0d5c6f4SDmitry Torokhov 		}
296f0d5c6f4SDmitry Torokhov 		psmouse->pktcnt = 0;
297f0d5c6f4SDmitry Torokhov 		break;
298f0d5c6f4SDmitry Torokhov 
299f0d5c6f4SDmitry Torokhov 	case PSMOUSE_FULL_PACKET:
300f0d5c6f4SDmitry Torokhov 		psmouse->pktcnt = 0;
301b7802c5cSDmitry Torokhov 		if (psmouse->out_of_sync_cnt) {
302b7802c5cSDmitry Torokhov 			psmouse->out_of_sync_cnt = 0;
303b5d21704SDmitry Torokhov 			psmouse_notice(psmouse,
304b5d21704SDmitry Torokhov 					"%s at %s - driver resynced.\n",
305f0d5c6f4SDmitry Torokhov 					psmouse->name, psmouse->phys);
306f0d5c6f4SDmitry Torokhov 		}
307f0d5c6f4SDmitry Torokhov 		break;
308f0d5c6f4SDmitry Torokhov 
309f0d5c6f4SDmitry Torokhov 	case PSMOUSE_GOOD_DATA:
310f0d5c6f4SDmitry Torokhov 		break;
311f0d5c6f4SDmitry Torokhov 	}
312f0d5c6f4SDmitry Torokhov 	return 0;
313f0d5c6f4SDmitry Torokhov }
314f0d5c6f4SDmitry Torokhov 
psmouse_handle_oob_data(struct psmouse * psmouse,u8 data)31519ba1eb1SBenjamin Tissoires static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data)
31619ba1eb1SBenjamin Tissoires {
31719ba1eb1SBenjamin Tissoires 	switch (psmouse->oob_data_type) {
31819ba1eb1SBenjamin Tissoires 	case PSMOUSE_OOB_NONE:
31919ba1eb1SBenjamin Tissoires 		psmouse->oob_data_type = data;
32019ba1eb1SBenjamin Tissoires 		break;
32119ba1eb1SBenjamin Tissoires 
32219ba1eb1SBenjamin Tissoires 	case PSMOUSE_OOB_EXTRA_BTNS:
32319ba1eb1SBenjamin Tissoires 		psmouse_report_standard_buttons(psmouse->dev, data);
32419ba1eb1SBenjamin Tissoires 		input_sync(psmouse->dev);
32519ba1eb1SBenjamin Tissoires 
32619ba1eb1SBenjamin Tissoires 		psmouse->extra_buttons = data;
32719ba1eb1SBenjamin Tissoires 		psmouse->oob_data_type = PSMOUSE_OOB_NONE;
32819ba1eb1SBenjamin Tissoires 		break;
32919ba1eb1SBenjamin Tissoires 
33019ba1eb1SBenjamin Tissoires 	default:
33119ba1eb1SBenjamin Tissoires 		psmouse_warn(psmouse,
33219ba1eb1SBenjamin Tissoires 			     "unknown OOB_DATA type: 0x%02x\n",
33319ba1eb1SBenjamin Tissoires 			     psmouse->oob_data_type);
33419ba1eb1SBenjamin Tissoires 		psmouse->oob_data_type = PSMOUSE_OOB_NONE;
33519ba1eb1SBenjamin Tissoires 		break;
33619ba1eb1SBenjamin Tissoires 	}
33719ba1eb1SBenjamin Tissoires }
33819ba1eb1SBenjamin Tissoires 
psmouse_pre_receive_byte(struct ps2dev * ps2dev,u8 data,unsigned int flags)339*c4c7eac8SDmitry Torokhov static enum ps2_disposition psmouse_pre_receive_byte(struct ps2dev *ps2dev,
340*c4c7eac8SDmitry Torokhov 						     u8 data,
341*c4c7eac8SDmitry Torokhov 						     unsigned int flags)
3421da177e4SLinus Torvalds {
343*c4c7eac8SDmitry Torokhov 	struct psmouse *psmouse = container_of(ps2dev, struct psmouse, ps2dev);
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 	if (psmouse->state == PSMOUSE_IGNORE)
346*c4c7eac8SDmitry Torokhov 		return PS2_IGNORE;
3471da177e4SLinus Torvalds 
3486b9d363cSDmitry Torokhov 	if (unlikely((flags & SERIO_TIMEOUT) ||
349085fa80dSDmitry Torokhov 		     ((flags & SERIO_PARITY) &&
350085fa80dSDmitry Torokhov 		      !psmouse->protocol->ignore_parity))) {
3516b9d363cSDmitry Torokhov 
3521da177e4SLinus Torvalds 		if (psmouse->state == PSMOUSE_ACTIVATED)
353b5d21704SDmitry Torokhov 			psmouse_warn(psmouse,
354b5d21704SDmitry Torokhov 				     "bad data from KBC -%s%s\n",
3551da177e4SLinus Torvalds 				     flags & SERIO_TIMEOUT ? " timeout" : "",
3561da177e4SLinus Torvalds 				     flags & SERIO_PARITY ? " bad parity" : "");
357*c4c7eac8SDmitry Torokhov 		return PS2_ERROR;
3581da177e4SLinus Torvalds 	}
3591da177e4SLinus Torvalds 
36019ba1eb1SBenjamin Tissoires 	if (flags & SERIO_OOB_DATA) {
36119ba1eb1SBenjamin Tissoires 		psmouse_handle_oob_data(psmouse, data);
362*c4c7eac8SDmitry Torokhov 		return PS2_IGNORE;
36319ba1eb1SBenjamin Tissoires 	}
36419ba1eb1SBenjamin Tissoires 
365*c4c7eac8SDmitry Torokhov 	return PS2_PROCESS;
366*c4c7eac8SDmitry Torokhov }
3671da177e4SLinus Torvalds 
psmouse_receive_byte(struct ps2dev * ps2dev,u8 data)368*c4c7eac8SDmitry Torokhov static void psmouse_receive_byte(struct ps2dev *ps2dev, u8 data)
369*c4c7eac8SDmitry Torokhov {
370*c4c7eac8SDmitry Torokhov 	struct psmouse *psmouse = container_of(ps2dev, struct psmouse, ps2dev);
3711da177e4SLinus Torvalds 
372*c4c7eac8SDmitry Torokhov 	pm_wakeup_event(&ps2dev->serio->dev, 0);
373aaca981eSDmitry Torokhov 
374f0d5c6f4SDmitry Torokhov 	if (psmouse->state <= PSMOUSE_RESYNCING)
375*c4c7eac8SDmitry Torokhov 		return;
3761da177e4SLinus Torvalds 
3771da177e4SLinus Torvalds 	if (psmouse->state == PSMOUSE_ACTIVATED &&
3781da177e4SLinus Torvalds 	    psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) {
379b5d21704SDmitry Torokhov 		psmouse_info(psmouse, "%s at %s lost synchronization, throwing %d bytes away.\n",
3801da177e4SLinus Torvalds 			     psmouse->name, psmouse->phys, psmouse->pktcnt);
381f0d5c6f4SDmitry Torokhov 		psmouse->badbyte = psmouse->packet[0];
382f0d5c6f4SDmitry Torokhov 		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
3838bf020eeSAndres Salomon 		psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
384*c4c7eac8SDmitry Torokhov 		return;
3851da177e4SLinus Torvalds 	}
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	psmouse->packet[psmouse->pktcnt++] = data;
388ad530771SDmitry Torokhov 
389ad530771SDmitry Torokhov 	/* Check if this is a new device announcement (0xAA 0x00) */
390f0d5c6f4SDmitry Torokhov 	if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
39189c9b480SDmitry Torokhov 		if (psmouse->pktcnt == 1) {
39289c9b480SDmitry Torokhov 			psmouse->last = jiffies;
393*c4c7eac8SDmitry Torokhov 			return;
39489c9b480SDmitry Torokhov 		}
3951da177e4SLinus Torvalds 
396535650fdSZephaniah E. Hull 		if (psmouse->packet[1] == PSMOUSE_RET_ID ||
397085fa80dSDmitry Torokhov 		    (psmouse->protocol->type == PSMOUSE_HGPK &&
398535650fdSZephaniah E. Hull 		     psmouse->packet[1] == PSMOUSE_RET_BAT)) {
399f0d5c6f4SDmitry Torokhov 			__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
400*c4c7eac8SDmitry Torokhov 			serio_reconnect(ps2dev->serio);
401*c4c7eac8SDmitry Torokhov 			return;
4021da177e4SLinus Torvalds 		}
403ad530771SDmitry Torokhov 
404ad530771SDmitry Torokhov 		/* Not a new device, try processing first byte normally */
405f0d5c6f4SDmitry Torokhov 		psmouse->pktcnt = 1;
4067d12e780SDavid Howells 		if (psmouse_handle_byte(psmouse))
407*c4c7eac8SDmitry Torokhov 			return;
408f0d5c6f4SDmitry Torokhov 
409f0d5c6f4SDmitry Torokhov 		psmouse->packet[psmouse->pktcnt++] = data;
410f0d5c6f4SDmitry Torokhov 	}
411f0d5c6f4SDmitry Torokhov 
412f0d5c6f4SDmitry Torokhov 	/*
413ad530771SDmitry Torokhov 	 * See if we need to force resync because mouse was idle for
414ad530771SDmitry Torokhov 	 * too long.
415f0d5c6f4SDmitry Torokhov 	 */
416f0d5c6f4SDmitry Torokhov 	if (psmouse->state == PSMOUSE_ACTIVATED &&
417f0d5c6f4SDmitry Torokhov 	    psmouse->pktcnt == 1 && psmouse->resync_time &&
418f0d5c6f4SDmitry Torokhov 	    time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
419f0d5c6f4SDmitry Torokhov 		psmouse->badbyte = psmouse->packet[0];
420f0d5c6f4SDmitry Torokhov 		__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
4218bf020eeSAndres Salomon 		psmouse_queue_work(psmouse, &psmouse->resync_work, 0);
422*c4c7eac8SDmitry Torokhov 		return;
4231da177e4SLinus Torvalds 	}
4241da177e4SLinus Torvalds 
425f0d5c6f4SDmitry Torokhov 	psmouse->last = jiffies;
4267d12e780SDavid Howells 	psmouse_handle_byte(psmouse);
4271da177e4SLinus Torvalds }
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds /*
4301da177e4SLinus Torvalds  * psmouse_reset() resets the mouse into power-on state.
4311da177e4SLinus Torvalds  */
psmouse_reset(struct psmouse * psmouse)4321da177e4SLinus Torvalds int psmouse_reset(struct psmouse *psmouse)
4331da177e4SLinus Torvalds {
434ba667650SDmitry Torokhov 	u8 param[2];
435ba667650SDmitry Torokhov 	int error;
4361da177e4SLinus Torvalds 
437ba667650SDmitry Torokhov 	error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT);
438ba667650SDmitry Torokhov 	if (error)
439ba667650SDmitry Torokhov 		return error;
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID)
442ba667650SDmitry Torokhov 		return -EIO;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	return 0;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds 
447ee9dfd7aSDmitry Torokhov /*
448ee9dfd7aSDmitry Torokhov  * Here we set the mouse resolution.
449ee9dfd7aSDmitry Torokhov  */
psmouse_set_resolution(struct psmouse * psmouse,unsigned int resolution)450ee9dfd7aSDmitry Torokhov void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
451ee9dfd7aSDmitry Torokhov {
452ba667650SDmitry Torokhov 	static const u8 params[] = { 0, 1, 2, 2, 3 };
453ba667650SDmitry Torokhov 	u8 p;
454ee9dfd7aSDmitry Torokhov 
455ee9dfd7aSDmitry Torokhov 	if (resolution == 0 || resolution > 200)
456ee9dfd7aSDmitry Torokhov 		resolution = 200;
457ee9dfd7aSDmitry Torokhov 
458ee9dfd7aSDmitry Torokhov 	p = params[resolution / 50];
459ee9dfd7aSDmitry Torokhov 	ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
460ee9dfd7aSDmitry Torokhov 	psmouse->resolution = 25 << p;
461ee9dfd7aSDmitry Torokhov }
462ee9dfd7aSDmitry Torokhov 
463ee9dfd7aSDmitry Torokhov /*
464ee9dfd7aSDmitry Torokhov  * Here we set the mouse report rate.
465ee9dfd7aSDmitry Torokhov  */
psmouse_set_rate(struct psmouse * psmouse,unsigned int rate)466ee9dfd7aSDmitry Torokhov static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
467ee9dfd7aSDmitry Torokhov {
468ba667650SDmitry Torokhov 	static const u8 rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
469ba667650SDmitry Torokhov 	u8 r;
470ee9dfd7aSDmitry Torokhov 	int i = 0;
471ee9dfd7aSDmitry Torokhov 
472ba667650SDmitry Torokhov 	while (rates[i] > rate)
473ba667650SDmitry Torokhov 		i++;
474ee9dfd7aSDmitry Torokhov 	r = rates[i];
475ee9dfd7aSDmitry Torokhov 	ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
476ee9dfd7aSDmitry Torokhov 	psmouse->rate = r;
477ee9dfd7aSDmitry Torokhov }
478ee9dfd7aSDmitry Torokhov 
479ee9dfd7aSDmitry Torokhov /*
4804ec212f0SMathias Gottschlag  * Here we set the mouse scaling.
4814ec212f0SMathias Gottschlag  */
psmouse_set_scale(struct psmouse * psmouse,enum psmouse_scale scale)4824ec212f0SMathias Gottschlag static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
4834ec212f0SMathias Gottschlag {
4844ec212f0SMathias Gottschlag 	ps2_command(&psmouse->ps2dev, NULL,
4854ec212f0SMathias Gottschlag 		    scale == PSMOUSE_SCALE21 ? PSMOUSE_CMD_SETSCALE21 :
4864ec212f0SMathias Gottschlag 					       PSMOUSE_CMD_SETSCALE11);
4874ec212f0SMathias Gottschlag }
4884ec212f0SMathias Gottschlag 
4894ec212f0SMathias Gottschlag /*
490ee9dfd7aSDmitry Torokhov  * psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
491ee9dfd7aSDmitry Torokhov  */
psmouse_poll(struct psmouse * psmouse)492ee9dfd7aSDmitry Torokhov static int psmouse_poll(struct psmouse *psmouse)
493ee9dfd7aSDmitry Torokhov {
494ee9dfd7aSDmitry Torokhov 	return ps2_command(&psmouse->ps2dev, psmouse->packet,
495ee9dfd7aSDmitry Torokhov 			   PSMOUSE_CMD_POLL | (psmouse->pktsize << 8));
496ee9dfd7aSDmitry Torokhov }
497ee9dfd7aSDmitry Torokhov 
psmouse_check_pnp_id(const char * id,const char * const ids[])49899e14c1eSDmitry Torokhov static bool psmouse_check_pnp_id(const char *id, const char * const ids[])
49999e14c1eSDmitry Torokhov {
50099e14c1eSDmitry Torokhov 	int i;
50199e14c1eSDmitry Torokhov 
50299e14c1eSDmitry Torokhov 	for (i = 0; ids[i]; i++)
50399e14c1eSDmitry Torokhov 		if (!strcasecmp(id, ids[i]))
50499e14c1eSDmitry Torokhov 			return true;
50599e14c1eSDmitry Torokhov 
50699e14c1eSDmitry Torokhov 	return false;
50799e14c1eSDmitry Torokhov }
50899e14c1eSDmitry Torokhov 
5092c75ada6SHans de Goede /*
5102c75ada6SHans de Goede  * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids.
5112c75ada6SHans de Goede  */
psmouse_matches_pnp_id(struct psmouse * psmouse,const char * const ids[])5122c75ada6SHans de Goede bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[])
5132c75ada6SHans de Goede {
51499e14c1eSDmitry Torokhov 	struct serio *serio = psmouse->ps2dev.serio;
51599e14c1eSDmitry Torokhov 	char *p, *fw_id_copy, *save_ptr;
51699e14c1eSDmitry Torokhov 	bool found = false;
5172c75ada6SHans de Goede 
51899e14c1eSDmitry Torokhov 	if (strncmp(serio->firmware_id, "PNP: ", 5))
5192c75ada6SHans de Goede 		return false;
52099e14c1eSDmitry Torokhov 
52199e14c1eSDmitry Torokhov 	fw_id_copy = kstrndup(&serio->firmware_id[5],
52299e14c1eSDmitry Torokhov 			      sizeof(serio->firmware_id) - 5,
52399e14c1eSDmitry Torokhov 			      GFP_KERNEL);
52499e14c1eSDmitry Torokhov 	if (!fw_id_copy)
52599e14c1eSDmitry Torokhov 		return false;
52699e14c1eSDmitry Torokhov 
52799e14c1eSDmitry Torokhov 	save_ptr = fw_id_copy;
52899e14c1eSDmitry Torokhov 	while ((p = strsep(&fw_id_copy, " ")) != NULL) {
52999e14c1eSDmitry Torokhov 		if (psmouse_check_pnp_id(p, ids)) {
53099e14c1eSDmitry Torokhov 			found = true;
53199e14c1eSDmitry Torokhov 			break;
53299e14c1eSDmitry Torokhov 		}
53399e14c1eSDmitry Torokhov 	}
53499e14c1eSDmitry Torokhov 
53599e14c1eSDmitry Torokhov 	kfree(save_ptr);
53699e14c1eSDmitry Torokhov 	return found;
5372c75ada6SHans de Goede }
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds /*
5401da177e4SLinus Torvalds  * Genius NetMouse magic init.
5411da177e4SLinus Torvalds  */
genius_detect(struct psmouse * psmouse,bool set_properties)542b7802c5cSDmitry Torokhov static int genius_detect(struct psmouse *psmouse, bool set_properties)
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
545ba667650SDmitry Torokhov 	u8 param[4];
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 	param[0] = 3;
5481da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
5491da177e4SLinus Torvalds 	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
5501da177e4SLinus Torvalds 	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
5511da177e4SLinus Torvalds 	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
5521da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55)
555ba667650SDmitry Torokhov 		return -ENODEV;
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds 	if (set_properties) {
558315eb996SDmitry Torokhov 		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
559b7802c5cSDmitry Torokhov 		__set_bit(BTN_EXTRA, psmouse->dev->keybit);
560b7802c5cSDmitry Torokhov 		__set_bit(BTN_SIDE, psmouse->dev->keybit);
561b7802c5cSDmitry Torokhov 		__set_bit(REL_WHEEL, psmouse->dev->relbit);
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 		psmouse->vendor = "Genius";
564a3f3f317SDmitry Torokhov 		psmouse->name = "Mouse";
5651da177e4SLinus Torvalds 		psmouse->pktsize = 4;
5661da177e4SLinus Torvalds 	}
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	return 0;
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds /*
5721da177e4SLinus Torvalds  * IntelliMouse magic init.
5731da177e4SLinus Torvalds  */
intellimouse_detect(struct psmouse * psmouse,bool set_properties)574b7802c5cSDmitry Torokhov static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
5751da177e4SLinus Torvalds {
5761da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
577ba667650SDmitry Torokhov 	u8 param[2];
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds 	param[0] = 200;
5801da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
5811da177e4SLinus Torvalds 	param[0] = 100;
5821da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
5831da177e4SLinus Torvalds 	param[0] =  80;
5841da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
5851da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 	if (param[0] != 3)
588ba667650SDmitry Torokhov 		return -ENODEV;
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 	if (set_properties) {
591b7802c5cSDmitry Torokhov 		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
592b7802c5cSDmitry Torokhov 		__set_bit(REL_WHEEL, psmouse->dev->relbit);
5931da177e4SLinus Torvalds 
594315eb996SDmitry Torokhov 		if (!psmouse->vendor)
595315eb996SDmitry Torokhov 			psmouse->vendor = "Generic";
596315eb996SDmitry Torokhov 		if (!psmouse->name)
597315eb996SDmitry Torokhov 			psmouse->name = "Wheel Mouse";
5981da177e4SLinus Torvalds 		psmouse->pktsize = 4;
5991da177e4SLinus Torvalds 	}
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds 	return 0;
6021da177e4SLinus Torvalds }
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds /*
6051da177e4SLinus Torvalds  * Try IntelliMouse/Explorer magic init.
6061da177e4SLinus Torvalds  */
im_explorer_detect(struct psmouse * psmouse,bool set_properties)607b7802c5cSDmitry Torokhov static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
6081da177e4SLinus Torvalds {
6091da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
610ba667650SDmitry Torokhov 	u8 param[2];
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	intellimouse_detect(psmouse, 0);
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	param[0] = 200;
6151da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
6161da177e4SLinus Torvalds 	param[0] = 200;
6171da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
6181da177e4SLinus Torvalds 	param[0] =  80;
6191da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
6201da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 	if (param[0] != 4)
623ba667650SDmitry Torokhov 		return -ENODEV;
6241da177e4SLinus Torvalds 
625b0c9ad8eSPozsar Balazs 	/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
626b0c9ad8eSPozsar Balazs 	param[0] = 200;
627b0c9ad8eSPozsar Balazs 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
628b0c9ad8eSPozsar Balazs 	param[0] =  80;
629b0c9ad8eSPozsar Balazs 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
630b0c9ad8eSPozsar Balazs 	param[0] =  40;
631b0c9ad8eSPozsar Balazs 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
632b0c9ad8eSPozsar Balazs 
6331da177e4SLinus Torvalds 	if (set_properties) {
634b7802c5cSDmitry Torokhov 		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
635b7802c5cSDmitry Torokhov 		__set_bit(REL_WHEEL, psmouse->dev->relbit);
636b7802c5cSDmitry Torokhov 		__set_bit(REL_HWHEEL, psmouse->dev->relbit);
637b7802c5cSDmitry Torokhov 		__set_bit(BTN_SIDE, psmouse->dev->keybit);
638b7802c5cSDmitry Torokhov 		__set_bit(BTN_EXTRA, psmouse->dev->keybit);
6391da177e4SLinus Torvalds 
640315eb996SDmitry Torokhov 		if (!psmouse->vendor)
641315eb996SDmitry Torokhov 			psmouse->vendor = "Generic";
642315eb996SDmitry Torokhov 		if (!psmouse->name)
643315eb996SDmitry Torokhov 			psmouse->name = "Explorer Mouse";
6441da177e4SLinus Torvalds 		psmouse->pktsize = 4;
6451da177e4SLinus Torvalds 	}
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds 	return 0;
6481da177e4SLinus Torvalds }
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds /*
6511da177e4SLinus Torvalds  * Kensington ThinkingMouse / ExpertMouse magic init.
6521da177e4SLinus Torvalds  */
thinking_detect(struct psmouse * psmouse,bool set_properties)653b7802c5cSDmitry Torokhov static int thinking_detect(struct psmouse *psmouse, bool set_properties)
6541da177e4SLinus Torvalds {
6551da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
656ba667650SDmitry Torokhov 	u8 param[2];
657ba667650SDmitry Torokhov 	static const u8 seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
6581da177e4SLinus Torvalds 	int i;
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds 	param[0] = 10;
6611da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
6621da177e4SLinus Torvalds 	param[0] = 0;
6631da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
664e38de678SHelge Deller 	for (i = 0; i < ARRAY_SIZE(seq); i++) {
665e38de678SHelge Deller 		param[0] = seq[i];
666e38de678SHelge Deller 		ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
667e38de678SHelge Deller 	}
6681da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds 	if (param[0] != 2)
671ba667650SDmitry Torokhov 		return -ENODEV;
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 	if (set_properties) {
674315eb996SDmitry Torokhov 		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
675b7802c5cSDmitry Torokhov 		__set_bit(BTN_EXTRA, psmouse->dev->keybit);
6761da177e4SLinus Torvalds 
6771da177e4SLinus Torvalds 		psmouse->vendor = "Kensington";
6781da177e4SLinus Torvalds 		psmouse->name = "ThinkingMouse";
6791da177e4SLinus Torvalds 	}
6801da177e4SLinus Torvalds 
6811da177e4SLinus Torvalds 	return 0;
6821da177e4SLinus Torvalds }
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds /*
6851da177e4SLinus Torvalds  * Bare PS/2 protocol "detection". Always succeeds.
6861da177e4SLinus Torvalds  */
ps2bare_detect(struct psmouse * psmouse,bool set_properties)687b7802c5cSDmitry Torokhov static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
6881da177e4SLinus Torvalds {
689dbf4ccd6SDmitry Torokhov 	if (set_properties) {
690315eb996SDmitry Torokhov 		if (!psmouse->vendor)
691315eb996SDmitry Torokhov 			psmouse->vendor = "Generic";
692315eb996SDmitry Torokhov 		if (!psmouse->name)
693315eb996SDmitry Torokhov 			psmouse->name = "Mouse";
694315eb996SDmitry Torokhov 
695315eb996SDmitry Torokhov 		/*
696315eb996SDmitry Torokhov 		 * We have no way of figuring true number of buttons so let's
697315eb996SDmitry Torokhov 		 * assume that the device has 3.
698315eb996SDmitry Torokhov 		 */
699ba667650SDmitry Torokhov 		input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
700dbf4ccd6SDmitry Torokhov 	}
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	return 0;
7031da177e4SLinus Torvalds }
7041da177e4SLinus Torvalds 
705aea6a461SAristeu Rozanski /*
706aea6a461SAristeu Rozanski  * Cortron PS/2 protocol detection. There's no special way to detect it, so it
707aea6a461SAristeu Rozanski  * must be forced by sysfs protocol writing.
708aea6a461SAristeu Rozanski  */
cortron_detect(struct psmouse * psmouse,bool set_properties)709b7802c5cSDmitry Torokhov static int cortron_detect(struct psmouse *psmouse, bool set_properties)
710aea6a461SAristeu Rozanski {
711aea6a461SAristeu Rozanski 	if (set_properties) {
712aea6a461SAristeu Rozanski 		psmouse->vendor = "Cortron";
713aea6a461SAristeu Rozanski 		psmouse->name = "PS/2 Trackball";
714315eb996SDmitry Torokhov 
715315eb996SDmitry Torokhov 		__set_bit(BTN_MIDDLE, psmouse->dev->keybit);
716b7802c5cSDmitry Torokhov 		__set_bit(BTN_SIDE, psmouse->dev->keybit);
717aea6a461SAristeu Rozanski 	}
718aea6a461SAristeu Rozanski 
719aea6a461SAristeu Rozanski 	return 0;
720aea6a461SAristeu Rozanski }
721dbf4ccd6SDmitry Torokhov 
7225fa75cfeSDmitry Torokhov static const struct psmouse_protocol psmouse_protocols[] = {
7235fa75cfeSDmitry Torokhov 	{
7245fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_PS2,
7255fa75cfeSDmitry Torokhov 		.name		= "PS/2",
7265fa75cfeSDmitry Torokhov 		.alias		= "bare",
7275fa75cfeSDmitry Torokhov 		.maxproto	= true,
7285fa75cfeSDmitry Torokhov 		.ignore_parity	= true,
7295fa75cfeSDmitry Torokhov 		.detect		= ps2bare_detect,
730ec6184b1SDmitry Torokhov 		.try_passthru	= true,
7315fa75cfeSDmitry Torokhov 	},
7325fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
7335fa75cfeSDmitry Torokhov 	{
7345fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_PS2PP,
7355fa75cfeSDmitry Torokhov 		.name		= "PS2++",
7365fa75cfeSDmitry Torokhov 		.alias		= "logitech",
737190e2031SDmitry Torokhov 		.detect		= ps2pp_detect,
7385fa75cfeSDmitry Torokhov 	},
7395fa75cfeSDmitry Torokhov #endif
7405fa75cfeSDmitry Torokhov 	{
7415fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_THINKPS,
7425fa75cfeSDmitry Torokhov 		.name		= "ThinkPS/2",
7435fa75cfeSDmitry Torokhov 		.alias		= "thinkps",
7445fa75cfeSDmitry Torokhov 		.detect		= thinking_detect,
7455fa75cfeSDmitry Torokhov 	},
7465fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_CYPRESS
7475fa75cfeSDmitry Torokhov 	{
7485fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_CYPRESS,
7495fa75cfeSDmitry Torokhov 		.name		= "CyPS/2",
7505fa75cfeSDmitry Torokhov 		.alias		= "cypress",
7515fa75cfeSDmitry Torokhov 		.detect		= cypress_detect,
7525fa75cfeSDmitry Torokhov 		.init		= cypress_init,
7535fa75cfeSDmitry Torokhov 	},
7545fa75cfeSDmitry Torokhov #endif
7555fa75cfeSDmitry Torokhov 	{
7565fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_GENPS,
7575fa75cfeSDmitry Torokhov 		.name		= "GenPS/2",
7585fa75cfeSDmitry Torokhov 		.alias		= "genius",
7595fa75cfeSDmitry Torokhov 		.detect		= genius_detect,
7605fa75cfeSDmitry Torokhov 	},
7615fa75cfeSDmitry Torokhov 	{
7625fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_IMPS,
7635fa75cfeSDmitry Torokhov 		.name		= "ImPS/2",
7645fa75cfeSDmitry Torokhov 		.alias		= "imps",
7655fa75cfeSDmitry Torokhov 		.maxproto	= true,
7665fa75cfeSDmitry Torokhov 		.ignore_parity	= true,
7675fa75cfeSDmitry Torokhov 		.detect		= intellimouse_detect,
768ec6184b1SDmitry Torokhov 		.try_passthru	= true,
7695fa75cfeSDmitry Torokhov 	},
7705fa75cfeSDmitry Torokhov 	{
7715fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_IMEX,
7725fa75cfeSDmitry Torokhov 		.name		= "ImExPS/2",
7735fa75cfeSDmitry Torokhov 		.alias		= "exps",
7745fa75cfeSDmitry Torokhov 		.maxproto	= true,
7755fa75cfeSDmitry Torokhov 		.ignore_parity	= true,
7765fa75cfeSDmitry Torokhov 		.detect		= im_explorer_detect,
777ec6184b1SDmitry Torokhov 		.try_passthru	= true,
7785fa75cfeSDmitry Torokhov 	},
7795fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_SYNAPTICS
7805fa75cfeSDmitry Torokhov 	{
7815fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_SYNAPTICS,
7825fa75cfeSDmitry Torokhov 		.name		= "SynPS/2",
7835fa75cfeSDmitry Torokhov 		.alias		= "synaptics",
7845fa75cfeSDmitry Torokhov 		.detect		= synaptics_detect,
785e839ffabSBenjamin Tissoires 		.init		= synaptics_init_absolute,
7865fa75cfeSDmitry Torokhov 	},
7875fa75cfeSDmitry Torokhov 	{
7885fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_SYNAPTICS_RELATIVE,
7895fa75cfeSDmitry Torokhov 		.name		= "SynRelPS/2",
7905fa75cfeSDmitry Torokhov 		.alias		= "synaptics-relative",
7915fa75cfeSDmitry Torokhov 		.detect		= synaptics_detect,
7925fa75cfeSDmitry Torokhov 		.init		= synaptics_init_relative,
7935fa75cfeSDmitry Torokhov 	},
7945fa75cfeSDmitry Torokhov #endif
795e839ffabSBenjamin Tissoires #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
796e839ffabSBenjamin Tissoires 	{
797e839ffabSBenjamin Tissoires 		.type		= PSMOUSE_SYNAPTICS_SMBUS,
798e839ffabSBenjamin Tissoires 		.name		= "SynSMBus",
799e839ffabSBenjamin Tissoires 		.alias		= "synaptics-smbus",
800e839ffabSBenjamin Tissoires 		.detect		= synaptics_detect,
801e839ffabSBenjamin Tissoires 		.init		= synaptics_init_smbus,
802e839ffabSBenjamin Tissoires 		.smbus_companion = true,
803e839ffabSBenjamin Tissoires 	},
804e839ffabSBenjamin Tissoires #endif
8055fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_ALPS
8065fa75cfeSDmitry Torokhov 	{
8075fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_ALPS,
8085fa75cfeSDmitry Torokhov 		.name		= "AlpsPS/2",
8095fa75cfeSDmitry Torokhov 		.alias		= "alps",
8105fa75cfeSDmitry Torokhov 		.detect		= alps_detect,
8115fa75cfeSDmitry Torokhov 		.init		= alps_init,
8125fa75cfeSDmitry Torokhov 	},
8135fa75cfeSDmitry Torokhov #endif
8145fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_LIFEBOOK
8155fa75cfeSDmitry Torokhov 	{
8165fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_LIFEBOOK,
8175fa75cfeSDmitry Torokhov 		.name		= "LBPS/2",
8185fa75cfeSDmitry Torokhov 		.alias		= "lifebook",
819c378b511SDmitry Torokhov 		.detect		= lifebook_detect,
8205fa75cfeSDmitry Torokhov 		.init		= lifebook_init,
8215fa75cfeSDmitry Torokhov 	},
8225fa75cfeSDmitry Torokhov #endif
8235fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_TRACKPOINT
8245fa75cfeSDmitry Torokhov 	{
8255fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_TRACKPOINT,
8265fa75cfeSDmitry Torokhov 		.name		= "TPPS/2",
8275fa75cfeSDmitry Torokhov 		.alias		= "trackpoint",
8285fa75cfeSDmitry Torokhov 		.detect		= trackpoint_detect,
829ec6184b1SDmitry Torokhov 		.try_passthru	= true,
8305fa75cfeSDmitry Torokhov 	},
8315fa75cfeSDmitry Torokhov #endif
8325fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_TOUCHKIT
8335fa75cfeSDmitry Torokhov 	{
8345fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_TOUCHKIT_PS2,
8355fa75cfeSDmitry Torokhov 		.name		= "touchkitPS/2",
8365fa75cfeSDmitry Torokhov 		.alias		= "touchkit",
8375fa75cfeSDmitry Torokhov 		.detect		= touchkit_ps2_detect,
8385fa75cfeSDmitry Torokhov 	},
8395fa75cfeSDmitry Torokhov #endif
8405fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_OLPC
8415fa75cfeSDmitry Torokhov 	{
8425fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_HGPK,
8435fa75cfeSDmitry Torokhov 		.name		= "OLPC HGPK",
8445fa75cfeSDmitry Torokhov 		.alias		= "hgpk",
8455fa75cfeSDmitry Torokhov 		.detect		= hgpk_detect,
8465fa75cfeSDmitry Torokhov 	},
8475fa75cfeSDmitry Torokhov #endif
8485fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_ELANTECH
8495fa75cfeSDmitry Torokhov 	{
8505fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_ELANTECH,
8515fa75cfeSDmitry Torokhov 		.name		= "ETPS/2",
8525fa75cfeSDmitry Torokhov 		.alias		= "elantech",
8535fa75cfeSDmitry Torokhov 		.detect		= elantech_detect,
85421c48dbdSBenjamin Tissoires 		.init		= elantech_init_ps2,
85521c48dbdSBenjamin Tissoires 	},
85621c48dbdSBenjamin Tissoires #endif
85721c48dbdSBenjamin Tissoires #ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
85821c48dbdSBenjamin Tissoires 	{
85921c48dbdSBenjamin Tissoires 		.type		= PSMOUSE_ELANTECH_SMBUS,
86021c48dbdSBenjamin Tissoires 		.name		= "ETSMBus",
86121c48dbdSBenjamin Tissoires 		.alias		= "elantech-smbus",
86221c48dbdSBenjamin Tissoires 		.detect		= elantech_detect,
86321c48dbdSBenjamin Tissoires 		.init		= elantech_init_smbus,
86421c48dbdSBenjamin Tissoires 		.smbus_companion = true,
8655fa75cfeSDmitry Torokhov 	},
8665fa75cfeSDmitry Torokhov #endif
8675fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_SENTELIC
8685fa75cfeSDmitry Torokhov 	{
8695fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_FSP,
8705fa75cfeSDmitry Torokhov 		.name		= "FSPPS/2",
8715fa75cfeSDmitry Torokhov 		.alias		= "fsp",
8725fa75cfeSDmitry Torokhov 		.detect		= fsp_detect,
8735fa75cfeSDmitry Torokhov 		.init		= fsp_init,
8745fa75cfeSDmitry Torokhov 	},
8755fa75cfeSDmitry Torokhov #endif
8765fa75cfeSDmitry Torokhov 	{
8775fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_CORTRON,
8785fa75cfeSDmitry Torokhov 		.name		= "CortronPS/2",
8795fa75cfeSDmitry Torokhov 		.alias		= "cortps",
8805fa75cfeSDmitry Torokhov 		.detect		= cortron_detect,
8815fa75cfeSDmitry Torokhov 	},
8825fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_FOCALTECH
8835fa75cfeSDmitry Torokhov 	{
8845fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_FOCALTECH,
8855fa75cfeSDmitry Torokhov 		.name		= "FocalTechPS/2",
8865fa75cfeSDmitry Torokhov 		.alias		= "focaltech",
8875fa75cfeSDmitry Torokhov 		.detect		= focaltech_detect,
8885fa75cfeSDmitry Torokhov 		.init		= focaltech_init,
8895fa75cfeSDmitry Torokhov 	},
8905fa75cfeSDmitry Torokhov #endif
8915fa75cfeSDmitry Torokhov #ifdef CONFIG_MOUSE_PS2_VMMOUSE
8925fa75cfeSDmitry Torokhov 	{
8935fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_VMMOUSE,
8945fa75cfeSDmitry Torokhov 		.name		= VMMOUSE_PSNAME,
8955fa75cfeSDmitry Torokhov 		.alias		= "vmmouse",
8965fa75cfeSDmitry Torokhov 		.detect		= vmmouse_detect,
8975fa75cfeSDmitry Torokhov 		.init		= vmmouse_init,
8985fa75cfeSDmitry Torokhov 	},
8995fa75cfeSDmitry Torokhov #endif
90098ee3771SChris Diamand #ifdef CONFIG_MOUSE_PS2_BYD
90198ee3771SChris Diamand 	{
90298ee3771SChris Diamand 		.type		= PSMOUSE_BYD,
9032d5f5611SRichard Pospesel 		.name		= "BYDPS/2",
90498ee3771SChris Diamand 		.alias		= "byd",
90598ee3771SChris Diamand 		.detect		= byd_detect,
90698ee3771SChris Diamand 		.init		= byd_init,
90798ee3771SChris Diamand 	},
90898ee3771SChris Diamand #endif
9095fa75cfeSDmitry Torokhov 	{
9105fa75cfeSDmitry Torokhov 		.type		= PSMOUSE_AUTO,
9115fa75cfeSDmitry Torokhov 		.name		= "auto",
9125fa75cfeSDmitry Torokhov 		.alias		= "any",
9135fa75cfeSDmitry Torokhov 		.maxproto	= true,
9145fa75cfeSDmitry Torokhov 	},
9155fa75cfeSDmitry Torokhov };
9165fa75cfeSDmitry Torokhov 
__psmouse_protocol_by_type(enum psmouse_type type)917c378b511SDmitry Torokhov static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type)
9185fa75cfeSDmitry Torokhov {
9195fa75cfeSDmitry Torokhov 	int i;
9205fa75cfeSDmitry Torokhov 
9215fa75cfeSDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++)
9225fa75cfeSDmitry Torokhov 		if (psmouse_protocols[i].type == type)
9235fa75cfeSDmitry Torokhov 			return &psmouse_protocols[i];
9245fa75cfeSDmitry Torokhov 
925c378b511SDmitry Torokhov 	return NULL;
926c378b511SDmitry Torokhov }
927c378b511SDmitry Torokhov 
psmouse_protocol_by_type(enum psmouse_type type)928c378b511SDmitry Torokhov static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
929c378b511SDmitry Torokhov {
930c378b511SDmitry Torokhov 	const struct psmouse_protocol *proto;
931c378b511SDmitry Torokhov 
932c378b511SDmitry Torokhov 	proto = __psmouse_protocol_by_type(type);
933c378b511SDmitry Torokhov 	if (proto)
934c378b511SDmitry Torokhov 		return proto;
935c378b511SDmitry Torokhov 
9365fa75cfeSDmitry Torokhov 	WARN_ON(1);
9375fa75cfeSDmitry Torokhov 	return &psmouse_protocols[0];
9385fa75cfeSDmitry Torokhov }
9395fa75cfeSDmitry Torokhov 
psmouse_protocol_by_name(const char * name,size_t len)9405fa75cfeSDmitry Torokhov static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
9415fa75cfeSDmitry Torokhov {
9425fa75cfeSDmitry Torokhov 	const struct psmouse_protocol *p;
9435fa75cfeSDmitry Torokhov 	int i;
9445fa75cfeSDmitry Torokhov 
9455fa75cfeSDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) {
9465fa75cfeSDmitry Torokhov 		p = &psmouse_protocols[i];
9475fa75cfeSDmitry Torokhov 
9485fa75cfeSDmitry Torokhov 		if ((strlen(p->name) == len && !strncmp(p->name, name, len)) ||
9495fa75cfeSDmitry Torokhov 		    (strlen(p->alias) == len && !strncmp(p->alias, name, len)))
9505fa75cfeSDmitry Torokhov 			return &psmouse_protocols[i];
9515fa75cfeSDmitry Torokhov 	}
9525fa75cfeSDmitry Torokhov 
9535fa75cfeSDmitry Torokhov 	return NULL;
9545fa75cfeSDmitry Torokhov }
9555fa75cfeSDmitry Torokhov 
9561da177e4SLinus Torvalds /*
957ee9dfd7aSDmitry Torokhov  * Apply default settings to the psmouse structure. Most of them will
958ee9dfd7aSDmitry Torokhov  * be overridden by individual protocol initialization routines.
959ee9dfd7aSDmitry Torokhov  */
psmouse_apply_defaults(struct psmouse * psmouse)960ee9dfd7aSDmitry Torokhov static void psmouse_apply_defaults(struct psmouse *psmouse)
961ee9dfd7aSDmitry Torokhov {
962ee9dfd7aSDmitry Torokhov 	struct input_dev *input_dev = psmouse->dev;
963ee9dfd7aSDmitry Torokhov 
964ba667650SDmitry Torokhov 	bitmap_zero(input_dev->evbit, EV_CNT);
965ba667650SDmitry Torokhov 	bitmap_zero(input_dev->keybit, KEY_CNT);
966ba667650SDmitry Torokhov 	bitmap_zero(input_dev->relbit, REL_CNT);
967ba667650SDmitry Torokhov 	bitmap_zero(input_dev->absbit, ABS_CNT);
968ba667650SDmitry Torokhov 	bitmap_zero(input_dev->mscbit, MSC_CNT);
969ee9dfd7aSDmitry Torokhov 
970ba667650SDmitry Torokhov 	input_set_capability(input_dev, EV_KEY, BTN_LEFT);
971ba667650SDmitry Torokhov 	input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
972ee9dfd7aSDmitry Torokhov 
973ba667650SDmitry Torokhov 	input_set_capability(input_dev, EV_REL, REL_X);
974ba667650SDmitry Torokhov 	input_set_capability(input_dev, EV_REL, REL_Y);
975ee9dfd7aSDmitry Torokhov 
97601d4cd5cSHans de Goede 	__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
97701d4cd5cSHans de Goede 
978085fa80dSDmitry Torokhov 	psmouse->protocol = &psmouse_protocols[0];
979085fa80dSDmitry Torokhov 
980ee9dfd7aSDmitry Torokhov 	psmouse->set_rate = psmouse_set_rate;
981ee9dfd7aSDmitry Torokhov 	psmouse->set_resolution = psmouse_set_resolution;
9824ec212f0SMathias Gottschlag 	psmouse->set_scale = psmouse_set_scale;
983ee9dfd7aSDmitry Torokhov 	psmouse->poll = psmouse_poll;
984ee9dfd7aSDmitry Torokhov 	psmouse->protocol_handler = psmouse_process_byte;
985ee9dfd7aSDmitry Torokhov 	psmouse->pktsize = 3;
986ee9dfd7aSDmitry Torokhov 	psmouse->reconnect = NULL;
9870ab3fa57SDmitry Torokhov 	psmouse->fast_reconnect = NULL;
988ee9dfd7aSDmitry Torokhov 	psmouse->disconnect = NULL;
989ee9dfd7aSDmitry Torokhov 	psmouse->cleanup = NULL;
990ee9dfd7aSDmitry Torokhov 	psmouse->pt_activate = NULL;
991ee9dfd7aSDmitry Torokhov 	psmouse->pt_deactivate = NULL;
992ee9dfd7aSDmitry Torokhov }
993ee9dfd7aSDmitry Torokhov 
psmouse_do_detect(int (* detect)(struct psmouse *,bool),struct psmouse * psmouse,bool allow_passthrough,bool set_properties)9942bc4298fSDmitry Torokhov static bool psmouse_do_detect(int (*detect)(struct psmouse *, bool),
9952bc4298fSDmitry Torokhov 			      struct psmouse *psmouse, bool allow_passthrough,
9962bc4298fSDmitry Torokhov 			      bool set_properties)
9972bc4298fSDmitry Torokhov {
9982bc4298fSDmitry Torokhov 	if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU &&
9992bc4298fSDmitry Torokhov 	    !allow_passthrough) {
10002bc4298fSDmitry Torokhov 		return false;
10012bc4298fSDmitry Torokhov 	}
10022bc4298fSDmitry Torokhov 
10032bc4298fSDmitry Torokhov 	if (set_properties)
10042bc4298fSDmitry Torokhov 		psmouse_apply_defaults(psmouse);
10052bc4298fSDmitry Torokhov 
10062bc4298fSDmitry Torokhov 	return detect(psmouse, set_properties) == 0;
10072bc4298fSDmitry Torokhov }
10082bc4298fSDmitry Torokhov 
psmouse_try_protocol(struct psmouse * psmouse,enum psmouse_type type,unsigned int * max_proto,bool set_properties,bool init_allowed)1009c378b511SDmitry Torokhov static bool psmouse_try_protocol(struct psmouse *psmouse,
1010c378b511SDmitry Torokhov 				 enum psmouse_type type,
1011c378b511SDmitry Torokhov 				 unsigned int *max_proto,
1012c378b511SDmitry Torokhov 				 bool set_properties, bool init_allowed)
1013ee9dfd7aSDmitry Torokhov {
1014c378b511SDmitry Torokhov 	const struct psmouse_protocol *proto;
1015c378b511SDmitry Torokhov 
1016c378b511SDmitry Torokhov 	proto = __psmouse_protocol_by_type(type);
1017c378b511SDmitry Torokhov 	if (!proto)
1018c378b511SDmitry Torokhov 		return false;
1019c378b511SDmitry Torokhov 
10202bc4298fSDmitry Torokhov 	if (!psmouse_do_detect(proto->detect, psmouse, proto->try_passthru,
10212bc4298fSDmitry Torokhov 			       set_properties))
1022c378b511SDmitry Torokhov 		return false;
1023c378b511SDmitry Torokhov 
1024c378b511SDmitry Torokhov 	if (set_properties && proto->init && init_allowed) {
1025c378b511SDmitry Torokhov 		if (proto->init(psmouse) != 0) {
1026c378b511SDmitry Torokhov 			/*
1027c378b511SDmitry Torokhov 			 * We detected device, but init failed. Adjust
1028c378b511SDmitry Torokhov 			 * max_proto so we only try standard protocols.
1029c378b511SDmitry Torokhov 			 */
1030c378b511SDmitry Torokhov 			if (*max_proto > PSMOUSE_IMEX)
1031c378b511SDmitry Torokhov 				*max_proto = PSMOUSE_IMEX;
1032c378b511SDmitry Torokhov 
1033c378b511SDmitry Torokhov 			return false;
1034c378b511SDmitry Torokhov 		}
1035c378b511SDmitry Torokhov 	}
1036c378b511SDmitry Torokhov 
1037c378b511SDmitry Torokhov 	return true;
1038ee9dfd7aSDmitry Torokhov }
1039ee9dfd7aSDmitry Torokhov 
1040ee9dfd7aSDmitry Torokhov /*
10411da177e4SLinus Torvalds  * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
10421da177e4SLinus Torvalds  * the mouse may have.
10431da177e4SLinus Torvalds  */
psmouse_extensions(struct psmouse * psmouse,unsigned int max_proto,bool set_properties)10441da177e4SLinus Torvalds static int psmouse_extensions(struct psmouse *psmouse,
1045b7802c5cSDmitry Torokhov 			      unsigned int max_proto, bool set_properties)
10461da177e4SLinus Torvalds {
10470698989dSJiri Kosina 	bool synaptics_hardware = false;
1048e839ffabSBenjamin Tissoires 	int ret;
10491da177e4SLinus Torvalds 
1050ad530771SDmitry Torokhov 	/*
1051ad530771SDmitry Torokhov 	 * Always check for focaltech, this is safe as it uses pnp-id
1052ad530771SDmitry Torokhov 	 * matching.
1053ad530771SDmitry Torokhov 	 */
10542bc4298fSDmitry Torokhov 	if (psmouse_do_detect(focaltech_detect,
10552bc4298fSDmitry Torokhov 			      psmouse, false, set_properties)) {
1056c378b511SDmitry Torokhov 		if (max_proto > PSMOUSE_IMEX &&
1057c378b511SDmitry Torokhov 		    IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
10582b6f39e9SDmitry Torokhov 		    (!set_properties || focaltech_init(psmouse) == 0)) {
105905be1d07SMathias Gottschlag 			return PSMOUSE_FOCALTECH;
10602b6f39e9SDmitry Torokhov 		}
10613ace3686SHans de Goede 		/*
10622b6f39e9SDmitry Torokhov 		 * Restrict psmouse_max_proto so that psmouse_initialize()
10632b6f39e9SDmitry Torokhov 		 * does not try to reset rate and resolution, because even
10642b6f39e9SDmitry Torokhov 		 * that upsets the device.
10652b6f39e9SDmitry Torokhov 		 * This also causes us to basically fall through to basic
10662b6f39e9SDmitry Torokhov 		 * protocol detection, where we fully reset the mouse,
10672b6f39e9SDmitry Torokhov 		 * and set it up as bare PS/2 protocol device.
10683ace3686SHans de Goede 		 */
10692b6f39e9SDmitry Torokhov 		psmouse_max_proto = max_proto = PSMOUSE_PS2;
107005be1d07SMathias Gottschlag 	}
10713ace3686SHans de Goede 
1072a15d60f8SDmitry Torokhov 	/*
1073ad530771SDmitry Torokhov 	 * We always check for LifeBook because it does not disturb mouse
1074a15d60f8SDmitry Torokhov 	 * (it only checks DMI information).
1075a15d60f8SDmitry Torokhov 	 */
1076c378b511SDmitry Torokhov 	if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
1077c378b511SDmitry Torokhov 				 set_properties, max_proto > PSMOUSE_IMEX))
107802d7f589SKenan Esau 		return PSMOUSE_LIFEBOOK;
107902d7f589SKenan Esau 
1080c378b511SDmitry Torokhov 	if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
1081c378b511SDmitry Torokhov 				 set_properties, max_proto > PSMOUSE_IMEX))
10828b8be51bSThomas Hellstrom 		return PSMOUSE_VMMOUSE;
10838b8be51bSThomas Hellstrom 
10841da177e4SLinus Torvalds 	/*
1085ad530771SDmitry Torokhov 	 * Try Kensington ThinkingMouse (we try first, because Synaptics
1086ad530771SDmitry Torokhov 	 * probe upsets the ThinkingMouse).
10871da177e4SLinus Torvalds 	 */
1088ee9dfd7aSDmitry Torokhov 	if (max_proto > PSMOUSE_IMEX &&
1089c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
1090c378b511SDmitry Torokhov 				 set_properties, true)) {
10911da177e4SLinus Torvalds 		return PSMOUSE_THINKPS;
1092ee9dfd7aSDmitry Torokhov 	}
10931da177e4SLinus Torvalds 
10941da177e4SLinus Torvalds 	/*
1095ad530771SDmitry Torokhov 	 * Try Synaptics TouchPad. Note that probing is done even if
1096ad530771SDmitry Torokhov 	 * Synaptics protocol support is disabled in config - we need to
1097ad530771SDmitry Torokhov 	 * know if it is Synaptics so we can reset it properly after
1098ad530771SDmitry Torokhov 	 * probing for IntelliMouse.
10991da177e4SLinus Torvalds 	 */
1100ee9dfd7aSDmitry Torokhov 	if (max_proto > PSMOUSE_PS2 &&
11012bc4298fSDmitry Torokhov 	    psmouse_do_detect(synaptics_detect,
11022bc4298fSDmitry Torokhov 			      psmouse, false, set_properties)) {
1103b7802c5cSDmitry Torokhov 		synaptics_hardware = true;
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds 		if (max_proto > PSMOUSE_IMEX) {
1106e4e6efd2SDaniel Drake 			/*
1107ad530771SDmitry Torokhov 			 * Try activating protocol, but check if support is
1108ad530771SDmitry Torokhov 			 * enabled first, since we try detecting Synaptics
1109ad530771SDmitry Torokhov 			 * even when protocol is disabled.
1110e4e6efd2SDaniel Drake 			 */
1111e839ffabSBenjamin Tissoires 			if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
1112e839ffabSBenjamin Tissoires 			    IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) {
1113e839ffabSBenjamin Tissoires 				if (!set_properties)
11141da177e4SLinus Torvalds 					return PSMOUSE_SYNAPTICS;
1115e839ffabSBenjamin Tissoires 
1116e839ffabSBenjamin Tissoires 				ret = synaptics_init(psmouse);
1117e839ffabSBenjamin Tissoires 				if (ret >= 0)
1118e839ffabSBenjamin Tissoires 					return ret;
1119e4e6efd2SDaniel Drake 			}
1120e4e6efd2SDaniel Drake 
11211da177e4SLinus Torvalds 			/*
1122ad530771SDmitry Torokhov 			 * Some Synaptics touchpads can emulate extended
1123ad530771SDmitry Torokhov 			 * protocols (like IMPS/2).  Unfortunately
1124ad530771SDmitry Torokhov 			 * Logitech/Genius probes confuse some firmware
1125ad530771SDmitry Torokhov 			 * versions so we'll have to skip them.
11261da177e4SLinus Torvalds 			 */
11271da177e4SLinus Torvalds 			max_proto = PSMOUSE_IMEX;
11281da177e4SLinus Torvalds 		}
1129ad530771SDmitry Torokhov 
11301da177e4SLinus Torvalds 		/*
1131ad530771SDmitry Torokhov 		 * Make sure that touchpad is in relative mode, gestures
1132ad530771SDmitry Torokhov 		 * (taps) are enabled.
11331da177e4SLinus Torvalds 		 */
11341da177e4SLinus Torvalds 		synaptics_reset(psmouse);
11351da177e4SLinus Torvalds 	}
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	/*
1138ad530771SDmitry Torokhov 	 * Try Cypress Trackpad. We must try it before Finger Sensing Pad
1139ad530771SDmitry Torokhov 	 * because Finger Sensing Pad probe upsets some modules of Cypress
1140ad530771SDmitry Torokhov 	 * Trackpads.
11410799a924SDudley Du 	 */
11420799a924SDudley Du 	if (max_proto > PSMOUSE_IMEX &&
1143c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
1144c378b511SDmitry Torokhov 				 set_properties, true)) {
11450799a924SDudley Du 		return PSMOUSE_CYPRESS;
11460799a924SDudley Du 	}
11470799a924SDudley Du 
1148ad530771SDmitry Torokhov 	/* Try ALPS TouchPad */
11491da177e4SLinus Torvalds 	if (max_proto > PSMOUSE_IMEX) {
11501da177e4SLinus Torvalds 		ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
1151c378b511SDmitry Torokhov 		if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS,
1152c378b511SDmitry Torokhov 					 &max_proto, set_properties, true))
11531da177e4SLinus Torvalds 			return PSMOUSE_ALPS;
11541da177e4SLinus Torvalds 	}
11551da177e4SLinus Torvalds 
1156ad530771SDmitry Torokhov 	/* Try OLPC HGPK touchpad */
1157df08ef27SAndres Salomon 	if (max_proto > PSMOUSE_IMEX &&
1158c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto,
1159c378b511SDmitry Torokhov 				 set_properties, true)) {
1160df08ef27SAndres Salomon 		return PSMOUSE_HGPK;
1161df08ef27SAndres Salomon 	}
116224bf10abSStefan Lucke 
1163ad530771SDmitry Torokhov 	/* Try Elantech touchpad */
11642a0bd75eSArjan Opmeer 	if (max_proto > PSMOUSE_IMEX &&
1165c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
116621c48dbdSBenjamin Tissoires 				 &max_proto, set_properties, false)) {
116721c48dbdSBenjamin Tissoires 		if (!set_properties)
11682a0bd75eSArjan Opmeer 			return PSMOUSE_ELANTECH;
116921c48dbdSBenjamin Tissoires 
117021c48dbdSBenjamin Tissoires 		ret = elantech_init(psmouse);
117121c48dbdSBenjamin Tissoires 		if (ret >= 0)
117221c48dbdSBenjamin Tissoires 			return ret;
11732a0bd75eSArjan Opmeer 	}
11742a0bd75eSArjan Opmeer 
1175df08ef27SAndres Salomon 	if (max_proto > PSMOUSE_IMEX) {
1176c378b511SDmitry Torokhov 		if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
1177c378b511SDmitry Torokhov 					 &max_proto, set_properties, true))
11781da177e4SLinus Torvalds 			return PSMOUSE_GENPS;
11791da177e4SLinus Torvalds 
1180c378b511SDmitry Torokhov 		if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
1181c378b511SDmitry Torokhov 					 &max_proto, set_properties, true))
11821da177e4SLinus Torvalds 			return PSMOUSE_PS2PP;
11831da177e4SLinus Torvalds 
1184c378b511SDmitry Torokhov 		if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
1185c378b511SDmitry Torokhov 					 &max_proto, set_properties, true))
1186ba44995aSDmitry Torokhov 			return PSMOUSE_TRACKPOINT;
1187ba44995aSDmitry Torokhov 
1188c378b511SDmitry Torokhov 		if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
1189c378b511SDmitry Torokhov 					 &max_proto, set_properties, true))
119024bf10abSStefan Lucke 			return PSMOUSE_TOUCHKIT_PS2;
119124bf10abSStefan Lucke 	}
119224bf10abSStefan Lucke 
11931da177e4SLinus Torvalds 	/*
11944a18b3abSTai-hwa Liang 	 * Try Finger Sensing Pad. We do it here because its probe upsets
11954a18b3abSTai-hwa Liang 	 * Trackpoint devices (causing TP_READ_ID command to time out).
11964a18b3abSTai-hwa Liang 	 */
1197c378b511SDmitry Torokhov 	if (max_proto > PSMOUSE_IMEX &&
1198c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_FSP,
1199c378b511SDmitry Torokhov 				 &max_proto, set_properties, true)) {
12004a18b3abSTai-hwa Liang 		return PSMOUSE_FSP;
12014a18b3abSTai-hwa Liang 	}
12024a18b3abSTai-hwa Liang 
12034a18b3abSTai-hwa Liang 	/*
12041da177e4SLinus Torvalds 	 * Reset to defaults in case the device got confused by extended
1205554fc193SAlon Ziv 	 * protocol probes. Note that we follow up with full reset because
1206554fc193SAlon Ziv 	 * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
12071da177e4SLinus Torvalds 	 */
1208554fc193SAlon Ziv 	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
1209ba44995aSDmitry Torokhov 	psmouse_reset(psmouse);
12101da177e4SLinus Torvalds 
1211ee9dfd7aSDmitry Torokhov 	if (max_proto >= PSMOUSE_IMEX &&
1212c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_IMEX,
1213c378b511SDmitry Torokhov 				 &max_proto, set_properties, true)) {
12141da177e4SLinus Torvalds 		return PSMOUSE_IMEX;
1215ee9dfd7aSDmitry Torokhov 	}
12161da177e4SLinus Torvalds 
1217ee9dfd7aSDmitry Torokhov 	if (max_proto >= PSMOUSE_IMPS &&
1218c378b511SDmitry Torokhov 	    psmouse_try_protocol(psmouse, PSMOUSE_IMPS,
1219c378b511SDmitry Torokhov 				 &max_proto, set_properties, true)) {
12201da177e4SLinus Torvalds 		return PSMOUSE_IMPS;
1221ee9dfd7aSDmitry Torokhov 	}
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds 	/*
1224ad530771SDmitry Torokhov 	 * Okay, all failed, we have a standard mouse here. The number of
1225ad530771SDmitry Torokhov 	 * the buttons is still a question, though. We assume 3.
12261da177e4SLinus Torvalds 	 */
1227c378b511SDmitry Torokhov 	psmouse_try_protocol(psmouse, PSMOUSE_PS2,
1228c378b511SDmitry Torokhov 			     &max_proto, set_properties, true);
12291da177e4SLinus Torvalds 
12301da177e4SLinus Torvalds 	if (synaptics_hardware) {
12311da177e4SLinus Torvalds 		/*
1232ad530771SDmitry Torokhov 		 * We detected Synaptics hardware but it did not respond to
1233ad530771SDmitry Torokhov 		 * IMPS/2 probes.  We need to reset the touchpad because if
1234ad530771SDmitry Torokhov 		 * there is a track point on the pass through port it could
1235ad530771SDmitry Torokhov 		 * get disabled while probing for protocol extensions.
12361da177e4SLinus Torvalds 		 */
12371da177e4SLinus Torvalds 		psmouse_reset(psmouse);
12381da177e4SLinus Torvalds 	}
12391da177e4SLinus Torvalds 
12401da177e4SLinus Torvalds 	return PSMOUSE_PS2;
12411da177e4SLinus Torvalds }
12421da177e4SLinus Torvalds 
12431da177e4SLinus Torvalds /*
12441da177e4SLinus Torvalds  * psmouse_probe() probes for a PS/2 mouse.
12451da177e4SLinus Torvalds  */
psmouse_probe(struct psmouse * psmouse)12461da177e4SLinus Torvalds static int psmouse_probe(struct psmouse *psmouse)
12471da177e4SLinus Torvalds {
12481da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
1249ba667650SDmitry Torokhov 	u8 param[2];
1250ba667650SDmitry Torokhov 	int error;
12511da177e4SLinus Torvalds 
12521da177e4SLinus Torvalds 	/*
1253ad530771SDmitry Torokhov 	 * First, we check if it's a mouse. It should send 0x00 or 0x03 in
1254ad530771SDmitry Torokhov 	 * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
1255ad530771SDmitry Torokhov 	 * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and
1256ad530771SDmitry Torokhov 	 * subsequent ID queries, probably due to a firmware bug.
12571da177e4SLinus Torvalds 	 */
12581da177e4SLinus Torvalds 	param[0] = 0xa5;
1259ba667650SDmitry Torokhov 	error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
1260ba667650SDmitry Torokhov 	if (error)
1261ba667650SDmitry Torokhov 		return error;
12621da177e4SLinus Torvalds 
12637741e931SVojtech Pavlik 	if (param[0] != 0x00 && param[0] != 0x03 &&
12647741e931SVojtech Pavlik 	    param[0] != 0x04 && param[0] != 0xff)
1265ba667650SDmitry Torokhov 		return -ENODEV;
12661da177e4SLinus Torvalds 
12671da177e4SLinus Torvalds 	/*
1268ad530771SDmitry Torokhov 	 * Then we reset and disable the mouse so that it doesn't generate
1269ad530771SDmitry Torokhov 	 * events.
12701da177e4SLinus Torvalds 	 */
1271ba667650SDmitry Torokhov 	error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
1272ba667650SDmitry Torokhov 	if (error)
1273ba667650SDmitry Torokhov 		psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n",
1274ba667650SDmitry Torokhov 			     ps2dev->serio->phys, error);
12751da177e4SLinus Torvalds 
12761da177e4SLinus Torvalds 	return 0;
12771da177e4SLinus Torvalds }
12781da177e4SLinus Torvalds 
12791da177e4SLinus Torvalds /*
12801da177e4SLinus Torvalds  * psmouse_initialize() initializes the mouse to a sane state.
12811da177e4SLinus Torvalds  */
psmouse_initialize(struct psmouse * psmouse)12821da177e4SLinus Torvalds static void psmouse_initialize(struct psmouse *psmouse)
12831da177e4SLinus Torvalds {
12841da177e4SLinus Torvalds 	/*
12851da177e4SLinus Torvalds 	 * We set the mouse report rate, resolution and scaling.
12861da177e4SLinus Torvalds 	 */
12871da177e4SLinus Torvalds 	if (psmouse_max_proto != PSMOUSE_PS2) {
12881da177e4SLinus Torvalds 		psmouse->set_rate(psmouse, psmouse->rate);
12891da177e4SLinus Torvalds 		psmouse->set_resolution(psmouse, psmouse->resolution);
12904ec212f0SMathias Gottschlag 		psmouse->set_scale(psmouse, PSMOUSE_SCALE11);
12911da177e4SLinus Torvalds 	}
12921da177e4SLinus Torvalds }
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds /*
12951da177e4SLinus Torvalds  * psmouse_activate() enables the mouse so that we get motion reports from it.
12961da177e4SLinus Torvalds  */
psmouse_activate(struct psmouse * psmouse)1297bd26f3d6SAndres Salomon int psmouse_activate(struct psmouse *psmouse)
12981da177e4SLinus Torvalds {
1299bd26f3d6SAndres Salomon 	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
1300b5d21704SDmitry Torokhov 		psmouse_warn(psmouse, "Failed to enable mouse on %s\n",
13011da177e4SLinus Torvalds 			     psmouse->ps2dev.serio->phys);
1302bd26f3d6SAndres Salomon 		return -1;
13031da177e4SLinus Torvalds 	}
13041da177e4SLinus Torvalds 
1305bd26f3d6SAndres Salomon 	psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
1306bd26f3d6SAndres Salomon 	return 0;
1307bd26f3d6SAndres Salomon }
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds /*
1310ad530771SDmitry Torokhov  * psmouse_deactivate() puts the mouse into poll mode so that we don't get
1311ad530771SDmitry Torokhov  * motion reports from it unless we explicitly request it.
13121da177e4SLinus Torvalds  */
psmouse_deactivate(struct psmouse * psmouse)1313bd26f3d6SAndres Salomon int psmouse_deactivate(struct psmouse *psmouse)
13141da177e4SLinus Torvalds {
1315ba667650SDmitry Torokhov 	int error;
1316ba667650SDmitry Torokhov 
1317ba667650SDmitry Torokhov 	error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE);
1318ba667650SDmitry Torokhov 	if (error) {
1319ba667650SDmitry Torokhov 		psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n",
1320ba667650SDmitry Torokhov 			     psmouse->ps2dev.serio->phys, error);
1321ba667650SDmitry Torokhov 		return error;
1322bd26f3d6SAndres Salomon 	}
13231da177e4SLinus Torvalds 
13241da177e4SLinus Torvalds 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
1325bd26f3d6SAndres Salomon 	return 0;
13261da177e4SLinus Torvalds }
13271da177e4SLinus Torvalds 
1328f0d5c6f4SDmitry Torokhov /*
1329f0d5c6f4SDmitry Torokhov  * psmouse_resync() attempts to re-validate current protocol.
1330f0d5c6f4SDmitry Torokhov  */
psmouse_resync(struct work_struct * work)1331c4028958SDavid Howells static void psmouse_resync(struct work_struct *work)
1332f0d5c6f4SDmitry Torokhov {
1333c4028958SDavid Howells 	struct psmouse *parent = NULL, *psmouse =
13348bf020eeSAndres Salomon 		container_of(work, struct psmouse, resync_work.work);
1335f0d5c6f4SDmitry Torokhov 	struct serio *serio = psmouse->ps2dev.serio;
1336f0d5c6f4SDmitry Torokhov 	psmouse_ret_t rc = PSMOUSE_GOOD_DATA;
1337b7802c5cSDmitry Torokhov 	bool failed = false, enabled = false;
1338f0d5c6f4SDmitry Torokhov 	int i;
1339f0d5c6f4SDmitry Torokhov 
1340c14471dcSIngo Molnar 	mutex_lock(&psmouse_mutex);
1341f0d5c6f4SDmitry Torokhov 
1342f0d5c6f4SDmitry Torokhov 	if (psmouse->state != PSMOUSE_RESYNCING)
1343f0d5c6f4SDmitry Torokhov 		goto out;
1344f0d5c6f4SDmitry Torokhov 
1345f0d5c6f4SDmitry Torokhov 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1346100e1695SDmitry Torokhov 		parent = psmouse_from_serio(serio->parent);
1347f0d5c6f4SDmitry Torokhov 		psmouse_deactivate(parent);
1348f0d5c6f4SDmitry Torokhov 	}
1349f0d5c6f4SDmitry Torokhov 
1350f0d5c6f4SDmitry Torokhov 	/*
1351f0d5c6f4SDmitry Torokhov 	 * Some mice don't ACK commands sent while they are in the middle of
1352f0d5c6f4SDmitry Torokhov 	 * transmitting motion packet. To avoid delay we use ps2_sendbyte()
1353f0d5c6f4SDmitry Torokhov 	 * instead of ps2_command() which would wait for 200ms for an ACK
1354f0d5c6f4SDmitry Torokhov 	 * that may never come.
1355f0d5c6f4SDmitry Torokhov 	 * As an additional quirk ALPS touchpads may not only forget to ACK
1356f0d5c6f4SDmitry Torokhov 	 * disable command but will stop reporting taps, so if we see that
1357f0d5c6f4SDmitry Torokhov 	 * mouse at least once ACKs disable we will do full reconnect if ACK
1358f0d5c6f4SDmitry Torokhov 	 * is missing.
1359f0d5c6f4SDmitry Torokhov 	 */
1360f0d5c6f4SDmitry Torokhov 	psmouse->num_resyncs++;
1361f0d5c6f4SDmitry Torokhov 
1362f0d5c6f4SDmitry Torokhov 	if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
1363f0d5c6f4SDmitry Torokhov 		if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command)
1364b7802c5cSDmitry Torokhov 			failed = true;
1365f0d5c6f4SDmitry Torokhov 	} else
1366b7802c5cSDmitry Torokhov 		psmouse->acks_disable_command = true;
1367f0d5c6f4SDmitry Torokhov 
1368f0d5c6f4SDmitry Torokhov 	/*
1369f0d5c6f4SDmitry Torokhov 	 * Poll the mouse. If it was reset the packet will be shorter than
1370f0d5c6f4SDmitry Torokhov 	 * psmouse->pktsize and ps2_command will fail. We do not expect and
1371f0d5c6f4SDmitry Torokhov 	 * do not handle scenario when mouse "upgrades" its protocol while
1372f0d5c6f4SDmitry Torokhov 	 * disconnected since it would require additional delay. If we ever
1373f0d5c6f4SDmitry Torokhov 	 * see a mouse that does it we'll adjust the code.
1374f0d5c6f4SDmitry Torokhov 	 */
1375f0d5c6f4SDmitry Torokhov 	if (!failed) {
1376f0d5c6f4SDmitry Torokhov 		if (psmouse->poll(psmouse))
1377b7802c5cSDmitry Torokhov 			failed = true;
1378f0d5c6f4SDmitry Torokhov 		else {
1379f0d5c6f4SDmitry Torokhov 			psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
1380f0d5c6f4SDmitry Torokhov 			for (i = 0; i < psmouse->pktsize; i++) {
1381f0d5c6f4SDmitry Torokhov 				psmouse->pktcnt++;
13827d12e780SDavid Howells 				rc = psmouse->protocol_handler(psmouse);
1383f0d5c6f4SDmitry Torokhov 				if (rc != PSMOUSE_GOOD_DATA)
1384f0d5c6f4SDmitry Torokhov 					break;
1385f0d5c6f4SDmitry Torokhov 			}
1386f0d5c6f4SDmitry Torokhov 			if (rc != PSMOUSE_FULL_PACKET)
1387b7802c5cSDmitry Torokhov 				failed = true;
1388f0d5c6f4SDmitry Torokhov 			psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
1389f0d5c6f4SDmitry Torokhov 		}
1390f0d5c6f4SDmitry Torokhov 	}
1391ad530771SDmitry Torokhov 
1392f0d5c6f4SDmitry Torokhov 	/*
1393ad530771SDmitry Torokhov 	 * Now try to enable mouse. We try to do that even if poll failed
1394ad530771SDmitry Torokhov 	 * and also repeat our attempts 5 times, otherwise we may be left
1395ad530771SDmitry Torokhov 	 * out with disabled mouse.
1396f0d5c6f4SDmitry Torokhov 	 */
1397f0d5c6f4SDmitry Torokhov 	for (i = 0; i < 5; i++) {
1398f0d5c6f4SDmitry Torokhov 		if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
1399b7802c5cSDmitry Torokhov 			enabled = true;
1400f0d5c6f4SDmitry Torokhov 			break;
1401f0d5c6f4SDmitry Torokhov 		}
1402f0d5c6f4SDmitry Torokhov 		msleep(200);
1403f0d5c6f4SDmitry Torokhov 	}
1404f0d5c6f4SDmitry Torokhov 
1405f0d5c6f4SDmitry Torokhov 	if (!enabled) {
1406b5d21704SDmitry Torokhov 		psmouse_warn(psmouse, "failed to re-enable mouse on %s\n",
1407f0d5c6f4SDmitry Torokhov 			     psmouse->ps2dev.serio->phys);
1408b7802c5cSDmitry Torokhov 		failed = true;
1409f0d5c6f4SDmitry Torokhov 	}
1410f0d5c6f4SDmitry Torokhov 
1411f0d5c6f4SDmitry Torokhov 	if (failed) {
1412f0d5c6f4SDmitry Torokhov 		psmouse_set_state(psmouse, PSMOUSE_IGNORE);
1413b5d21704SDmitry Torokhov 		psmouse_info(psmouse,
1414b5d21704SDmitry Torokhov 			     "resync failed, issuing reconnect request\n");
1415f0d5c6f4SDmitry Torokhov 		serio_reconnect(serio);
1416f0d5c6f4SDmitry Torokhov 	} else
1417f0d5c6f4SDmitry Torokhov 		psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
1418f0d5c6f4SDmitry Torokhov 
1419f0d5c6f4SDmitry Torokhov 	if (parent)
1420f0d5c6f4SDmitry Torokhov 		psmouse_activate(parent);
1421f0d5c6f4SDmitry Torokhov  out:
1422c14471dcSIngo Molnar 	mutex_unlock(&psmouse_mutex);
1423f0d5c6f4SDmitry Torokhov }
14241da177e4SLinus Torvalds 
14251da177e4SLinus Torvalds /*
14261da177e4SLinus Torvalds  * psmouse_cleanup() resets the mouse into power-on state.
14271da177e4SLinus Torvalds  */
psmouse_cleanup(struct serio * serio)14281da177e4SLinus Torvalds static void psmouse_cleanup(struct serio *serio)
14291da177e4SLinus Torvalds {
1430100e1695SDmitry Torokhov 	struct psmouse *psmouse = psmouse_from_serio(serio);
1431a1cec061SDmitry Torokhov 	struct psmouse *parent = NULL;
1432a1cec061SDmitry Torokhov 
1433a1cec061SDmitry Torokhov 	mutex_lock(&psmouse_mutex);
1434a1cec061SDmitry Torokhov 
1435a1cec061SDmitry Torokhov 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1436100e1695SDmitry Torokhov 		parent = psmouse_from_serio(serio->parent);
1437a1cec061SDmitry Torokhov 		psmouse_deactivate(parent);
1438a1cec061SDmitry Torokhov 	}
1439a1cec061SDmitry Torokhov 
1440a9f0c381SDmitry Torokhov 	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
1441a9f0c381SDmitry Torokhov 
1442a9f0c381SDmitry Torokhov 	/*
1443a9f0c381SDmitry Torokhov 	 * Disable stream mode so cleanup routine can proceed undisturbed.
1444a9f0c381SDmitry Torokhov 	 */
1445a9f0c381SDmitry Torokhov 	if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
1446b5d21704SDmitry Torokhov 		psmouse_warn(psmouse, "Failed to disable mouse on %s\n",
1447a9f0c381SDmitry Torokhov 			     psmouse->ps2dev.serio->phys);
1448a1cec061SDmitry Torokhov 
1449a1cec061SDmitry Torokhov 	if (psmouse->cleanup)
1450a1cec061SDmitry Torokhov 		psmouse->cleanup(psmouse);
14511da177e4SLinus Torvalds 
14524a299bf5SDmitry Torokhov 	/*
14534a299bf5SDmitry Torokhov 	 * Reset the mouse to defaults (bare PS/2 protocol).
14544a299bf5SDmitry Torokhov 	 */
14554a299bf5SDmitry Torokhov 	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
1456a1cec061SDmitry Torokhov 
1457a1cec061SDmitry Torokhov 	/*
1458a1cec061SDmitry Torokhov 	 * Some boxes, such as HP nx7400, get terribly confused if mouse
1459a1cec061SDmitry Torokhov 	 * is not fully enabled before suspending/shutting down.
1460a1cec061SDmitry Torokhov 	 */
1461a1cec061SDmitry Torokhov 	ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
1462a1cec061SDmitry Torokhov 
1463a1cec061SDmitry Torokhov 	if (parent) {
1464a1cec061SDmitry Torokhov 		if (parent->pt_deactivate)
1465a1cec061SDmitry Torokhov 			parent->pt_deactivate(parent);
1466a1cec061SDmitry Torokhov 
1467a1cec061SDmitry Torokhov 		psmouse_activate(parent);
1468a1cec061SDmitry Torokhov 	}
1469a1cec061SDmitry Torokhov 
1470a1cec061SDmitry Torokhov 	mutex_unlock(&psmouse_mutex);
14711da177e4SLinus Torvalds }
14721da177e4SLinus Torvalds 
14731da177e4SLinus Torvalds /*
14741da177e4SLinus Torvalds  * psmouse_disconnect() closes and frees.
14751da177e4SLinus Torvalds  */
psmouse_disconnect(struct serio * serio)14761da177e4SLinus Torvalds static void psmouse_disconnect(struct serio *serio)
14771da177e4SLinus Torvalds {
1478100e1695SDmitry Torokhov 	struct psmouse *psmouse = psmouse_from_serio(serio);
1479c774326aSDmitry Torokhov 	struct psmouse *parent = NULL;
14801da177e4SLinus Torvalds 
1481c14471dcSIngo Molnar 	mutex_lock(&psmouse_mutex);
148204df1925SDmitry Torokhov 
14831da177e4SLinus Torvalds 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
14841da177e4SLinus Torvalds 
1485f0d5c6f4SDmitry Torokhov 	/* make sure we don't have a resync in progress */
1486c14471dcSIngo Molnar 	mutex_unlock(&psmouse_mutex);
1487f0d5c6f4SDmitry Torokhov 	flush_workqueue(kpsmoused_wq);
1488c14471dcSIngo Molnar 	mutex_lock(&psmouse_mutex);
1489f0d5c6f4SDmitry Torokhov 
14901da177e4SLinus Torvalds 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1491100e1695SDmitry Torokhov 		parent = psmouse_from_serio(serio->parent);
149204df1925SDmitry Torokhov 		psmouse_deactivate(parent);
14931da177e4SLinus Torvalds 	}
14941da177e4SLinus Torvalds 
14951da177e4SLinus Torvalds 	if (psmouse->disconnect)
14961da177e4SLinus Torvalds 		psmouse->disconnect(psmouse);
14971da177e4SLinus Torvalds 
149804df1925SDmitry Torokhov 	if (parent && parent->pt_deactivate)
149904df1925SDmitry Torokhov 		parent->pt_deactivate(parent);
150004df1925SDmitry Torokhov 
15011da177e4SLinus Torvalds 	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
15021da177e4SLinus Torvalds 
15031da177e4SLinus Torvalds 	serio_close(serio);
15041da177e4SLinus Torvalds 	serio_set_drvdata(serio, NULL);
1505c774326aSDmitry Torokhov 
1506c774326aSDmitry Torokhov 	if (psmouse->dev)
15072e5b636bSDmitry Torokhov 		input_unregister_device(psmouse->dev);
1508c774326aSDmitry Torokhov 
15091da177e4SLinus Torvalds 	kfree(psmouse);
151004df1925SDmitry Torokhov 
151104df1925SDmitry Torokhov 	if (parent)
151204df1925SDmitry Torokhov 		psmouse_activate(parent);
151304df1925SDmitry Torokhov 
1514c14471dcSIngo Molnar 	mutex_unlock(&psmouse_mutex);
15151da177e4SLinus Torvalds }
15161da177e4SLinus Torvalds 
psmouse_switch_protocol(struct psmouse * psmouse,const struct psmouse_protocol * proto)1517315eb996SDmitry Torokhov static int psmouse_switch_protocol(struct psmouse *psmouse,
1518315eb996SDmitry Torokhov 				   const struct psmouse_protocol *proto)
1519dbf4ccd6SDmitry Torokhov {
15206b9d363cSDmitry Torokhov 	const struct psmouse_protocol *selected_proto;
15212e5b636bSDmitry Torokhov 	struct input_dev *input_dev = psmouse->dev;
1522085fa80dSDmitry Torokhov 	enum psmouse_type type;
1523dbf4ccd6SDmitry Torokhov 
152428aa7f1cSDmitry Torokhov 	input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
1525dbf4ccd6SDmitry Torokhov 
1526dbf4ccd6SDmitry Torokhov 	if (proto && (proto->detect || proto->init)) {
1527ee9dfd7aSDmitry Torokhov 		psmouse_apply_defaults(psmouse);
1528ee9dfd7aSDmitry Torokhov 
1529a62f0d27SDmitry Torokhov 		if (proto->detect && proto->detect(psmouse, true) < 0)
1530dbf4ccd6SDmitry Torokhov 			return -1;
1531dbf4ccd6SDmitry Torokhov 
1532dbf4ccd6SDmitry Torokhov 		if (proto->init && proto->init(psmouse) < 0)
1533dbf4ccd6SDmitry Torokhov 			return -1;
1534dbf4ccd6SDmitry Torokhov 
15356b9d363cSDmitry Torokhov 		selected_proto = proto;
15366b9d363cSDmitry Torokhov 	} else {
1537085fa80dSDmitry Torokhov 		type = psmouse_extensions(psmouse, psmouse_max_proto, true);
1538085fa80dSDmitry Torokhov 		selected_proto = psmouse_protocol_by_type(type);
15396b9d363cSDmitry Torokhov 	}
15406b9d363cSDmitry Torokhov 
1541085fa80dSDmitry Torokhov 	psmouse->protocol = selected_proto;
1542dbf4ccd6SDmitry Torokhov 
1543f0d5c6f4SDmitry Torokhov 	/*
1544f0d5c6f4SDmitry Torokhov 	 * If mouse's packet size is 3 there is no point in polling the
1545f0d5c6f4SDmitry Torokhov 	 * device in hopes to detect protocol reset - we won't get less
1546f0d5c6f4SDmitry Torokhov 	 * than 3 bytes response anyhow.
1547f0d5c6f4SDmitry Torokhov 	 */
1548f0d5c6f4SDmitry Torokhov 	if (psmouse->pktsize == 3)
1549f0d5c6f4SDmitry Torokhov 		psmouse->resync_time = 0;
1550f0d5c6f4SDmitry Torokhov 
1551f0d5c6f4SDmitry Torokhov 	/*
1552f0d5c6f4SDmitry Torokhov 	 * Some smart KVMs fake response to POLL command returning just
1553f0d5c6f4SDmitry Torokhov 	 * 3 bytes and messing up our resync logic, so if initial poll
1554f0d5c6f4SDmitry Torokhov 	 * fails we won't try polling the device anymore. Hopefully
1555f0d5c6f4SDmitry Torokhov 	 * such KVM will maintain initially selected protocol.
1556f0d5c6f4SDmitry Torokhov 	 */
1557f0d5c6f4SDmitry Torokhov 	if (psmouse->resync_time && psmouse->poll(psmouse))
1558f0d5c6f4SDmitry Torokhov 		psmouse->resync_time = 0;
1559f0d5c6f4SDmitry Torokhov 
156008ffce45SDmitry Torokhov 	snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s",
15616b9d363cSDmitry Torokhov 		 selected_proto->name, psmouse->vendor, psmouse->name);
1562dbf4ccd6SDmitry Torokhov 
15632e5b636bSDmitry Torokhov 	input_dev->name = psmouse->devname;
15642e5b636bSDmitry Torokhov 	input_dev->phys = psmouse->phys;
15652e5b636bSDmitry Torokhov 	input_dev->id.bustype = BUS_I8042;
15662e5b636bSDmitry Torokhov 	input_dev->id.vendor = 0x0002;
1567085fa80dSDmitry Torokhov 	input_dev->id.product = psmouse->protocol->type;
15682e5b636bSDmitry Torokhov 	input_dev->id.version = psmouse->model;
1569dbf4ccd6SDmitry Torokhov 
1570dbf4ccd6SDmitry Torokhov 	return 0;
1571dbf4ccd6SDmitry Torokhov }
1572dbf4ccd6SDmitry Torokhov 
15731da177e4SLinus Torvalds /*
15741da177e4SLinus Torvalds  * psmouse_connect() is a callback from the serio module when
15751da177e4SLinus Torvalds  * an unhandled serio port is found.
15761da177e4SLinus Torvalds  */
psmouse_connect(struct serio * serio,struct serio_driver * drv)15771da177e4SLinus Torvalds static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
15781da177e4SLinus Torvalds {
15791da177e4SLinus Torvalds 	struct psmouse *psmouse, *parent = NULL;
15802e5b636bSDmitry Torokhov 	struct input_dev *input_dev;
158172155615SDmitry Torokhov 	int retval = 0, error = -ENOMEM;
15821da177e4SLinus Torvalds 
1583c14471dcSIngo Molnar 	mutex_lock(&psmouse_mutex);
158404df1925SDmitry Torokhov 
15851da177e4SLinus Torvalds 	/*
15861da177e4SLinus Torvalds 	 * If this is a pass-through port deactivate parent so the device
15871da177e4SLinus Torvalds 	 * connected to this port can be successfully identified
15881da177e4SLinus Torvalds 	 */
15891da177e4SLinus Torvalds 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1590100e1695SDmitry Torokhov 		parent = psmouse_from_serio(serio->parent);
15911da177e4SLinus Torvalds 		psmouse_deactivate(parent);
15921da177e4SLinus Torvalds 	}
15931da177e4SLinus Torvalds 
15942e5b636bSDmitry Torokhov 	psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL);
15952e5b636bSDmitry Torokhov 	input_dev = input_allocate_device();
15962e5b636bSDmitry Torokhov 	if (!psmouse || !input_dev)
159772155615SDmitry Torokhov 		goto err_free;
15981da177e4SLinus Torvalds 
1599*c4c7eac8SDmitry Torokhov 	ps2_init(&psmouse->ps2dev, serio,
1600*c4c7eac8SDmitry Torokhov 		 psmouse_pre_receive_byte, psmouse_receive_byte);
16018bf020eeSAndres Salomon 	INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync);
16022e5b636bSDmitry Torokhov 	psmouse->dev = input_dev;
160308ffce45SDmitry Torokhov 	snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys);
1604dbf4ccd6SDmitry Torokhov 
16051da177e4SLinus Torvalds 	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
16061da177e4SLinus Torvalds 
160772155615SDmitry Torokhov 	error = serio_open(serio, drv);
160872155615SDmitry Torokhov 	if (error)
160972155615SDmitry Torokhov 		goto err_clear_drvdata;
16101da177e4SLinus Torvalds 
161166bc2f51SStefan Assmann 	/* give PT device some time to settle down before probing */
161266bc2f51SStefan Assmann 	if (serio->id.type == SERIO_PS_PSTHRU)
161366bc2f51SStefan Assmann 		usleep_range(10000, 15000);
161466bc2f51SStefan Assmann 
16151da177e4SLinus Torvalds 	if (psmouse_probe(psmouse) < 0) {
161672155615SDmitry Torokhov 		error = -ENODEV;
161772155615SDmitry Torokhov 		goto err_close_serio;
16181da177e4SLinus Torvalds 	}
16191da177e4SLinus Torvalds 
16201da177e4SLinus Torvalds 	psmouse->rate = psmouse_rate;
16211da177e4SLinus Torvalds 	psmouse->resolution = psmouse_resolution;
16221da177e4SLinus Torvalds 	psmouse->resetafter = psmouse_resetafter;
1623f0d5c6f4SDmitry Torokhov 	psmouse->resync_time = parent ? 0 : psmouse_resync_time;
16241da177e4SLinus Torvalds 	psmouse->smartscroll = psmouse_smartscroll;
16251da177e4SLinus Torvalds 
1626dbf4ccd6SDmitry Torokhov 	psmouse_switch_protocol(psmouse, NULL);
16271da177e4SLinus Torvalds 
1628c774326aSDmitry Torokhov 	if (!psmouse->protocol->smbus_companion) {
16291da177e4SLinus Torvalds 		psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
16301da177e4SLinus Torvalds 		psmouse_initialize(psmouse);
16311da177e4SLinus Torvalds 
1632c774326aSDmitry Torokhov 		error = input_register_device(input_dev);
163372155615SDmitry Torokhov 		if (error)
163472155615SDmitry Torokhov 			goto err_protocol_disconnect;
1635c774326aSDmitry Torokhov 	} else {
1636c774326aSDmitry Torokhov 		/* Smbus companion will be reporting events, not us. */
1637c774326aSDmitry Torokhov 		input_free_device(input_dev);
1638c774326aSDmitry Torokhov 		psmouse->dev = input_dev = NULL;
1639c774326aSDmitry Torokhov 	}
16402e5b636bSDmitry Torokhov 
16411da177e4SLinus Torvalds 	if (parent && parent->pt_activate)
16421da177e4SLinus Torvalds 		parent->pt_activate(parent);
16431da177e4SLinus Torvalds 
1644c774326aSDmitry Torokhov 	/*
1645c774326aSDmitry Torokhov 	 * PS/2 devices having SMBus companions should stay disabled
1646c774326aSDmitry Torokhov 	 * on PS/2 side, in order to have SMBus part operable.
1647c774326aSDmitry Torokhov 	 */
1648c774326aSDmitry Torokhov 	if (!psmouse->protocol->smbus_companion)
16491da177e4SLinus Torvalds 		psmouse_activate(psmouse);
16501da177e4SLinus Torvalds 
16511da177e4SLinus Torvalds  out:
165204df1925SDmitry Torokhov 	/* If this is a pass-through port the parent needs to be re-activated */
16531da177e4SLinus Torvalds 	if (parent)
16541da177e4SLinus Torvalds 		psmouse_activate(parent);
16551da177e4SLinus Torvalds 
1656c14471dcSIngo Molnar 	mutex_unlock(&psmouse_mutex);
16571da177e4SLinus Torvalds 	return retval;
165872155615SDmitry Torokhov 
165972155615SDmitry Torokhov  err_protocol_disconnect:
166072155615SDmitry Torokhov 	if (psmouse->disconnect)
166172155615SDmitry Torokhov 		psmouse->disconnect(psmouse);
166272155615SDmitry Torokhov 	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
166372155615SDmitry Torokhov  err_close_serio:
166472155615SDmitry Torokhov 	serio_close(serio);
166572155615SDmitry Torokhov  err_clear_drvdata:
166672155615SDmitry Torokhov 	serio_set_drvdata(serio, NULL);
166772155615SDmitry Torokhov  err_free:
166872155615SDmitry Torokhov 	input_free_device(input_dev);
166972155615SDmitry Torokhov 	kfree(psmouse);
167072155615SDmitry Torokhov 
167172155615SDmitry Torokhov 	retval = error;
167272155615SDmitry Torokhov 	goto out;
16731da177e4SLinus Torvalds }
16741da177e4SLinus Torvalds 
__psmouse_reconnect(struct serio * serio,bool fast_reconnect)16750ab3fa57SDmitry Torokhov static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
16761da177e4SLinus Torvalds {
1677100e1695SDmitry Torokhov 	struct psmouse *psmouse = psmouse_from_serio(serio);
16781da177e4SLinus Torvalds 	struct psmouse *parent = NULL;
16790ab3fa57SDmitry Torokhov 	int (*reconnect_handler)(struct psmouse *);
1680085fa80dSDmitry Torokhov 	enum psmouse_type type;
16811da177e4SLinus Torvalds 	int rc = -1;
16821da177e4SLinus Torvalds 
1683c14471dcSIngo Molnar 	mutex_lock(&psmouse_mutex);
168404df1925SDmitry Torokhov 
16850ab3fa57SDmitry Torokhov 	if (fast_reconnect) {
16860ab3fa57SDmitry Torokhov 		reconnect_handler = psmouse->fast_reconnect;
16870ab3fa57SDmitry Torokhov 		if (!reconnect_handler) {
16880ab3fa57SDmitry Torokhov 			rc = -ENOENT;
16890ab3fa57SDmitry Torokhov 			goto out_unlock;
16900ab3fa57SDmitry Torokhov 		}
16910ab3fa57SDmitry Torokhov 	} else {
16920ab3fa57SDmitry Torokhov 		reconnect_handler = psmouse->reconnect;
16930ab3fa57SDmitry Torokhov 	}
16940ab3fa57SDmitry Torokhov 
16951da177e4SLinus Torvalds 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1696100e1695SDmitry Torokhov 		parent = psmouse_from_serio(serio->parent);
16971da177e4SLinus Torvalds 		psmouse_deactivate(parent);
16981da177e4SLinus Torvalds 	}
16991da177e4SLinus Torvalds 
17001da177e4SLinus Torvalds 	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
17011da177e4SLinus Torvalds 
17020ab3fa57SDmitry Torokhov 	if (reconnect_handler) {
17030ab3fa57SDmitry Torokhov 		if (reconnect_handler(psmouse))
17041da177e4SLinus Torvalds 			goto out;
1705ef110b24SDmitry Torokhov 	} else {
1706ef110b24SDmitry Torokhov 		psmouse_reset(psmouse);
1707ef110b24SDmitry Torokhov 
1708ef110b24SDmitry Torokhov 		if (psmouse_probe(psmouse) < 0)
1709ef110b24SDmitry Torokhov 			goto out;
1710ef110b24SDmitry Torokhov 
1711ef110b24SDmitry Torokhov 		type = psmouse_extensions(psmouse, psmouse_max_proto, false);
1712085fa80dSDmitry Torokhov 		if (psmouse->protocol->type != type)
17131da177e4SLinus Torvalds 			goto out;
1714b7802c5cSDmitry Torokhov 	}
17151da177e4SLinus Torvalds 
1716b5d21704SDmitry Torokhov 	/*
1717b5d21704SDmitry Torokhov 	 * OK, the device type (and capabilities) match the old one,
1718b5d21704SDmitry Torokhov 	 * we can continue using it, complete initialization
17191da177e4SLinus Torvalds 	 */
1720c774326aSDmitry Torokhov 	if (!psmouse->protocol->smbus_companion) {
17211da177e4SLinus Torvalds 		psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
17221da177e4SLinus Torvalds 		psmouse_initialize(psmouse);
1723c774326aSDmitry Torokhov 	}
17241da177e4SLinus Torvalds 
17251da177e4SLinus Torvalds 	if (parent && parent->pt_activate)
17261da177e4SLinus Torvalds 		parent->pt_activate(parent);
17271da177e4SLinus Torvalds 
1728c774326aSDmitry Torokhov 	/*
1729c774326aSDmitry Torokhov 	 * PS/2 devices having SMBus companions should stay disabled
1730c774326aSDmitry Torokhov 	 * on PS/2 side, in order to have SMBus part operable.
1731c774326aSDmitry Torokhov 	 */
1732c774326aSDmitry Torokhov 	if (!psmouse->protocol->smbus_companion)
17331da177e4SLinus Torvalds 		psmouse_activate(psmouse);
1734c774326aSDmitry Torokhov 
17351da177e4SLinus Torvalds 	rc = 0;
17361da177e4SLinus Torvalds 
17371da177e4SLinus Torvalds out:
17381da177e4SLinus Torvalds 	/* If this is a pass-through port the parent waits to be activated */
17391da177e4SLinus Torvalds 	if (parent)
17401da177e4SLinus Torvalds 		psmouse_activate(parent);
17411da177e4SLinus Torvalds 
17420ab3fa57SDmitry Torokhov out_unlock:
1743c14471dcSIngo Molnar 	mutex_unlock(&psmouse_mutex);
17441da177e4SLinus Torvalds 	return rc;
17451da177e4SLinus Torvalds }
17461da177e4SLinus Torvalds 
psmouse_reconnect(struct serio * serio)17470ab3fa57SDmitry Torokhov static int psmouse_reconnect(struct serio *serio)
17480ab3fa57SDmitry Torokhov {
17490ab3fa57SDmitry Torokhov 	return __psmouse_reconnect(serio, false);
17500ab3fa57SDmitry Torokhov }
17510ab3fa57SDmitry Torokhov 
psmouse_fast_reconnect(struct serio * serio)17520ab3fa57SDmitry Torokhov static int psmouse_fast_reconnect(struct serio *serio)
17530ab3fa57SDmitry Torokhov {
17540ab3fa57SDmitry Torokhov 	return __psmouse_reconnect(serio, true);
17550ab3fa57SDmitry Torokhov }
17560ab3fa57SDmitry Torokhov 
17571da177e4SLinus Torvalds static struct serio_device_id psmouse_serio_ids[] = {
17581da177e4SLinus Torvalds 	{
17591da177e4SLinus Torvalds 		.type	= SERIO_8042,
17601da177e4SLinus Torvalds 		.proto	= SERIO_ANY,
17611da177e4SLinus Torvalds 		.id	= SERIO_ANY,
17621da177e4SLinus Torvalds 		.extra	= SERIO_ANY,
17631da177e4SLinus Torvalds 	},
17641da177e4SLinus Torvalds 	{
17651da177e4SLinus Torvalds 		.type	= SERIO_PS_PSTHRU,
17661da177e4SLinus Torvalds 		.proto	= SERIO_ANY,
17671da177e4SLinus Torvalds 		.id	= SERIO_ANY,
17681da177e4SLinus Torvalds 		.extra	= SERIO_ANY,
17691da177e4SLinus Torvalds 	},
17701da177e4SLinus Torvalds 	{ 0 }
17711da177e4SLinus Torvalds };
17721da177e4SLinus Torvalds 
17731da177e4SLinus Torvalds MODULE_DEVICE_TABLE(serio, psmouse_serio_ids);
17741da177e4SLinus Torvalds 
17751da177e4SLinus Torvalds static struct serio_driver psmouse_drv = {
17761da177e4SLinus Torvalds 	.driver		= {
17771da177e4SLinus Torvalds 		.name		= "psmouse",
1778fd30a4baSDmitry Torokhov 		.dev_groups	= psmouse_dev_groups,
17791da177e4SLinus Torvalds 	},
17801da177e4SLinus Torvalds 	.description	= DRIVER_DESC,
17811da177e4SLinus Torvalds 	.id_table	= psmouse_serio_ids,
1782*c4c7eac8SDmitry Torokhov 	.interrupt	= ps2_interrupt,
17831da177e4SLinus Torvalds 	.connect	= psmouse_connect,
17841da177e4SLinus Torvalds 	.reconnect	= psmouse_reconnect,
17850ab3fa57SDmitry Torokhov 	.fast_reconnect	= psmouse_fast_reconnect,
17861da177e4SLinus Torvalds 	.disconnect	= psmouse_disconnect,
17871da177e4SLinus Torvalds 	.cleanup	= psmouse_cleanup,
17881da177e4SLinus Torvalds };
17891da177e4SLinus Torvalds 
psmouse_attr_show_helper(struct device * dev,struct device_attribute * devattr,char * buf)1790cfe9e888SDmitry Torokhov ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr,
1791cfe9e888SDmitry Torokhov 				 char *buf)
17921da177e4SLinus Torvalds {
17931da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
1794cfe9e888SDmitry Torokhov 	struct psmouse_attribute *attr = to_psmouse_attr(devattr);
1795100e1695SDmitry Torokhov 	struct psmouse *psmouse = psmouse_from_serio(serio);
17961da177e4SLinus Torvalds 
1797c774326aSDmitry Torokhov 	if (psmouse->protocol->smbus_companion &&
1798c774326aSDmitry Torokhov 			devattr != &psmouse_attr_protocol.dattr)
1799c774326aSDmitry Torokhov 		return -ENOENT;
1800cfe9e888SDmitry Torokhov 
180159b01513SEric W. Biederman 	return attr->show(psmouse, attr->data, buf);
18021da177e4SLinus Torvalds }
18031da177e4SLinus Torvalds 
psmouse_attr_set_helper(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)1804cfe9e888SDmitry Torokhov ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *devattr,
1805cfe9e888SDmitry Torokhov 				const char *buf, size_t count)
18061da177e4SLinus Torvalds {
18071da177e4SLinus Torvalds 	struct serio *serio = to_serio_port(dev);
1808cfe9e888SDmitry Torokhov 	struct psmouse_attribute *attr = to_psmouse_attr(devattr);
1809cfe9e888SDmitry Torokhov 	struct psmouse *psmouse, *parent = NULL;
18101da177e4SLinus Torvalds 	int retval;
18111da177e4SLinus Torvalds 
1812c14471dcSIngo Molnar 	retval = mutex_lock_interruptible(&psmouse_mutex);
181304df1925SDmitry Torokhov 	if (retval)
181459b01513SEric W. Biederman 		goto out;
181504df1925SDmitry Torokhov 
1816100e1695SDmitry Torokhov 	psmouse = psmouse_from_serio(serio);
1817cfe9e888SDmitry Torokhov 
1818c774326aSDmitry Torokhov 	if (psmouse->protocol->smbus_companion &&
1819c774326aSDmitry Torokhov 			devattr != &psmouse_attr_protocol.dattr) {
1820c774326aSDmitry Torokhov 		retval = -ENOENT;
1821c774326aSDmitry Torokhov 		goto out_unlock;
1822c774326aSDmitry Torokhov 	}
1823c774326aSDmitry Torokhov 
182468d48221SAndres Salomon 	if (attr->protect) {
182504df1925SDmitry Torokhov 		if (psmouse->state == PSMOUSE_IGNORE) {
182604df1925SDmitry Torokhov 			retval = -ENODEV;
1827c14471dcSIngo Molnar 			goto out_unlock;
18281da177e4SLinus Torvalds 		}
18291da177e4SLinus Torvalds 
18301da177e4SLinus Torvalds 		if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1831100e1695SDmitry Torokhov 			parent = psmouse_from_serio(serio->parent);
18321da177e4SLinus Torvalds 			psmouse_deactivate(parent);
18331da177e4SLinus Torvalds 		}
1834dbf4ccd6SDmitry Torokhov 
1835c774326aSDmitry Torokhov 		if (!psmouse->protocol->smbus_companion)
18361da177e4SLinus Torvalds 			psmouse_deactivate(psmouse);
183768d48221SAndres Salomon 	}
18381da177e4SLinus Torvalds 
1839cfe9e888SDmitry Torokhov 	retval = attr->set(psmouse, attr->data, buf, count);
18401da177e4SLinus Torvalds 
184168d48221SAndres Salomon 	if (attr->protect) {
1842c774326aSDmitry Torokhov 		if (retval != -ENODEV && !psmouse->protocol->smbus_companion)
18431da177e4SLinus Torvalds 			psmouse_activate(psmouse);
1844dbf4ccd6SDmitry Torokhov 
18451da177e4SLinus Torvalds 		if (parent)
18461da177e4SLinus Torvalds 			psmouse_activate(parent);
184768d48221SAndres Salomon 	}
18481da177e4SLinus Torvalds 
1849c14471dcSIngo Molnar  out_unlock:
1850c14471dcSIngo Molnar 	mutex_unlock(&psmouse_mutex);
185159b01513SEric W. Biederman  out:
18521da177e4SLinus Torvalds 	return retval;
18531da177e4SLinus Torvalds }
18541da177e4SLinus Torvalds 
psmouse_show_int_attr(struct psmouse * psmouse,void * offset,char * buf)1855cfe9e888SDmitry Torokhov static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf)
1856cfe9e888SDmitry Torokhov {
1857eb5d5829SSergey Vlasov 	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
1858cfe9e888SDmitry Torokhov 
1859eb5d5829SSergey Vlasov 	return sprintf(buf, "%u\n", *field);
1860cfe9e888SDmitry Torokhov }
1861cfe9e888SDmitry Torokhov 
psmouse_set_int_attr(struct psmouse * psmouse,void * offset,const char * buf,size_t count)1862cfe9e888SDmitry Torokhov static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count)
1863cfe9e888SDmitry Torokhov {
1864eb5d5829SSergey Vlasov 	unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset);
186576496e7aSJJ Ding 	unsigned int value;
186676496e7aSJJ Ding 	int err;
1867cfe9e888SDmitry Torokhov 
186876496e7aSJJ Ding 	err = kstrtouint(buf, 10, &value);
186976496e7aSJJ Ding 	if (err)
187076496e7aSJJ Ding 		return err;
1871eb5d5829SSergey Vlasov 
1872cfe9e888SDmitry Torokhov 	*field = value;
1873cfe9e888SDmitry Torokhov 
1874cfe9e888SDmitry Torokhov 	return count;
1875cfe9e888SDmitry Torokhov }
1876cfe9e888SDmitry Torokhov 
psmouse_attr_show_protocol(struct psmouse * psmouse,void * data,char * buf)1877cfe9e888SDmitry Torokhov static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
1878dbf4ccd6SDmitry Torokhov {
1879085fa80dSDmitry Torokhov 	return sprintf(buf, "%s\n", psmouse->protocol->name);
1880dbf4ccd6SDmitry Torokhov }
1881dbf4ccd6SDmitry Torokhov 
psmouse_attr_set_protocol(struct psmouse * psmouse,void * data,const char * buf,size_t count)1882cfe9e888SDmitry Torokhov static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
1883dbf4ccd6SDmitry Torokhov {
1884dbf4ccd6SDmitry Torokhov 	struct serio *serio = psmouse->ps2dev.serio;
1885dbf4ccd6SDmitry Torokhov 	struct psmouse *parent = NULL;
188672155615SDmitry Torokhov 	struct input_dev *old_dev, *new_dev;
188772155615SDmitry Torokhov 	const struct psmouse_protocol *proto, *old_proto;
188872155615SDmitry Torokhov 	int error;
1889dbf4ccd6SDmitry Torokhov 	int retry = 0;
1890dbf4ccd6SDmitry Torokhov 
189172155615SDmitry Torokhov 	proto = psmouse_protocol_by_name(buf, count);
189272155615SDmitry Torokhov 	if (!proto)
1893dbf4ccd6SDmitry Torokhov 		return -EINVAL;
1894dbf4ccd6SDmitry Torokhov 
1895085fa80dSDmitry Torokhov 	if (psmouse->protocol == proto)
1896dbf4ccd6SDmitry Torokhov 		return count;
1897dbf4ccd6SDmitry Torokhov 
189872155615SDmitry Torokhov 	new_dev = input_allocate_device();
189972155615SDmitry Torokhov 	if (!new_dev)
19002e5b636bSDmitry Torokhov 		return -ENOMEM;
19012e5b636bSDmitry Torokhov 
190209822582SDmitry Eremin-Solenikov 	while (!list_empty(&serio->children)) {
1903dbf4ccd6SDmitry Torokhov 		if (++retry > 3) {
1904b5d21704SDmitry Torokhov 			psmouse_warn(psmouse,
1905b5d21704SDmitry Torokhov 				     "failed to destroy children ports, protocol change aborted.\n");
19062e5b636bSDmitry Torokhov 			input_free_device(new_dev);
1907dbf4ccd6SDmitry Torokhov 			return -EIO;
1908dbf4ccd6SDmitry Torokhov 		}
1909dbf4ccd6SDmitry Torokhov 
1910c14471dcSIngo Molnar 		mutex_unlock(&psmouse_mutex);
1911dbf4ccd6SDmitry Torokhov 		serio_unregister_child_port(serio);
1912c14471dcSIngo Molnar 		mutex_lock(&psmouse_mutex);
1913dbf4ccd6SDmitry Torokhov 
19142e5b636bSDmitry Torokhov 		if (serio->drv != &psmouse_drv) {
19152e5b636bSDmitry Torokhov 			input_free_device(new_dev);
1916dbf4ccd6SDmitry Torokhov 			return -ENODEV;
19172e5b636bSDmitry Torokhov 		}
1918dbf4ccd6SDmitry Torokhov 
1919085fa80dSDmitry Torokhov 		if (psmouse->protocol == proto) {
19202e5b636bSDmitry Torokhov 			input_free_device(new_dev);
1921dbf4ccd6SDmitry Torokhov 			return count; /* switched by other thread */
1922dbf4ccd6SDmitry Torokhov 		}
19232e5b636bSDmitry Torokhov 	}
1924dbf4ccd6SDmitry Torokhov 
1925dbf4ccd6SDmitry Torokhov 	if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
1926100e1695SDmitry Torokhov 		parent = psmouse_from_serio(serio->parent);
1927dbf4ccd6SDmitry Torokhov 		if (parent->pt_deactivate)
1928dbf4ccd6SDmitry Torokhov 			parent->pt_deactivate(parent);
1929dbf4ccd6SDmitry Torokhov 	}
1930dbf4ccd6SDmitry Torokhov 
193172155615SDmitry Torokhov 	old_dev = psmouse->dev;
1932085fa80dSDmitry Torokhov 	old_proto = psmouse->protocol;
193372155615SDmitry Torokhov 
1934dbf4ccd6SDmitry Torokhov 	if (psmouse->disconnect)
1935dbf4ccd6SDmitry Torokhov 		psmouse->disconnect(psmouse);
1936dbf4ccd6SDmitry Torokhov 
1937dbf4ccd6SDmitry Torokhov 	psmouse_set_state(psmouse, PSMOUSE_IGNORE);
1938dbf4ccd6SDmitry Torokhov 
19392e5b636bSDmitry Torokhov 	psmouse->dev = new_dev;
1940dbf4ccd6SDmitry Torokhov 	psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
1941dbf4ccd6SDmitry Torokhov 
1942dbf4ccd6SDmitry Torokhov 	if (psmouse_switch_protocol(psmouse, proto) < 0) {
1943dbf4ccd6SDmitry Torokhov 		psmouse_reset(psmouse);
1944dbf4ccd6SDmitry Torokhov 		/* default to PSMOUSE_PS2 */
1945dbf4ccd6SDmitry Torokhov 		psmouse_switch_protocol(psmouse, &psmouse_protocols[0]);
1946dbf4ccd6SDmitry Torokhov 	}
1947dbf4ccd6SDmitry Torokhov 
1948dbf4ccd6SDmitry Torokhov 	psmouse_initialize(psmouse);
1949dbf4ccd6SDmitry Torokhov 	psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
1950dbf4ccd6SDmitry Torokhov 
1951c774326aSDmitry Torokhov 	if (psmouse->protocol->smbus_companion) {
1952c774326aSDmitry Torokhov 		input_free_device(psmouse->dev);
1953c774326aSDmitry Torokhov 		psmouse->dev = NULL;
1954c774326aSDmitry Torokhov 	} else {
195572155615SDmitry Torokhov 		error = input_register_device(psmouse->dev);
195672155615SDmitry Torokhov 		if (error) {
195772155615SDmitry Torokhov 			if (psmouse->disconnect)
195872155615SDmitry Torokhov 				psmouse->disconnect(psmouse);
195972155615SDmitry Torokhov 
196072155615SDmitry Torokhov 			psmouse_set_state(psmouse, PSMOUSE_IGNORE);
196172155615SDmitry Torokhov 			input_free_device(new_dev);
196272155615SDmitry Torokhov 			psmouse->dev = old_dev;
196372155615SDmitry Torokhov 			psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
196472155615SDmitry Torokhov 			psmouse_switch_protocol(psmouse, old_proto);
196572155615SDmitry Torokhov 			psmouse_initialize(psmouse);
196672155615SDmitry Torokhov 			psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
196772155615SDmitry Torokhov 
196872155615SDmitry Torokhov 			return error;
196972155615SDmitry Torokhov 		}
1970c774326aSDmitry Torokhov 	}
197172155615SDmitry Torokhov 
1972c774326aSDmitry Torokhov 	if (old_dev)
197372155615SDmitry Torokhov 		input_unregister_device(old_dev);
1974dbf4ccd6SDmitry Torokhov 
1975dbf4ccd6SDmitry Torokhov 	if (parent && parent->pt_activate)
1976dbf4ccd6SDmitry Torokhov 		parent->pt_activate(parent);
1977dbf4ccd6SDmitry Torokhov 
1978dbf4ccd6SDmitry Torokhov 	return count;
1979dbf4ccd6SDmitry Torokhov }
1980dbf4ccd6SDmitry Torokhov 
psmouse_attr_set_rate(struct psmouse * psmouse,void * data,const char * buf,size_t count)1981cfe9e888SDmitry Torokhov static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count)
19821da177e4SLinus Torvalds {
198376496e7aSJJ Ding 	unsigned int value;
198476496e7aSJJ Ding 	int err;
19851da177e4SLinus Torvalds 
198676496e7aSJJ Ding 	err = kstrtouint(buf, 10, &value);
198776496e7aSJJ Ding 	if (err)
198876496e7aSJJ Ding 		return err;
19891da177e4SLinus Torvalds 
19901da177e4SLinus Torvalds 	psmouse->set_rate(psmouse, value);
19911da177e4SLinus Torvalds 	return count;
19921da177e4SLinus Torvalds }
19931da177e4SLinus Torvalds 
psmouse_attr_set_resolution(struct psmouse * psmouse,void * data,const char * buf,size_t count)1994cfe9e888SDmitry Torokhov static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count)
19951da177e4SLinus Torvalds {
199676496e7aSJJ Ding 	unsigned int value;
199776496e7aSJJ Ding 	int err;
19981da177e4SLinus Torvalds 
199976496e7aSJJ Ding 	err = kstrtouint(buf, 10, &value);
200076496e7aSJJ Ding 	if (err)
200176496e7aSJJ Ding 		return err;
20021da177e4SLinus Torvalds 
20031da177e4SLinus Torvalds 	psmouse->set_resolution(psmouse, value);
20041da177e4SLinus Torvalds 	return count;
20051da177e4SLinus Torvalds }
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 
psmouse_set_maxproto(const char * val,const struct kernel_param * kp)20089bbb9e5aSRusty Russell static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp)
20091da177e4SLinus Torvalds {
2010e38de678SHelge Deller 	const struct psmouse_protocol *proto;
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds 	if (!val)
20131da177e4SLinus Torvalds 		return -EINVAL;
20141da177e4SLinus Torvalds 
2015dbf4ccd6SDmitry Torokhov 	proto = psmouse_protocol_by_name(val, strlen(val));
20161da177e4SLinus Torvalds 
2017dbf4ccd6SDmitry Torokhov 	if (!proto || !proto->maxproto)
2018dbf4ccd6SDmitry Torokhov 		return -EINVAL;
20191da177e4SLinus Torvalds 
2020dbf4ccd6SDmitry Torokhov 	*((unsigned int *)kp->arg) = proto->type;
20211da177e4SLinus Torvalds 
2022541e316aSStephen Evanchik 	return 0;
20231da177e4SLinus Torvalds }
20241da177e4SLinus Torvalds 
psmouse_get_maxproto(char * buffer,const struct kernel_param * kp)20259bbb9e5aSRusty Russell static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp)
20261da177e4SLinus Torvalds {
2027dbf4ccd6SDmitry Torokhov 	int type = *((unsigned int *)kp->arg);
2028dbf4ccd6SDmitry Torokhov 
20294aec14deSXiongfeng Wang 	return sprintf(buffer, "%s\n", psmouse_protocol_by_type(type)->name);
20301da177e4SLinus Torvalds }
20311da177e4SLinus Torvalds 
psmouse_init(void)20321da177e4SLinus Torvalds static int __init psmouse_init(void)
20331da177e4SLinus Torvalds {
2034153a9df0SAkinobu Mita 	int err;
2035153a9df0SAkinobu Mita 
20367705d548SDmitry Torokhov 	lifebook_module_init();
20377705d548SDmitry Torokhov 	synaptics_module_init();
2038ca94ec43SDaniel Drake 	hgpk_module_init();
20397705d548SDmitry Torokhov 
20408eb92e5cSBenjamin Tissoires 	err = psmouse_smbus_module_init();
20418eb92e5cSBenjamin Tissoires 	if (err)
20428eb92e5cSBenjamin Tissoires 		return err;
20438eb92e5cSBenjamin Tissoires 
204424dde60fSBhaktipriya Shridhar 	kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
2045f0d5c6f4SDmitry Torokhov 	if (!kpsmoused_wq) {
2046b5d21704SDmitry Torokhov 		pr_err("failed to create kpsmoused workqueue\n");
20478eb92e5cSBenjamin Tissoires 		err = -ENOMEM;
20488eb92e5cSBenjamin Tissoires 		goto err_smbus_exit;
2049f0d5c6f4SDmitry Torokhov 	}
2050f0d5c6f4SDmitry Torokhov 
2051153a9df0SAkinobu Mita 	err = serio_register_driver(&psmouse_drv);
2052153a9df0SAkinobu Mita 	if (err)
20538eb92e5cSBenjamin Tissoires 		goto err_destroy_wq;
2054f0d5c6f4SDmitry Torokhov 
20558eb92e5cSBenjamin Tissoires 	return 0;
20568eb92e5cSBenjamin Tissoires 
20578eb92e5cSBenjamin Tissoires err_destroy_wq:
20588eb92e5cSBenjamin Tissoires 	destroy_workqueue(kpsmoused_wq);
20598eb92e5cSBenjamin Tissoires err_smbus_exit:
20608eb92e5cSBenjamin Tissoires 	psmouse_smbus_module_exit();
2061153a9df0SAkinobu Mita 	return err;
20621da177e4SLinus Torvalds }
20631da177e4SLinus Torvalds 
psmouse_exit(void)20641da177e4SLinus Torvalds static void __exit psmouse_exit(void)
20651da177e4SLinus Torvalds {
20661da177e4SLinus Torvalds 	serio_unregister_driver(&psmouse_drv);
2067f0d5c6f4SDmitry Torokhov 	destroy_workqueue(kpsmoused_wq);
20688eb92e5cSBenjamin Tissoires 	psmouse_smbus_module_exit();
20691da177e4SLinus Torvalds }
20701da177e4SLinus Torvalds 
20711da177e4SLinus Torvalds module_init(psmouse_init);
20721da177e4SLinus Torvalds module_exit(psmouse_exit);
2073