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