xref: /linux-6.15/drivers/input/mouse/logips2pp.c (revision 1da177e4)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  * Logitech PS/2++ mouse driver
3*1da177e4SLinus Torvalds  *
4*1da177e4SLinus Torvalds  * Copyright (c) 1999-2003 Vojtech Pavlik <[email protected]>
5*1da177e4SLinus Torvalds  * Copyright (c) 2003 Eric Wong <[email protected]>
6*1da177e4SLinus Torvalds  *
7*1da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify it
8*1da177e4SLinus Torvalds  * under the terms of the GNU General Public License version 2 as published by
9*1da177e4SLinus Torvalds  * the Free Software Foundation.
10*1da177e4SLinus Torvalds  */
11*1da177e4SLinus Torvalds 
12*1da177e4SLinus Torvalds #include <linux/input.h>
13*1da177e4SLinus Torvalds #include <linux/serio.h>
14*1da177e4SLinus Torvalds #include <linux/libps2.h>
15*1da177e4SLinus Torvalds #include "psmouse.h"
16*1da177e4SLinus Torvalds #include "logips2pp.h"
17*1da177e4SLinus Torvalds 
18*1da177e4SLinus Torvalds /* Logitech mouse types */
19*1da177e4SLinus Torvalds #define PS2PP_KIND_WHEEL	1
20*1da177e4SLinus Torvalds #define PS2PP_KIND_MX		2
21*1da177e4SLinus Torvalds #define PS2PP_KIND_TP3		3
22*1da177e4SLinus Torvalds 
23*1da177e4SLinus Torvalds /* Logitech mouse features */
24*1da177e4SLinus Torvalds #define PS2PP_WHEEL		0x01
25*1da177e4SLinus Torvalds #define PS2PP_HWHEEL		0x02
26*1da177e4SLinus Torvalds #define PS2PP_SIDE_BTN		0x04
27*1da177e4SLinus Torvalds #define PS2PP_EXTRA_BTN		0x08
28*1da177e4SLinus Torvalds #define PS2PP_TASK_BTN		0x10
29*1da177e4SLinus Torvalds #define PS2PP_NAV_BTN		0x20
30*1da177e4SLinus Torvalds 
31*1da177e4SLinus Torvalds struct ps2pp_info {
32*1da177e4SLinus Torvalds 	const int model;
33*1da177e4SLinus Torvalds 	unsigned const int kind;
34*1da177e4SLinus Torvalds 	unsigned const int features;
35*1da177e4SLinus Torvalds };
36*1da177e4SLinus Torvalds 
37*1da177e4SLinus Torvalds /*
38*1da177e4SLinus Torvalds  * Process a PS2++ or PS2T++ packet.
39*1da177e4SLinus Torvalds  */
40*1da177e4SLinus Torvalds 
41*1da177e4SLinus Torvalds static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse, struct pt_regs *regs)
42*1da177e4SLinus Torvalds {
43*1da177e4SLinus Torvalds 	struct input_dev *dev = &psmouse->dev;
44*1da177e4SLinus Torvalds 	unsigned char *packet = psmouse->packet;
45*1da177e4SLinus Torvalds 
46*1da177e4SLinus Torvalds 	if (psmouse->pktcnt < 3)
47*1da177e4SLinus Torvalds 		return PSMOUSE_GOOD_DATA;
48*1da177e4SLinus Torvalds 
49*1da177e4SLinus Torvalds /*
50*1da177e4SLinus Torvalds  * Full packet accumulated, process it
51*1da177e4SLinus Torvalds  */
52*1da177e4SLinus Torvalds 
53*1da177e4SLinus Torvalds 	input_regs(dev, regs);
54*1da177e4SLinus Torvalds 
55*1da177e4SLinus Torvalds 	if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) {
56*1da177e4SLinus Torvalds 
57*1da177e4SLinus Torvalds 		/* Logitech extended packet */
58*1da177e4SLinus Torvalds 		switch ((packet[1] >> 4) | (packet[0] & 0x30)) {
59*1da177e4SLinus Torvalds 
60*1da177e4SLinus Torvalds 			case 0x0d: /* Mouse extra info */
61*1da177e4SLinus Torvalds 
62*1da177e4SLinus Torvalds 				input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL,
63*1da177e4SLinus Torvalds 					(int) (packet[2] & 8) - (int) (packet[2] & 7));
64*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1);
65*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_EXTRA, (packet[2] >> 5) & 1);
66*1da177e4SLinus Torvalds 
67*1da177e4SLinus Torvalds 				break;
68*1da177e4SLinus Torvalds 
69*1da177e4SLinus Torvalds 			case 0x0e: /* buttons 4, 5, 6, 7, 8, 9, 10 info */
70*1da177e4SLinus Torvalds 
71*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_SIDE, (packet[2]) & 1);
72*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1);
73*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1);
74*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1);
75*1da177e4SLinus Torvalds 				input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1);
76*1da177e4SLinus Torvalds 
77*1da177e4SLinus Torvalds 				break;
78*1da177e4SLinus Torvalds 
79*1da177e4SLinus Torvalds 			case 0x0f: /* TouchPad extra info */
80*1da177e4SLinus Torvalds 
81*1da177e4SLinus Torvalds 				input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL,
82*1da177e4SLinus Torvalds 					(int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7));
83*1da177e4SLinus Torvalds 				packet[0] = packet[2] | 0x08;
84*1da177e4SLinus Torvalds 				break;
85*1da177e4SLinus Torvalds 
86*1da177e4SLinus Torvalds #ifdef DEBUG
87*1da177e4SLinus Torvalds 			default:
88*1da177e4SLinus Torvalds 				printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n",
89*1da177e4SLinus Torvalds 					(packet[1] >> 4) | (packet[0] & 0x30));
90*1da177e4SLinus Torvalds #endif
91*1da177e4SLinus Torvalds 		}
92*1da177e4SLinus Torvalds 	} else {
93*1da177e4SLinus Torvalds 		/* Standard PS/2 motion data */
94*1da177e4SLinus Torvalds 		input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0);
95*1da177e4SLinus Torvalds 		input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0);
96*1da177e4SLinus Torvalds 	}
97*1da177e4SLinus Torvalds 
98*1da177e4SLinus Torvalds 	input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
99*1da177e4SLinus Torvalds 	input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
100*1da177e4SLinus Torvalds 	input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
101*1da177e4SLinus Torvalds 
102*1da177e4SLinus Torvalds 	input_sync(dev);
103*1da177e4SLinus Torvalds 
104*1da177e4SLinus Torvalds 	return PSMOUSE_FULL_PACKET;
105*1da177e4SLinus Torvalds 
106*1da177e4SLinus Torvalds }
107*1da177e4SLinus Torvalds 
108*1da177e4SLinus Torvalds /*
109*1da177e4SLinus Torvalds  * ps2pp_cmd() sends a PS2++ command, sliced into two bit
110*1da177e4SLinus Torvalds  * pieces through the SETRES command. This is needed to send extended
111*1da177e4SLinus Torvalds  * commands to mice on notebooks that try to understand the PS/2 protocol
112*1da177e4SLinus Torvalds  * Ugly.
113*1da177e4SLinus Torvalds  */
114*1da177e4SLinus Torvalds 
115*1da177e4SLinus Torvalds static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command)
116*1da177e4SLinus Torvalds {
117*1da177e4SLinus Torvalds 	if (psmouse_sliced_command(psmouse, command))
118*1da177e4SLinus Torvalds 		return -1;
119*1da177e4SLinus Torvalds 
120*1da177e4SLinus Torvalds 	if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL))
121*1da177e4SLinus Torvalds 		return -1;
122*1da177e4SLinus Torvalds 
123*1da177e4SLinus Torvalds 	return 0;
124*1da177e4SLinus Torvalds }
125*1da177e4SLinus Torvalds 
126*1da177e4SLinus Torvalds /*
127*1da177e4SLinus Torvalds  * SmartScroll / CruiseControl for some newer Logitech mice Defaults to
128*1da177e4SLinus Torvalds  * enabled if we do nothing to it. Of course I put this in because I want it
129*1da177e4SLinus Torvalds  * disabled :P
130*1da177e4SLinus Torvalds  * 1 - enabled (if previously disabled, also default)
131*1da177e4SLinus Torvalds  * 0 - disabled
132*1da177e4SLinus Torvalds  */
133*1da177e4SLinus Torvalds 
134*1da177e4SLinus Torvalds static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll)
135*1da177e4SLinus Torvalds {
136*1da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
137*1da177e4SLinus Torvalds 	unsigned char param[4];
138*1da177e4SLinus Torvalds 
139*1da177e4SLinus Torvalds 	if (smartscroll > 1)
140*1da177e4SLinus Torvalds 		smartscroll = 1;
141*1da177e4SLinus Torvalds 
142*1da177e4SLinus Torvalds 	ps2pp_cmd(psmouse, param, 0x32);
143*1da177e4SLinus Torvalds 
144*1da177e4SLinus Torvalds 	param[0] = 0;
145*1da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
146*1da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
147*1da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
148*1da177e4SLinus Torvalds 
149*1da177e4SLinus Torvalds 	param[0] = smartscroll;
150*1da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
151*1da177e4SLinus Torvalds }
152*1da177e4SLinus Torvalds 
153*1da177e4SLinus Torvalds static ssize_t psmouse_attr_show_smartscroll(struct psmouse *psmouse, char *buf)
154*1da177e4SLinus Torvalds {
155*1da177e4SLinus Torvalds 	return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0);
156*1da177e4SLinus Torvalds }
157*1da177e4SLinus Torvalds 
158*1da177e4SLinus Torvalds static ssize_t psmouse_attr_set_smartscroll(struct psmouse *psmouse, const char *buf, size_t count)
159*1da177e4SLinus Torvalds {
160*1da177e4SLinus Torvalds 	unsigned long value;
161*1da177e4SLinus Torvalds 	char *rest;
162*1da177e4SLinus Torvalds 
163*1da177e4SLinus Torvalds 	value = simple_strtoul(buf, &rest, 10);
164*1da177e4SLinus Torvalds 	if (*rest || value > 1)
165*1da177e4SLinus Torvalds 		return -EINVAL;
166*1da177e4SLinus Torvalds 
167*1da177e4SLinus Torvalds 	ps2pp_set_smartscroll(psmouse, value);
168*1da177e4SLinus Torvalds 	psmouse->smartscroll = value;
169*1da177e4SLinus Torvalds 	return count;
170*1da177e4SLinus Torvalds }
171*1da177e4SLinus Torvalds 
172*1da177e4SLinus Torvalds PSMOUSE_DEFINE_ATTR(smartscroll);
173*1da177e4SLinus Torvalds 
174*1da177e4SLinus Torvalds /*
175*1da177e4SLinus Torvalds  * Support 800 dpi resolution _only_ if the user wants it (there are good
176*1da177e4SLinus Torvalds  * reasons to not use it even if the mouse supports it, and of course there are
177*1da177e4SLinus Torvalds  * also good reasons to use it, let the user decide).
178*1da177e4SLinus Torvalds  */
179*1da177e4SLinus Torvalds 
180*1da177e4SLinus Torvalds static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution)
181*1da177e4SLinus Torvalds {
182*1da177e4SLinus Torvalds 	if (resolution > 400) {
183*1da177e4SLinus Torvalds 		struct ps2dev *ps2dev = &psmouse->ps2dev;
184*1da177e4SLinus Torvalds 		unsigned char param = 3;
185*1da177e4SLinus Torvalds 
186*1da177e4SLinus Torvalds 		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
187*1da177e4SLinus Torvalds 		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
188*1da177e4SLinus Torvalds 		ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11);
189*1da177e4SLinus Torvalds 		ps2_command(ps2dev, &param, PSMOUSE_CMD_SETRES);
190*1da177e4SLinus Torvalds 		psmouse->resolution = 800;
191*1da177e4SLinus Torvalds 	} else
192*1da177e4SLinus Torvalds 		psmouse_set_resolution(psmouse, resolution);
193*1da177e4SLinus Torvalds }
194*1da177e4SLinus Torvalds 
195*1da177e4SLinus Torvalds static void ps2pp_disconnect(struct psmouse *psmouse)
196*1da177e4SLinus Torvalds {
197*1da177e4SLinus Torvalds 	device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll);
198*1da177e4SLinus Torvalds }
199*1da177e4SLinus Torvalds 
200*1da177e4SLinus Torvalds static struct ps2pp_info *get_model_info(unsigned char model)
201*1da177e4SLinus Torvalds {
202*1da177e4SLinus Torvalds 	static struct ps2pp_info ps2pp_list[] = {
203*1da177e4SLinus Torvalds 		{ 12,	0,			PS2PP_SIDE_BTN},
204*1da177e4SLinus Torvalds 		{ 13,	0,			0 },
205*1da177e4SLinus Torvalds 		{ 15,	PS2PP_KIND_MX,					/* MX1000 */
206*1da177e4SLinus Torvalds 				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
207*1da177e4SLinus Torvalds 				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL },
208*1da177e4SLinus Torvalds 		{ 40,	0,			PS2PP_SIDE_BTN },
209*1da177e4SLinus Torvalds 		{ 41,	0,			PS2PP_SIDE_BTN },
210*1da177e4SLinus Torvalds 		{ 42,	0,			PS2PP_SIDE_BTN },
211*1da177e4SLinus Torvalds 		{ 43,	0,			PS2PP_SIDE_BTN },
212*1da177e4SLinus Torvalds 		{ 50,	0,			0 },
213*1da177e4SLinus Torvalds 		{ 51,	0,			0 },
214*1da177e4SLinus Torvalds 		{ 52,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL },
215*1da177e4SLinus Torvalds 		{ 53,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
216*1da177e4SLinus Torvalds 		{ 61,	PS2PP_KIND_MX,					/* MX700 */
217*1da177e4SLinus Torvalds 				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
218*1da177e4SLinus Torvalds 				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
219*1da177e4SLinus Torvalds 		{ 73,	0,			PS2PP_SIDE_BTN },
220*1da177e4SLinus Torvalds 		{ 75,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
221*1da177e4SLinus Torvalds 		{ 76,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
222*1da177e4SLinus Torvalds 		{ 80,	PS2PP_KIND_WHEEL,	PS2PP_SIDE_BTN | PS2PP_WHEEL },
223*1da177e4SLinus Torvalds 		{ 81,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
224*1da177e4SLinus Torvalds 		{ 83,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
225*1da177e4SLinus Torvalds 		{ 88,	PS2PP_KIND_WHEEL,	PS2PP_WHEEL },
226*1da177e4SLinus Torvalds 		{ 96,	0,			0 },
227*1da177e4SLinus Torvalds 		{ 97,	PS2PP_KIND_TP3,		PS2PP_WHEEL | PS2PP_HWHEEL },
228*1da177e4SLinus Torvalds 		{ 100,	PS2PP_KIND_MX,					/* MX510 */
229*1da177e4SLinus Torvalds 				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
230*1da177e4SLinus Torvalds 				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
231*1da177e4SLinus Torvalds 		{ 111,  PS2PP_KIND_MX,					/* MX300 */
232*1da177e4SLinus Torvalds 				PS2PP_WHEEL | PS2PP_EXTRA_BTN | PS2PP_TASK_BTN },
233*1da177e4SLinus Torvalds 		{ 112,	PS2PP_KIND_MX,					/* MX500 */
234*1da177e4SLinus Torvalds 				PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
235*1da177e4SLinus Torvalds 				PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
236*1da177e4SLinus Torvalds 		{ 114,	PS2PP_KIND_MX,					/* MX310 */
237*1da177e4SLinus Torvalds 				PS2PP_WHEEL | PS2PP_SIDE_BTN |
238*1da177e4SLinus Torvalds 				PS2PP_TASK_BTN | PS2PP_EXTRA_BTN },
239*1da177e4SLinus Torvalds 		{ }
240*1da177e4SLinus Torvalds 	};
241*1da177e4SLinus Torvalds 	int i;
242*1da177e4SLinus Torvalds 
243*1da177e4SLinus Torvalds 	for (i = 0; ps2pp_list[i].model; i++)
244*1da177e4SLinus Torvalds 		if (model == ps2pp_list[i].model)
245*1da177e4SLinus Torvalds 			return &ps2pp_list[i];
246*1da177e4SLinus Torvalds 
247*1da177e4SLinus Torvalds 	printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model);
248*1da177e4SLinus Torvalds 	return NULL;
249*1da177e4SLinus Torvalds }
250*1da177e4SLinus Torvalds 
251*1da177e4SLinus Torvalds /*
252*1da177e4SLinus Torvalds  * Set up input device's properties based on the detected mouse model.
253*1da177e4SLinus Torvalds  */
254*1da177e4SLinus Torvalds 
255*1da177e4SLinus Torvalds static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info,
256*1da177e4SLinus Torvalds 				       int using_ps2pp)
257*1da177e4SLinus Torvalds {
258*1da177e4SLinus Torvalds 	if (model_info->features & PS2PP_SIDE_BTN)
259*1da177e4SLinus Torvalds 		set_bit(BTN_SIDE, psmouse->dev.keybit);
260*1da177e4SLinus Torvalds 
261*1da177e4SLinus Torvalds 	if (model_info->features & PS2PP_EXTRA_BTN)
262*1da177e4SLinus Torvalds 		set_bit(BTN_EXTRA, psmouse->dev.keybit);
263*1da177e4SLinus Torvalds 
264*1da177e4SLinus Torvalds 	if (model_info->features & PS2PP_TASK_BTN)
265*1da177e4SLinus Torvalds 		set_bit(BTN_TASK, psmouse->dev.keybit);
266*1da177e4SLinus Torvalds 
267*1da177e4SLinus Torvalds 	if (model_info->features & PS2PP_NAV_BTN) {
268*1da177e4SLinus Torvalds 		set_bit(BTN_FORWARD, psmouse->dev.keybit);
269*1da177e4SLinus Torvalds 		set_bit(BTN_BACK, psmouse->dev.keybit);
270*1da177e4SLinus Torvalds 	}
271*1da177e4SLinus Torvalds 
272*1da177e4SLinus Torvalds 	if (model_info->features & PS2PP_WHEEL)
273*1da177e4SLinus Torvalds 		set_bit(REL_WHEEL, psmouse->dev.relbit);
274*1da177e4SLinus Torvalds 
275*1da177e4SLinus Torvalds 	if (model_info->features & PS2PP_HWHEEL)
276*1da177e4SLinus Torvalds 		set_bit(REL_HWHEEL, psmouse->dev.relbit);
277*1da177e4SLinus Torvalds 
278*1da177e4SLinus Torvalds 	switch (model_info->kind) {
279*1da177e4SLinus Torvalds 		case PS2PP_KIND_WHEEL:
280*1da177e4SLinus Torvalds 			psmouse->name = "Wheel Mouse";
281*1da177e4SLinus Torvalds 			break;
282*1da177e4SLinus Torvalds 
283*1da177e4SLinus Torvalds 		case PS2PP_KIND_MX:
284*1da177e4SLinus Torvalds 			psmouse->name = "MX Mouse";
285*1da177e4SLinus Torvalds 			break;
286*1da177e4SLinus Torvalds 
287*1da177e4SLinus Torvalds 		case PS2PP_KIND_TP3:
288*1da177e4SLinus Torvalds 			psmouse->name = "TouchPad 3";
289*1da177e4SLinus Torvalds 			break;
290*1da177e4SLinus Torvalds 
291*1da177e4SLinus Torvalds 		default:
292*1da177e4SLinus Torvalds 			/*
293*1da177e4SLinus Torvalds 			 * Set name to "Mouse" only when using PS2++,
294*1da177e4SLinus Torvalds 			 * otherwise let other protocols define suitable
295*1da177e4SLinus Torvalds 			 * name
296*1da177e4SLinus Torvalds 			 */
297*1da177e4SLinus Torvalds 			if (using_ps2pp)
298*1da177e4SLinus Torvalds 				psmouse->name = "Mouse";
299*1da177e4SLinus Torvalds 			break;
300*1da177e4SLinus Torvalds 	}
301*1da177e4SLinus Torvalds }
302*1da177e4SLinus Torvalds 
303*1da177e4SLinus Torvalds 
304*1da177e4SLinus Torvalds /*
305*1da177e4SLinus Torvalds  * Logitech magic init. Detect whether the mouse is a Logitech one
306*1da177e4SLinus Torvalds  * and its exact model and try turning on extended protocol for ones
307*1da177e4SLinus Torvalds  * that support it.
308*1da177e4SLinus Torvalds  */
309*1da177e4SLinus Torvalds 
310*1da177e4SLinus Torvalds int ps2pp_init(struct psmouse *psmouse, int set_properties)
311*1da177e4SLinus Torvalds {
312*1da177e4SLinus Torvalds 	struct ps2dev *ps2dev = &psmouse->ps2dev;
313*1da177e4SLinus Torvalds 	unsigned char param[4];
314*1da177e4SLinus Torvalds 	unsigned char model, buttons;
315*1da177e4SLinus Torvalds 	struct ps2pp_info *model_info;
316*1da177e4SLinus Torvalds 	int use_ps2pp = 0;
317*1da177e4SLinus Torvalds 
318*1da177e4SLinus Torvalds 	param[0] = 0;
319*1da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
320*1da177e4SLinus Torvalds 	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
321*1da177e4SLinus Torvalds 	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
322*1da177e4SLinus Torvalds 	ps2_command(ps2dev,  NULL, PSMOUSE_CMD_SETSCALE11);
323*1da177e4SLinus Torvalds 	param[1] = 0;
324*1da177e4SLinus Torvalds 	ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO);
325*1da177e4SLinus Torvalds 
326*1da177e4SLinus Torvalds 	if (!param[1])
327*1da177e4SLinus Torvalds 		return -1;
328*1da177e4SLinus Torvalds 
329*1da177e4SLinus Torvalds 	model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78);
330*1da177e4SLinus Torvalds 	buttons = param[1];
331*1da177e4SLinus Torvalds 
332*1da177e4SLinus Torvalds 	if ((model_info = get_model_info(model)) != NULL) {
333*1da177e4SLinus Torvalds 
334*1da177e4SLinus Torvalds /*
335*1da177e4SLinus Torvalds  * Do Logitech PS2++ / PS2T++ magic init.
336*1da177e4SLinus Torvalds  */
337*1da177e4SLinus Torvalds 		if (model == 97) { /* Touch Pad 3 */
338*1da177e4SLinus Torvalds 
339*1da177e4SLinus Torvalds 			/* Unprotect RAM */
340*1da177e4SLinus Torvalds 			param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
341*1da177e4SLinus Torvalds 			ps2_command(ps2dev, param, 0x30d1);
342*1da177e4SLinus Torvalds 			/* Enable features */
343*1da177e4SLinus Torvalds 			param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b;
344*1da177e4SLinus Torvalds 			ps2_command(ps2dev, param, 0x30d1);
345*1da177e4SLinus Torvalds 			/* Enable PS2++ */
346*1da177e4SLinus Torvalds 			param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3;
347*1da177e4SLinus Torvalds 			ps2_command(ps2dev, param, 0x30d1);
348*1da177e4SLinus Torvalds 
349*1da177e4SLinus Torvalds 			param[0] = 0;
350*1da177e4SLinus Torvalds 			if (!ps2_command(ps2dev, param, 0x13d1) &&
351*1da177e4SLinus Torvalds 			    param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
352*1da177e4SLinus Torvalds 				use_ps2pp = 1;
353*1da177e4SLinus Torvalds 			}
354*1da177e4SLinus Torvalds 
355*1da177e4SLinus Torvalds 		} else {
356*1da177e4SLinus Torvalds 
357*1da177e4SLinus Torvalds 			param[0] = param[1] = param[2] = 0;
358*1da177e4SLinus Torvalds 			ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */
359*1da177e4SLinus Torvalds 			ps2pp_cmd(psmouse, param, 0xDB);
360*1da177e4SLinus Torvalds 
361*1da177e4SLinus Torvalds 			if ((param[0] & 0x78) == 0x48 &&
362*1da177e4SLinus Torvalds 			    (param[1] & 0xf3) == 0xc2 &&
363*1da177e4SLinus Torvalds 			    (param[2] & 0x03) == ((param[1] >> 2) & 3)) {
364*1da177e4SLinus Torvalds 				ps2pp_set_smartscroll(psmouse, psmouse->smartscroll);
365*1da177e4SLinus Torvalds 				use_ps2pp = 1;
366*1da177e4SLinus Torvalds 			}
367*1da177e4SLinus Torvalds 		}
368*1da177e4SLinus Torvalds 	}
369*1da177e4SLinus Torvalds 
370*1da177e4SLinus Torvalds 	if (set_properties) {
371*1da177e4SLinus Torvalds 		psmouse->vendor = "Logitech";
372*1da177e4SLinus Torvalds 		psmouse->model = model;
373*1da177e4SLinus Torvalds 
374*1da177e4SLinus Torvalds 		if (use_ps2pp) {
375*1da177e4SLinus Torvalds 			psmouse->protocol_handler = ps2pp_process_byte;
376*1da177e4SLinus Torvalds 			psmouse->pktsize = 3;
377*1da177e4SLinus Torvalds 
378*1da177e4SLinus Torvalds 			if (model_info->kind != PS2PP_KIND_TP3) {
379*1da177e4SLinus Torvalds 				psmouse->set_resolution = ps2pp_set_resolution;
380*1da177e4SLinus Torvalds 				psmouse->disconnect = ps2pp_disconnect;
381*1da177e4SLinus Torvalds 
382*1da177e4SLinus Torvalds 				device_create_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll);
383*1da177e4SLinus Torvalds 			}
384*1da177e4SLinus Torvalds 		}
385*1da177e4SLinus Torvalds 
386*1da177e4SLinus Torvalds 		if (buttons < 3)
387*1da177e4SLinus Torvalds 			clear_bit(BTN_MIDDLE, psmouse->dev.keybit);
388*1da177e4SLinus Torvalds 		if (buttons < 2)
389*1da177e4SLinus Torvalds 			clear_bit(BTN_RIGHT, psmouse->dev.keybit);
390*1da177e4SLinus Torvalds 
391*1da177e4SLinus Torvalds 		if (model_info)
392*1da177e4SLinus Torvalds 			ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
393*1da177e4SLinus Torvalds 	}
394*1da177e4SLinus Torvalds 
395*1da177e4SLinus Torvalds 	return use_ps2pp ? 0 : -1;
396*1da177e4SLinus Torvalds }
397*1da177e4SLinus Torvalds 
398