Branch data Line data Source code
1 : : /*
2 : : * Stephen Evanchik <evanchsa@gmail.com>
3 : : *
4 : : * This program is free software; you can redistribute it and/or modify it
5 : : * under the terms of the GNU General Public License version 2 as published by
6 : : * the Free Software Foundation.
7 : : *
8 : : * Trademarks are the property of their respective owners.
9 : : */
10 : :
11 : : #include <linux/slab.h>
12 : : #include <linux/delay.h>
13 : : #include <linux/serio.h>
14 : : #include <linux/module.h>
15 : : #include <linux/input.h>
16 : : #include <linux/libps2.h>
17 : : #include <linux/proc_fs.h>
18 : : #include <asm/uaccess.h>
19 : : #include "psmouse.h"
20 : : #include "trackpoint.h"
21 : :
22 : : /*
23 : : * Power-on Reset: Resets all trackpoint parameters, including RAM values,
24 : : * to defaults.
25 : : * Returns zero on success, non-zero on failure.
26 : : */
27 : 0 : static int trackpoint_power_on_reset(struct ps2dev *ps2dev)
28 : : {
29 : : unsigned char results[2];
30 : : int tries = 0;
31 : :
32 : : /* Issue POR command, and repeat up to once if 0xFC00 received */
33 : : do {
34 [ # # # # ]: 0 : if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
35 : 0 : ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 2, TP_POR)))
36 : : return -1;
37 [ # # ][ # # ]: 0 : } while (results[0] == 0xFC && results[1] == 0x00 && ++tries < 2);
[ # # ]
38 : :
39 : : /* Check for success response -- 0xAA00 */
40 [ # # ][ # # ]: 0 : if (results[0] != 0xAA || results[1] != 0x00)
41 : : return -1;
42 : :
43 : 0 : return 0;
44 : : }
45 : :
46 : : /*
47 : : * Device IO: read, write and toggle bit
48 : : */
49 : 0 : static int trackpoint_read(struct ps2dev *ps2dev,
50 : : unsigned char loc, unsigned char *results)
51 : : {
52 [ # # # # ]: 0 : if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
53 : 0 : ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) {
54 : : return -1;
55 : : }
56 : :
57 : : return 0;
58 : : }
59 : :
60 : 0 : static int trackpoint_write(struct ps2dev *ps2dev,
61 : : unsigned char loc, unsigned char val)
62 : : {
63 [ # # # # ]: 0 : if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
64 [ # # ]: 0 : ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM)) ||
65 [ # # ]: 0 : ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
66 : 0 : ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, val))) {
67 : : return -1;
68 : : }
69 : :
70 : : return 0;
71 : : }
72 : :
73 : 0 : static int trackpoint_toggle_bit(struct ps2dev *ps2dev,
74 : : unsigned char loc, unsigned char mask)
75 : : {
76 : : /* Bad things will happen if the loc param isn't in this range */
77 [ # # ]: 0 : if (loc < 0x20 || loc >= 0x2F)
78 : : return -1;
79 : :
80 [ # # # # ]: 0 : if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) ||
81 [ # # ]: 0 : ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_TOGGLE)) ||
82 [ # # ]: 0 : ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, loc)) ||
83 : 0 : ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, mask))) {
84 : : return -1;
85 : : }
86 : :
87 : : return 0;
88 : : }
89 : :
90 : 0 : static int trackpoint_update_bit(struct ps2dev *ps2dev, unsigned char loc,
91 : : unsigned char mask, unsigned char value)
92 : : {
93 : : int retval = 0;
94 : : unsigned char data;
95 : :
96 : 0 : trackpoint_read(ps2dev, loc, &data);
97 [ # # ]: 0 : if (((data & mask) == mask) != !!value)
98 : 0 : retval = trackpoint_toggle_bit(ps2dev, loc, mask);
99 : :
100 : 0 : return retval;
101 : : }
102 : :
103 : : /*
104 : : * Trackpoint-specific attributes
105 : : */
106 : : struct trackpoint_attr_data {
107 : : size_t field_offset;
108 : : unsigned char command;
109 : : unsigned char mask;
110 : : unsigned char inverted;
111 : : unsigned char power_on_default;
112 : : };
113 : :
114 : 0 : static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf)
115 : : {
116 : 0 : struct trackpoint_data *tp = psmouse->private;
117 : : struct trackpoint_attr_data *attr = data;
118 : 0 : unsigned char value = *(unsigned char *)((char *)tp + attr->field_offset);
119 : :
120 [ # # ]: 0 : if (attr->inverted)
121 : 0 : value = !value;
122 : :
123 : 0 : return sprintf(buf, "%u\n", value);
124 : : }
125 : :
126 : 0 : static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data,
127 : : const char *buf, size_t count)
128 : : {
129 : 0 : struct trackpoint_data *tp = psmouse->private;
130 : : struct trackpoint_attr_data *attr = data;
131 : 0 : unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
132 : : unsigned char value;
133 : : int err;
134 : :
135 : 0 : err = kstrtou8(buf, 10, &value);
136 [ # # ]: 0 : if (err)
137 : : return err;
138 : :
139 : 0 : *field = value;
140 : 0 : trackpoint_write(&psmouse->ps2dev, attr->command, value);
141 : :
142 : 0 : return count;
143 : : }
144 : :
145 : : #define TRACKPOINT_INT_ATTR(_name, _command, _default) \
146 : : static struct trackpoint_attr_data trackpoint_attr_##_name = { \
147 : : .field_offset = offsetof(struct trackpoint_data, _name), \
148 : : .command = _command, \
149 : : .power_on_default = _default, \
150 : : }; \
151 : : PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
152 : : &trackpoint_attr_##_name, \
153 : : trackpoint_show_int_attr, trackpoint_set_int_attr)
154 : :
155 : 0 : static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data,
156 : : const char *buf, size_t count)
157 : : {
158 : 0 : struct trackpoint_data *tp = psmouse->private;
159 : : struct trackpoint_attr_data *attr = data;
160 : 0 : unsigned char *field = (unsigned char *)((char *)tp + attr->field_offset);
161 : : unsigned int value;
162 : : int err;
163 : :
164 : 0 : err = kstrtouint(buf, 10, &value);
165 [ # # ]: 0 : if (err)
166 : : return err;
167 : :
168 [ # # ]: 0 : if (value > 1)
169 : : return -EINVAL;
170 : :
171 [ # # ]: 0 : if (attr->inverted)
172 : 0 : value = !value;
173 : :
174 [ # # ]: 0 : if (*field != value) {
175 : 0 : *field = value;
176 : 0 : trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask);
177 : : }
178 : :
179 : 0 : return count;
180 : : }
181 : :
182 : :
183 : : #define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv, _default) \
184 : : static struct trackpoint_attr_data trackpoint_attr_##_name = { \
185 : : .field_offset = offsetof(struct trackpoint_data, \
186 : : _name), \
187 : : .command = _command, \
188 : : .mask = _mask, \
189 : : .inverted = _inv, \
190 : : .power_on_default = _default, \
191 : : }; \
192 : : PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
193 : : &trackpoint_attr_##_name, \
194 : : trackpoint_show_int_attr, trackpoint_set_bit_attr)
195 : :
196 : : #define TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name) \
197 : : do { \
198 : : struct trackpoint_attr_data *_attr = &trackpoint_attr_##_name; \
199 : : \
200 : : trackpoint_update_bit(&_psmouse->ps2dev, \
201 : : _attr->command, _attr->mask, _tp->_name); \
202 : : } while (0)
203 : :
204 : : #define TRACKPOINT_UPDATE(_power_on, _psmouse, _tp, _name) \
205 : : do { \
206 : : if (!_power_on || \
207 : : _tp->_name != trackpoint_attr_##_name.power_on_default) { \
208 : : if (!trackpoint_attr_##_name.mask) \
209 : : trackpoint_write(&_psmouse->ps2dev, \
210 : : trackpoint_attr_##_name.command, \
211 : : _tp->_name); \
212 : : else \
213 : : TRACKPOINT_UPDATE_BIT(_psmouse, _tp, _name); \
214 : : } \
215 : : } while (0)
216 : :
217 : : #define TRACKPOINT_SET_POWER_ON_DEFAULT(_tp, _name) \
218 : : (_tp->_name = trackpoint_attr_##_name.power_on_default)
219 : :
220 : : TRACKPOINT_INT_ATTR(sensitivity, TP_SENS, TP_DEF_SENS);
221 : : TRACKPOINT_INT_ATTR(speed, TP_SPEED, TP_DEF_SPEED);
222 : : TRACKPOINT_INT_ATTR(inertia, TP_INERTIA, TP_DEF_INERTIA);
223 : : TRACKPOINT_INT_ATTR(reach, TP_REACH, TP_DEF_REACH);
224 : : TRACKPOINT_INT_ATTR(draghys, TP_DRAGHYS, TP_DEF_DRAGHYS);
225 : : TRACKPOINT_INT_ATTR(mindrag, TP_MINDRAG, TP_DEF_MINDRAG);
226 : : TRACKPOINT_INT_ATTR(thresh, TP_THRESH, TP_DEF_THRESH);
227 : : TRACKPOINT_INT_ATTR(upthresh, TP_UP_THRESH, TP_DEF_UP_THRESH);
228 : : TRACKPOINT_INT_ATTR(ztime, TP_Z_TIME, TP_DEF_Z_TIME);
229 : : TRACKPOINT_INT_ATTR(jenks, TP_JENKS_CURV, TP_DEF_JENKS_CURV);
230 : :
231 : : TRACKPOINT_BIT_ATTR(press_to_select, TP_TOGGLE_PTSON, TP_MASK_PTSON, 0,
232 : : TP_DEF_PTSON);
233 : : TRACKPOINT_BIT_ATTR(skipback, TP_TOGGLE_SKIPBACK, TP_MASK_SKIPBACK, 0,
234 : : TP_DEF_SKIPBACK);
235 : : TRACKPOINT_BIT_ATTR(ext_dev, TP_TOGGLE_EXT_DEV, TP_MASK_EXT_DEV, 1,
236 : : TP_DEF_EXT_DEV);
237 : :
238 : : static struct attribute *trackpoint_attrs[] = {
239 : : &psmouse_attr_sensitivity.dattr.attr,
240 : : &psmouse_attr_speed.dattr.attr,
241 : : &psmouse_attr_inertia.dattr.attr,
242 : : &psmouse_attr_reach.dattr.attr,
243 : : &psmouse_attr_draghys.dattr.attr,
244 : : &psmouse_attr_mindrag.dattr.attr,
245 : : &psmouse_attr_thresh.dattr.attr,
246 : : &psmouse_attr_upthresh.dattr.attr,
247 : : &psmouse_attr_ztime.dattr.attr,
248 : : &psmouse_attr_jenks.dattr.attr,
249 : : &psmouse_attr_press_to_select.dattr.attr,
250 : : &psmouse_attr_skipback.dattr.attr,
251 : : &psmouse_attr_ext_dev.dattr.attr,
252 : : NULL
253 : : };
254 : :
255 : : static struct attribute_group trackpoint_attr_group = {
256 : : .attrs = trackpoint_attrs,
257 : : };
258 : :
259 : 0 : static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id)
260 : : {
261 : 0 : unsigned char param[2] = { 0 };
262 : :
263 [ # # ]: 0 : if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
264 : : return -1;
265 : :
266 [ # # ]: 0 : if (param[0] != TP_MAGIC_IDENT)
267 : : return -1;
268 : :
269 [ # # ]: 0 : if (firmware_id)
270 : 0 : *firmware_id = param[1];
271 : :
272 : : return 0;
273 : : }
274 : :
275 : : /*
276 : : * Write parameters to trackpad.
277 : : * in_power_on_state: Set to true if TP is in default / power-on state (ex. if
278 : : * power-on reset was run). If so, values will only be
279 : : * written to TP if they differ from power-on default.
280 : : */
281 : 0 : static int trackpoint_sync(struct psmouse *psmouse, bool in_power_on_state)
282 : : {
283 : 0 : struct trackpoint_data *tp = psmouse->private;
284 : :
285 [ # # ]: 0 : if (!in_power_on_state) {
286 : : /*
287 : : * Disable features that may make device unusable
288 : : * with this driver.
289 : : */
290 : 0 : trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_TWOHAND,
291 : : TP_MASK_TWOHAND, TP_DEF_TWOHAND);
292 : :
293 : 0 : trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_SOURCE_TAG,
294 : : TP_MASK_SOURCE_TAG, TP_DEF_SOURCE_TAG);
295 : :
296 : 0 : trackpoint_update_bit(&psmouse->ps2dev, TP_TOGGLE_MB,
297 : : TP_MASK_MB, TP_DEF_MB);
298 : : }
299 : :
300 : : /*
301 : : * These properties can be changed in this driver. Only
302 : : * configure them if the values are non-default or if the TP is in
303 : : * an unknown state.
304 : : */
305 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, sensitivity);
[ # # ]
306 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, inertia);
[ # # ]
307 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, speed);
[ # # ]
308 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, reach);
[ # # ]
309 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, draghys);
[ # # ]
310 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, mindrag);
[ # # ]
311 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, thresh);
[ # # ]
312 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, upthresh);
[ # # ]
313 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ztime);
[ # # ]
314 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, jenks);
[ # # ]
315 : :
316 : : /* toggles */
317 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, press_to_select);
[ # # ]
318 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, skipback);
[ # # ]
319 [ # # ][ # # ]: 0 : TRACKPOINT_UPDATE(in_power_on_state, psmouse, tp, ext_dev);
[ # # ]
320 : :
321 : 0 : return 0;
322 : : }
323 : :
324 : 0 : static void trackpoint_defaults(struct trackpoint_data *tp)
325 : : {
326 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, sensitivity);
327 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, speed);
328 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, reach);
329 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, draghys);
330 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, mindrag);
331 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, thresh);
332 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, upthresh);
333 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ztime);
334 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, jenks);
335 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, inertia);
336 : :
337 : : /* toggles */
338 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, press_to_select);
339 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, skipback);
340 : 0 : TRACKPOINT_SET_POWER_ON_DEFAULT(tp, ext_dev);
341 : 0 : }
342 : :
343 : 0 : static void trackpoint_disconnect(struct psmouse *psmouse)
344 : : {
345 : 0 : sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group);
346 : :
347 : 0 : kfree(psmouse->private);
348 : 0 : psmouse->private = NULL;
349 : 0 : }
350 : :
351 : 0 : static int trackpoint_reconnect(struct psmouse *psmouse)
352 : : {
353 : : int reset_fail;
354 : :
355 [ # # ]: 0 : if (trackpoint_start_protocol(psmouse, NULL))
356 : : return -1;
357 : :
358 : 0 : reset_fail = trackpoint_power_on_reset(&psmouse->ps2dev);
359 [ # # ]: 0 : if (trackpoint_sync(psmouse, !reset_fail))
360 : : return -1;
361 : :
362 : 0 : return 0;
363 : : }
364 : :
365 : 0 : int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
366 : : {
367 : : struct ps2dev *ps2dev = &psmouse->ps2dev;
368 : : unsigned char firmware_id;
369 : : unsigned char button_info;
370 : : int error;
371 : :
372 [ # # ]: 0 : if (trackpoint_start_protocol(psmouse, &firmware_id))
373 : : return -1;
374 : :
375 [ # # ]: 0 : if (!set_properties)
376 : : return 0;
377 : :
378 [ # # ]: 0 : if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
379 : 0 : psmouse_warn(psmouse, "failed to get extended button data\n");
380 : 0 : button_info = 0;
381 : : }
382 : :
383 : 0 : psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
384 [ # # ]: 0 : if (!psmouse->private)
385 : : return -ENOMEM;
386 : :
387 : 0 : psmouse->vendor = "IBM";
388 : 0 : psmouse->name = "TrackPoint";
389 : :
390 : 0 : psmouse->reconnect = trackpoint_reconnect;
391 : 0 : psmouse->disconnect = trackpoint_disconnect;
392 : :
393 [ # # ]: 0 : if ((button_info & 0x0f) >= 3)
394 : 0 : __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
395 : :
396 : 0 : trackpoint_defaults(psmouse->private);
397 : :
398 : 0 : error = trackpoint_power_on_reset(&psmouse->ps2dev);
399 : :
400 : : /* Write defaults to TP only if reset fails. */
401 [ # # ]: 0 : if (error)
402 : 0 : trackpoint_sync(psmouse, false);
403 : :
404 : 0 : error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group);
405 [ # # ]: 0 : if (error) {
406 : 0 : psmouse_err(psmouse,
407 : : "failed to create sysfs attributes, error: %d\n",
408 : : error);
409 : 0 : kfree(psmouse->private);
410 : 0 : psmouse->private = NULL;
411 : 0 : return -1;
412 : : }
413 : :
414 : 0 : psmouse_info(psmouse,
415 : : "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
416 : : firmware_id,
417 : : (button_info & 0xf0) >> 4, button_info & 0x0f);
418 : :
419 : 0 : return 0;
420 : : }
421 : :
|