2 * Stephen Evanchik <evanchsa@gmail.com>
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.
8 * Trademarks are the property of their respective owners.
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>
20 #include "trackpoint.h"
23 * Device IO: read, write and toggle bit
25 static int trackpoint_read(struct ps2dev
*ps2dev
, unsigned char loc
, unsigned char *results
)
27 if (ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, TP_COMMAND
)) ||
28 ps2_command(ps2dev
, results
, MAKE_PS2_CMD(0, 1, loc
))) {
35 static int trackpoint_write(struct ps2dev
*ps2dev
, unsigned char loc
, unsigned char val
)
37 if (ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, TP_COMMAND
)) ||
38 ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, TP_WRITE_MEM
)) ||
39 ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, loc
)) ||
40 ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, val
))) {
47 static int trackpoint_toggle_bit(struct ps2dev
*ps2dev
, unsigned char loc
, unsigned char mask
)
49 /* Bad things will happen if the loc param isn't in this range */
50 if (loc
< 0x20 || loc
>= 0x2F)
53 if (ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, TP_COMMAND
)) ||
54 ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, TP_TOGGLE
)) ||
55 ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, loc
)) ||
56 ps2_command(ps2dev
, NULL
, MAKE_PS2_CMD(0, 0, mask
))) {
65 * Trackpoint-specific attributes
67 struct trackpoint_attr_data
{
69 unsigned char command
;
71 unsigned char inverted
;
74 static ssize_t
trackpoint_show_int_attr(struct psmouse
*psmouse
, void *data
, char *buf
)
76 struct trackpoint_data
*tp
= psmouse
->private;
77 struct trackpoint_attr_data
*attr
= data
;
78 unsigned char value
= *(unsigned char *)((char *)tp
+ attr
->field_offset
);
83 return sprintf(buf
, "%u\n", value
);
86 static ssize_t
trackpoint_set_int_attr(struct psmouse
*psmouse
, void *data
,
87 const char *buf
, size_t count
)
89 struct trackpoint_data
*tp
= psmouse
->private;
90 struct trackpoint_attr_data
*attr
= data
;
91 unsigned char *field
= (unsigned char *)((char *)tp
+ attr
->field_offset
);
94 if (strict_strtoul(buf
, 10, &value
) || value
> 255)
98 trackpoint_write(&psmouse
->ps2dev
, attr
->command
, value
);
103 #define TRACKPOINT_INT_ATTR(_name, _command) \
104 static struct trackpoint_attr_data trackpoint_attr_##_name = { \
105 .field_offset = offsetof(struct trackpoint_data, _name), \
106 .command = _command, \
108 PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
109 &trackpoint_attr_##_name, \
110 trackpoint_show_int_attr, trackpoint_set_int_attr)
112 static ssize_t
trackpoint_set_bit_attr(struct psmouse
*psmouse
, void *data
,
113 const char *buf
, size_t count
)
115 struct trackpoint_data
*tp
= psmouse
->private;
116 struct trackpoint_attr_data
*attr
= data
;
117 unsigned char *field
= (unsigned char *)((char *)tp
+ attr
->field_offset
);
120 if (strict_strtoul(buf
, 10, &value
) || value
> 1)
126 if (*field
!= value
) {
128 trackpoint_toggle_bit(&psmouse
->ps2dev
, attr
->command
, attr
->mask
);
135 #define TRACKPOINT_BIT_ATTR(_name, _command, _mask, _inv) \
136 static struct trackpoint_attr_data trackpoint_attr_##_name = { \
137 .field_offset = offsetof(struct trackpoint_data, _name), \
138 .command = _command, \
142 PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
143 &trackpoint_attr_##_name, \
144 trackpoint_show_int_attr, trackpoint_set_bit_attr)
146 TRACKPOINT_INT_ATTR(sensitivity
, TP_SENS
);
147 TRACKPOINT_INT_ATTR(speed
, TP_SPEED
);
148 TRACKPOINT_INT_ATTR(inertia
, TP_INERTIA
);
149 TRACKPOINT_INT_ATTR(reach
, TP_REACH
);
150 TRACKPOINT_INT_ATTR(draghys
, TP_DRAGHYS
);
151 TRACKPOINT_INT_ATTR(mindrag
, TP_MINDRAG
);
152 TRACKPOINT_INT_ATTR(thresh
, TP_THRESH
);
153 TRACKPOINT_INT_ATTR(upthresh
, TP_UP_THRESH
);
154 TRACKPOINT_INT_ATTR(ztime
, TP_Z_TIME
);
155 TRACKPOINT_INT_ATTR(jenks
, TP_JENKS_CURV
);
157 TRACKPOINT_BIT_ATTR(press_to_select
, TP_TOGGLE_PTSON
, TP_MASK_PTSON
, 0);
158 TRACKPOINT_BIT_ATTR(skipback
, TP_TOGGLE_SKIPBACK
, TP_MASK_SKIPBACK
, 0);
159 TRACKPOINT_BIT_ATTR(ext_dev
, TP_TOGGLE_EXT_DEV
, TP_MASK_EXT_DEV
, 1);
161 static struct attribute
*trackpoint_attrs
[] = {
162 &psmouse_attr_sensitivity
.dattr
.attr
,
163 &psmouse_attr_speed
.dattr
.attr
,
164 &psmouse_attr_inertia
.dattr
.attr
,
165 &psmouse_attr_reach
.dattr
.attr
,
166 &psmouse_attr_draghys
.dattr
.attr
,
167 &psmouse_attr_mindrag
.dattr
.attr
,
168 &psmouse_attr_thresh
.dattr
.attr
,
169 &psmouse_attr_upthresh
.dattr
.attr
,
170 &psmouse_attr_ztime
.dattr
.attr
,
171 &psmouse_attr_jenks
.dattr
.attr
,
172 &psmouse_attr_press_to_select
.dattr
.attr
,
173 &psmouse_attr_skipback
.dattr
.attr
,
174 &psmouse_attr_ext_dev
.dattr
.attr
,
178 static struct attribute_group trackpoint_attr_group
= {
179 .attrs
= trackpoint_attrs
,
182 static int trackpoint_start_protocol(struct psmouse
*psmouse
, unsigned char *firmware_id
)
184 unsigned char param
[2] = { 0 };
186 if (ps2_command(&psmouse
->ps2dev
, param
, MAKE_PS2_CMD(0, 2, TP_READ_ID
)))
189 if (param
[0] != TP_MAGIC_IDENT
)
193 *firmware_id
= param
[1];
198 static int trackpoint_sync(struct psmouse
*psmouse
)
200 struct trackpoint_data
*tp
= psmouse
->private;
201 unsigned char toggle
;
203 /* Disable features that may make device unusable with this driver */
204 trackpoint_read(&psmouse
->ps2dev
, TP_TOGGLE_TWOHAND
, &toggle
);
205 if (toggle
& TP_MASK_TWOHAND
)
206 trackpoint_toggle_bit(&psmouse
->ps2dev
, TP_TOGGLE_TWOHAND
, TP_MASK_TWOHAND
);
208 trackpoint_read(&psmouse
->ps2dev
, TP_TOGGLE_SOURCE_TAG
, &toggle
);
209 if (toggle
& TP_MASK_SOURCE_TAG
)
210 trackpoint_toggle_bit(&psmouse
->ps2dev
, TP_TOGGLE_SOURCE_TAG
, TP_MASK_SOURCE_TAG
);
212 trackpoint_read(&psmouse
->ps2dev
, TP_TOGGLE_MB
, &toggle
);
213 if (toggle
& TP_MASK_MB
)
214 trackpoint_toggle_bit(&psmouse
->ps2dev
, TP_TOGGLE_MB
, TP_MASK_MB
);
216 /* Push the config to the device */
217 trackpoint_write(&psmouse
->ps2dev
, TP_SENS
, tp
->sensitivity
);
218 trackpoint_write(&psmouse
->ps2dev
, TP_INERTIA
, tp
->inertia
);
219 trackpoint_write(&psmouse
->ps2dev
, TP_SPEED
, tp
->speed
);
221 trackpoint_write(&psmouse
->ps2dev
, TP_REACH
, tp
->reach
);
222 trackpoint_write(&psmouse
->ps2dev
, TP_DRAGHYS
, tp
->draghys
);
223 trackpoint_write(&psmouse
->ps2dev
, TP_MINDRAG
, tp
->mindrag
);
225 trackpoint_write(&psmouse
->ps2dev
, TP_THRESH
, tp
->thresh
);
226 trackpoint_write(&psmouse
->ps2dev
, TP_UP_THRESH
, tp
->upthresh
);
228 trackpoint_write(&psmouse
->ps2dev
, TP_Z_TIME
, tp
->ztime
);
229 trackpoint_write(&psmouse
->ps2dev
, TP_JENKS_CURV
, tp
->jenks
);
231 trackpoint_read(&psmouse
->ps2dev
, TP_TOGGLE_PTSON
, &toggle
);
232 if (((toggle
& TP_MASK_PTSON
) == TP_MASK_PTSON
) != tp
->press_to_select
)
233 trackpoint_toggle_bit(&psmouse
->ps2dev
, TP_TOGGLE_PTSON
, TP_MASK_PTSON
);
235 trackpoint_read(&psmouse
->ps2dev
, TP_TOGGLE_SKIPBACK
, &toggle
);
236 if (((toggle
& TP_MASK_SKIPBACK
) == TP_MASK_SKIPBACK
) != tp
->skipback
)
237 trackpoint_toggle_bit(&psmouse
->ps2dev
, TP_TOGGLE_SKIPBACK
, TP_MASK_SKIPBACK
);
239 trackpoint_read(&psmouse
->ps2dev
, TP_TOGGLE_EXT_DEV
, &toggle
);
240 if (((toggle
& TP_MASK_EXT_DEV
) == TP_MASK_EXT_DEV
) != tp
->ext_dev
)
241 trackpoint_toggle_bit(&psmouse
->ps2dev
, TP_TOGGLE_EXT_DEV
, TP_MASK_EXT_DEV
);
246 static void trackpoint_defaults(struct trackpoint_data
*tp
)
248 tp
->press_to_select
= TP_DEF_PTSON
;
249 tp
->sensitivity
= TP_DEF_SENS
;
250 tp
->speed
= TP_DEF_SPEED
;
251 tp
->reach
= TP_DEF_REACH
;
253 tp
->draghys
= TP_DEF_DRAGHYS
;
254 tp
->mindrag
= TP_DEF_MINDRAG
;
256 tp
->thresh
= TP_DEF_THRESH
;
257 tp
->upthresh
= TP_DEF_UP_THRESH
;
259 tp
->ztime
= TP_DEF_Z_TIME
;
260 tp
->jenks
= TP_DEF_JENKS_CURV
;
262 tp
->inertia
= TP_DEF_INERTIA
;
263 tp
->skipback
= TP_DEF_SKIPBACK
;
264 tp
->ext_dev
= TP_DEF_EXT_DEV
;
267 static void trackpoint_disconnect(struct psmouse
*psmouse
)
269 sysfs_remove_group(&psmouse
->ps2dev
.serio
->dev
.kobj
, &trackpoint_attr_group
);
271 kfree(psmouse
->private);
272 psmouse
->private = NULL
;
275 static int trackpoint_reconnect(struct psmouse
*psmouse
)
277 if (trackpoint_start_protocol(psmouse
, NULL
))
280 if (trackpoint_sync(psmouse
))
286 int trackpoint_detect(struct psmouse
*psmouse
, bool set_properties
)
288 struct ps2dev
*ps2dev
= &psmouse
->ps2dev
;
289 unsigned char firmware_id
;
290 unsigned char button_info
;
293 if (trackpoint_start_protocol(psmouse
, &firmware_id
))
299 if (trackpoint_read(&psmouse
->ps2dev
, TP_EXT_BTN
, &button_info
)) {
300 printk(KERN_WARNING
"trackpoint.c: failed to get extended button data\n");
304 psmouse
->private = kzalloc(sizeof(struct trackpoint_data
), GFP_KERNEL
);
305 if (!psmouse
->private)
308 psmouse
->vendor
= "IBM";
309 psmouse
->name
= "TrackPoint";
311 psmouse
->reconnect
= trackpoint_reconnect
;
312 psmouse
->disconnect
= trackpoint_disconnect
;
314 if ((button_info
& 0x0f) >= 3)
315 __set_bit(BTN_MIDDLE
, psmouse
->dev
->keybit
);
317 trackpoint_defaults(psmouse
->private);
318 trackpoint_sync(psmouse
);
320 error
= sysfs_create_group(&ps2dev
->serio
->dev
.kobj
, &trackpoint_attr_group
);
323 "trackpoint.c: failed to create sysfs attributes, error: %d\n",
325 kfree(psmouse
->private);
326 psmouse
->private = NULL
;
330 printk(KERN_INFO
"IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n",
331 firmware_id
, (button_info
& 0xf0) >> 4, button_info
& 0x0f);