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