diff options
| author | Henrik Rydberg <rydberg@euromail.se> | 2010-11-16 14:04:09 +0100 |
|---|---|---|
| committer | Henrik Rydberg <rydberg@euromail.se> | 2010-11-16 14:04:09 +0100 |
| commit | e56d270d5dd472219864c1735d8eee16d6f1ccdb (patch) | |
| tree | a486aeada19c76357bc9042364fcdb1ef04c0336 /usr/src | |
Add psmouse driver from maverick (Ubuntu-2.6.35-23.36).
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Diffstat (limited to 'usr/src')
23 files changed, 7602 insertions, 0 deletions
diff --git a/usr/src/dkms_main_tree/dkms_dbversion b/usr/src/dkms_main_tree/dkms_dbversion new file mode 100644 index 0000000..227cea2 --- /dev/null +++ b/usr/src/dkms_main_tree/dkms_dbversion | |||
| @@ -0,0 +1 @@ | |||
| 2.0.0 | |||
diff --git a/usr/src/dkms_source_tree/Makefile b/usr/src/dkms_source_tree/Makefile new file mode 100644 index 0000000..8a48755 --- /dev/null +++ b/usr/src/dkms_source_tree/Makefile | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the mouse drivers. | ||
| 3 | # | ||
| 4 | |||
| 5 | # Each configuration option enables a list of files. | ||
| 6 | |||
| 7 | obj-m += psmouse.o | ||
| 8 | |||
| 9 | psmouse-objs := psmouse-base.o synaptics.o | ||
| 10 | |||
| 11 | psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o | ||
| 12 | psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o | ||
| 13 | psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o | ||
| 14 | psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o | ||
| 15 | psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o | ||
| 16 | psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o | ||
| 17 | psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o | ||
| 18 | psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o | ||
diff --git a/usr/src/dkms_source_tree/alps.c b/usr/src/dkms_source_tree/alps.c new file mode 100644 index 0000000..99d5876 --- /dev/null +++ b/usr/src/dkms_source_tree/alps.c | |||
| @@ -0,0 +1,774 @@ | |||
| 1 | /* | ||
| 2 | * ALPS touchpad PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au> | ||
| 5 | * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com> | ||
| 6 | * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru> | ||
| 7 | * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> | ||
| 8 | * Copyright (c) 2009 Sebastian Kapfer <sebastian_kapfer@gmx.net> | ||
| 9 | * | ||
| 10 | * ALPS detection, tap switching and status querying info is taken from | ||
| 11 | * tpconfig utility (by C. Scott Ananian and Bruce Kall). | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify it | ||
| 14 | * under the terms of the GNU General Public License version 2 as published by | ||
| 15 | * the Free Software Foundation. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/input.h> | ||
| 20 | #include <linux/serio.h> | ||
| 21 | #include <linux/libps2.h> | ||
| 22 | |||
| 23 | #include "psmouse.h" | ||
| 24 | #include "alps.h" | ||
| 25 | |||
| 26 | #undef DEBUG | ||
| 27 | #ifdef DEBUG | ||
| 28 | #define dbg(format, arg...) printk(KERN_INFO "alps.c: " format "\n", ## arg) | ||
| 29 | #else | ||
| 30 | #define dbg(format, arg...) do {} while (0) | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #define ALPS_OLDPROTO 0x01 /* old style input */ | ||
| 34 | #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ | ||
| 35 | #define ALPS_PASS 0x04 /* device has a pass-through port */ | ||
| 36 | |||
| 37 | #define ALPS_WHEEL 0x08 /* hardware wheel present */ | ||
| 38 | #define ALPS_FW_BK_1 0x10 /* front & back buttons present */ | ||
| 39 | #define ALPS_FW_BK_2 0x20 /* front & back buttons present */ | ||
| 40 | #define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ | ||
| 41 | #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with | ||
| 42 | 6-byte ALPS packet */ | ||
| 43 | |||
| 44 | static const struct alps_model_info alps_model_data[] = { | ||
| 45 | { { 0x32, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ | ||
| 46 | { { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */ | ||
| 47 | { { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | ||
| 48 | { { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | ||
| 49 | { { 0x60, 0x03, 0xc8 }, 0xf8, 0xf8, 0 }, /* HP ze1115 */ | ||
| 50 | { { 0x63, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | ||
| 51 | { { 0x63, 0x02, 0x14 }, 0xf8, 0xf8, 0 }, | ||
| 52 | { { 0x63, 0x02, 0x28 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ | ||
| 53 | { { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ | ||
| 54 | { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ | ||
| 55 | { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, | ||
| 56 | { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ | ||
| 57 | { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ | ||
| 58 | { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, | ||
| 59 | { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ | ||
| 60 | { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ | ||
| 61 | { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, | ||
| 62 | { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ | ||
| 63 | /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ | ||
| 64 | { { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, | ||
| 65 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, | ||
| 66 | { { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ | ||
| 67 | { { 0x52, 0x01, 0x14 }, 0xff, 0xff, | ||
| 68 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ | ||
| 69 | }; | ||
| 70 | |||
| 71 | /* | ||
| 72 | * XXX - this entry is suspicious. First byte has zero lower nibble, | ||
| 73 | * which is what a normal mouse would report. Also, the value 0x0e | ||
| 74 | * isn't valid per PS/2 spec. | ||
| 75 | */ | ||
| 76 | |||
| 77 | /* | ||
| 78 | * PS/2 packet format | ||
| 79 | * | ||
| 80 | * byte 0: 0 0 YSGN XSGN 1 M R L | ||
| 81 | * byte 1: X7 X6 X5 X4 X3 X2 X1 X0 | ||
| 82 | * byte 2: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 | ||
| 83 | * | ||
| 84 | * Note that the device never signals overflow condition. | ||
| 85 | * | ||
| 86 | * ALPS absolute Mode - new format | ||
| 87 | * | ||
| 88 | * byte 0: 1 ? ? ? 1 ? ? ? | ||
| 89 | * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 | ||
| 90 | * byte 2: 0 x10 x9 x8 x7 ? fin ges | ||
| 91 | * byte 3: 0 y9 y8 y7 1 M R L | ||
| 92 | * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 | ||
| 93 | * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 | ||
| 94 | * | ||
| 95 | * Dualpoint device -- interleaved packet format | ||
| 96 | * | ||
| 97 | * byte 0: 1 1 0 0 1 1 1 1 | ||
| 98 | * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 | ||
| 99 | * byte 2: 0 x10 x9 x8 x7 0 fin ges | ||
| 100 | * byte 3: 0 0 YSGN XSGN 1 1 1 1 | ||
| 101 | * byte 4: X7 X6 X5 X4 X3 X2 X1 X0 | ||
| 102 | * byte 5: Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 | ||
| 103 | * byte 6: 0 y9 y8 y7 1 m r l | ||
| 104 | * byte 7: 0 y6 y5 y4 y3 y2 y1 y0 | ||
| 105 | * byte 8: 0 z6 z5 z4 z3 z2 z1 z0 | ||
| 106 | * | ||
| 107 | * CAPITALS = stick, miniscules = touchpad | ||
| 108 | * | ||
| 109 | * ?'s can have different meanings on different models, | ||
| 110 | * such as wheel rotation, extra buttons, stick buttons | ||
| 111 | * on a dualpoint, etc. | ||
| 112 | */ | ||
| 113 | |||
| 114 | static bool alps_is_valid_first_byte(const struct alps_model_info *model, | ||
| 115 | unsigned char data) | ||
| 116 | { | ||
| 117 | return (data & model->mask0) == model->byte0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static void alps_report_buttons(struct psmouse *psmouse, | ||
| 121 | struct input_dev *dev1, struct input_dev *dev2, | ||
| 122 | int left, int right, int middle) | ||
| 123 | { | ||
| 124 | struct input_dev *dev; | ||
| 125 | |||
| 126 | /* | ||
| 127 | * If shared button has already been reported on the | ||
| 128 | * other device (dev2) then this event should be also | ||
| 129 | * sent through that device. | ||
| 130 | */ | ||
| 131 | dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1; | ||
| 132 | input_report_key(dev, BTN_LEFT, left); | ||
| 133 | |||
| 134 | dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1; | ||
| 135 | input_report_key(dev, BTN_RIGHT, right); | ||
| 136 | |||
| 137 | dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1; | ||
| 138 | input_report_key(dev, BTN_MIDDLE, middle); | ||
| 139 | |||
| 140 | /* | ||
| 141 | * Sync the _other_ device now, we'll do the first | ||
| 142 | * device later once we report the rest of the events. | ||
| 143 | */ | ||
| 144 | input_sync(dev2); | ||
| 145 | } | ||
| 146 | |||
| 147 | static void alps_process_packet(struct psmouse *psmouse) | ||
| 148 | { | ||
| 149 | struct alps_data *priv = psmouse->private; | ||
| 150 | const struct alps_model_info *model = priv->i; | ||
| 151 | unsigned char *packet = psmouse->packet; | ||
| 152 | struct input_dev *dev = psmouse->dev; | ||
| 153 | struct input_dev *dev2 = priv->dev2; | ||
| 154 | int x, y, z, ges, fin, left, right, middle; | ||
| 155 | int back = 0, forward = 0; | ||
| 156 | |||
| 157 | if (model->flags & ALPS_OLDPROTO) { | ||
| 158 | left = packet[2] & 0x10; | ||
| 159 | right = packet[2] & 0x08; | ||
| 160 | middle = 0; | ||
| 161 | x = packet[1] | ((packet[0] & 0x07) << 7); | ||
| 162 | y = packet[4] | ((packet[3] & 0x07) << 7); | ||
| 163 | z = packet[5]; | ||
| 164 | } else { | ||
| 165 | left = packet[3] & 1; | ||
| 166 | right = packet[3] & 2; | ||
| 167 | middle = packet[3] & 4; | ||
| 168 | x = packet[1] | ((packet[2] & 0x78) << (7 - 3)); | ||
| 169 | y = packet[4] | ((packet[3] & 0x70) << (7 - 4)); | ||
| 170 | z = packet[5]; | ||
| 171 | } | ||
| 172 | |||
| 173 | if (model->flags & ALPS_FW_BK_1) { | ||
| 174 | back = packet[0] & 0x10; | ||
| 175 | forward = packet[2] & 4; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (model->flags & ALPS_FW_BK_2) { | ||
| 179 | back = packet[3] & 4; | ||
| 180 | forward = packet[2] & 4; | ||
| 181 | if ((middle = forward && back)) | ||
| 182 | forward = back = 0; | ||
| 183 | } | ||
| 184 | |||
| 185 | ges = packet[2] & 1; | ||
| 186 | fin = packet[2] & 2; | ||
| 187 | |||
| 188 | if ((model->flags & ALPS_DUALPOINT) && z == 127) { | ||
| 189 | input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); | ||
| 190 | input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); | ||
| 191 | |||
| 192 | alps_report_buttons(psmouse, dev2, dev, left, right, middle); | ||
| 193 | |||
| 194 | input_sync(dev2); | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | |||
| 198 | alps_report_buttons(psmouse, dev, dev2, left, right, middle); | ||
| 199 | |||
| 200 | /* Convert hardware tap to a reasonable Z value */ | ||
| 201 | if (ges && !fin) | ||
| 202 | z = 40; | ||
| 203 | |||
| 204 | /* | ||
| 205 | * A "tap and drag" operation is reported by the hardware as a transition | ||
| 206 | * from (!fin && ges) to (fin && ges). This should be translated to the | ||
| 207 | * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. | ||
| 208 | */ | ||
| 209 | if (ges && fin && !priv->prev_fin) { | ||
| 210 | input_report_abs(dev, ABS_X, x); | ||
| 211 | input_report_abs(dev, ABS_Y, y); | ||
| 212 | input_report_abs(dev, ABS_PRESSURE, 0); | ||
| 213 | input_report_key(dev, BTN_TOOL_FINGER, 0); | ||
| 214 | input_sync(dev); | ||
| 215 | } | ||
| 216 | priv->prev_fin = fin; | ||
| 217 | |||
| 218 | if (z > 30) | ||
| 219 | input_report_key(dev, BTN_TOUCH, 1); | ||
| 220 | if (z < 25) | ||
| 221 | input_report_key(dev, BTN_TOUCH, 0); | ||
| 222 | |||
| 223 | if (z > 0) { | ||
| 224 | input_report_abs(dev, ABS_X, x); | ||
| 225 | input_report_abs(dev, ABS_Y, y); | ||
| 226 | } | ||
| 227 | |||
| 228 | input_report_abs(dev, ABS_PRESSURE, z); | ||
| 229 | input_report_key(dev, BTN_TOOL_FINGER, z > 0); | ||
| 230 | |||
| 231 | if (model->flags & ALPS_WHEEL) | ||
| 232 | input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); | ||
| 233 | |||
| 234 | if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { | ||
| 235 | input_report_key(dev, BTN_FORWARD, forward); | ||
| 236 | input_report_key(dev, BTN_BACK, back); | ||
| 237 | } | ||
| 238 | |||
| 239 | if (model->flags & ALPS_FOUR_BUTTONS) { | ||
| 240 | input_report_key(dev, BTN_0, packet[2] & 4); | ||
| 241 | input_report_key(dev, BTN_1, packet[0] & 0x10); | ||
| 242 | input_report_key(dev, BTN_2, packet[3] & 4); | ||
| 243 | input_report_key(dev, BTN_3, packet[0] & 0x20); | ||
| 244 | } | ||
| 245 | |||
| 246 | input_sync(dev); | ||
| 247 | } | ||
| 248 | |||
| 249 | static void alps_report_bare_ps2_packet(struct psmouse *psmouse, | ||
| 250 | unsigned char packet[], | ||
| 251 | bool report_buttons) | ||
| 252 | { | ||
| 253 | struct alps_data *priv = psmouse->private; | ||
| 254 | struct input_dev *dev2 = priv->dev2; | ||
| 255 | |||
| 256 | if (report_buttons) | ||
| 257 | alps_report_buttons(psmouse, dev2, psmouse->dev, | ||
| 258 | packet[0] & 1, packet[0] & 2, packet[0] & 4); | ||
| 259 | |||
| 260 | input_report_rel(dev2, REL_X, | ||
| 261 | packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0); | ||
| 262 | input_report_rel(dev2, REL_Y, | ||
| 263 | packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0); | ||
| 264 | |||
| 265 | input_sync(dev2); | ||
| 266 | } | ||
| 267 | |||
| 268 | static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) | ||
| 269 | { | ||
| 270 | struct alps_data *priv = psmouse->private; | ||
| 271 | |||
| 272 | if (psmouse->pktcnt < 6) | ||
| 273 | return PSMOUSE_GOOD_DATA; | ||
| 274 | |||
| 275 | if (psmouse->pktcnt == 6) { | ||
| 276 | /* | ||
| 277 | * Start a timer to flush the packet if it ends up last | ||
| 278 | * 6-byte packet in the stream. Timer needs to fire | ||
| 279 | * psmouse core times out itself. 20 ms should be enough | ||
| 280 | * to decide if we are getting more data or not. | ||
| 281 | */ | ||
| 282 | mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20)); | ||
| 283 | return PSMOUSE_GOOD_DATA; | ||
| 284 | } | ||
| 285 | |||
| 286 | del_timer(&priv->timer); | ||
| 287 | |||
| 288 | if (psmouse->packet[6] & 0x80) { | ||
| 289 | |||
| 290 | /* | ||
| 291 | * Highest bit is set - that means we either had | ||
| 292 | * complete ALPS packet and this is start of the | ||
| 293 | * next packet or we got garbage. | ||
| 294 | */ | ||
| 295 | |||
| 296 | if (((psmouse->packet[3] | | ||
| 297 | psmouse->packet[4] | | ||
| 298 | psmouse->packet[5]) & 0x80) || | ||
| 299 | (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { | ||
| 300 | dbg("refusing packet %x %x %x %x " | ||
| 301 | "(suspected interleaved ps/2)\n", | ||
| 302 | psmouse->packet[3], psmouse->packet[4], | ||
| 303 | psmouse->packet[5], psmouse->packet[6]); | ||
| 304 | return PSMOUSE_BAD_DATA; | ||
| 305 | } | ||
| 306 | |||
| 307 | alps_process_packet(psmouse); | ||
| 308 | |||
| 309 | /* Continue with the next packet */ | ||
| 310 | psmouse->packet[0] = psmouse->packet[6]; | ||
| 311 | psmouse->pktcnt = 1; | ||
| 312 | |||
| 313 | } else { | ||
| 314 | |||
| 315 | /* | ||
| 316 | * High bit is 0 - that means that we indeed got a PS/2 | ||
| 317 | * packet in the middle of ALPS packet. | ||
| 318 | * | ||
| 319 | * There is also possibility that we got 6-byte ALPS | ||
| 320 | * packet followed by 3-byte packet from trackpoint. We | ||
| 321 | * can not distinguish between these 2 scenarios but | ||
| 322 | * becase the latter is unlikely to happen in course of | ||
| 323 | * normal operation (user would need to press all | ||
| 324 | * buttons on the pad and start moving trackpoint | ||
| 325 | * without touching the pad surface) we assume former. | ||
| 326 | * Even if we are wrong the wost thing that would happen | ||
| 327 | * the cursor would jump but we should not get protocol | ||
| 328 | * desynchronization. | ||
| 329 | */ | ||
| 330 | |||
| 331 | alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], | ||
| 332 | false); | ||
| 333 | |||
| 334 | /* | ||
| 335 | * Continue with the standard ALPS protocol handling, | ||
| 336 | * but make sure we won't process it as an interleaved | ||
| 337 | * packet again, which may happen if all buttons are | ||
| 338 | * pressed. To avoid this let's reset the 4th bit which | ||
| 339 | * is normally 1. | ||
| 340 | */ | ||
| 341 | psmouse->packet[3] = psmouse->packet[6] & 0xf7; | ||
| 342 | psmouse->pktcnt = 4; | ||
| 343 | } | ||
| 344 | |||
| 345 | return PSMOUSE_GOOD_DATA; | ||
| 346 | } | ||
| 347 | |||
| 348 | static void alps_flush_packet(unsigned long data) | ||
| 349 | { | ||
| 350 | struct psmouse *psmouse = (struct psmouse *)data; | ||
| 351 | |||
| 352 | serio_pause_rx(psmouse->ps2dev.serio); | ||
| 353 | |||
| 354 | if (psmouse->pktcnt == 6) { | ||
| 355 | |||
| 356 | /* | ||
| 357 | * We did not any more data in reasonable amount of time. | ||
| 358 | * Validate the last 3 bytes and process as a standard | ||
| 359 | * ALPS packet. | ||
| 360 | */ | ||
| 361 | if ((psmouse->packet[3] | | ||
| 362 | psmouse->packet[4] | | ||
| 363 | psmouse->packet[5]) & 0x80) { | ||
| 364 | dbg("refusing packet %x %x %x " | ||
| 365 | "(suspected interleaved ps/2)\n", | ||
| 366 | psmouse->packet[3], psmouse->packet[4], | ||
| 367 | psmouse->packet[5]); | ||
| 368 | } else { | ||
| 369 | alps_process_packet(psmouse); | ||
| 370 | } | ||
| 371 | psmouse->pktcnt = 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | serio_continue_rx(psmouse->ps2dev.serio); | ||
| 375 | } | ||
| 376 | |||
| 377 | static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) | ||
| 378 | { | ||
| 379 | struct alps_data *priv = psmouse->private; | ||
| 380 | const struct alps_model_info *model = priv->i; | ||
| 381 | |||
| 382 | if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ | ||
| 383 | if (psmouse->pktcnt == 3) { | ||
| 384 | alps_report_bare_ps2_packet(psmouse, psmouse->packet, | ||
| 385 | true); | ||
| 386 | return PSMOUSE_FULL_PACKET; | ||
| 387 | } | ||
| 388 | return PSMOUSE_GOOD_DATA; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ | ||
| 392 | |||
| 393 | if ((model->flags & ALPS_PS2_INTERLEAVED) && | ||
| 394 | psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) { | ||
| 395 | return alps_handle_interleaved_ps2(psmouse); | ||
| 396 | } | ||
| 397 | |||
| 398 | if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { | ||
| 399 | dbg("refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", | ||
| 400 | psmouse->packet[0], model->mask0, model->byte0); | ||
| 401 | return PSMOUSE_BAD_DATA; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* Bytes 2 - 6 should have 0 in the highest bit */ | ||
| 405 | if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= 6 && | ||
| 406 | (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { | ||
| 407 | dbg("refusing packet[%i] = %x\n", | ||
| 408 | psmouse->pktcnt - 1, psmouse->packet[psmouse->pktcnt - 1]); | ||
| 409 | return PSMOUSE_BAD_DATA; | ||
| 410 | } | ||
| 411 | |||
| 412 | if (psmouse->pktcnt == 6) { | ||
| 413 | alps_process_packet(psmouse); | ||
| 414 | return PSMOUSE_FULL_PACKET; | ||
| 415 | } | ||
| 416 | |||
| 417 | return PSMOUSE_GOOD_DATA; | ||
| 418 | } | ||
| 419 | |||
| 420 | static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) | ||
| 421 | { | ||
| 422 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 423 | static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; | ||
| 424 | unsigned char param[4]; | ||
| 425 | int i; | ||
| 426 | |||
| 427 | /* | ||
| 428 | * First try "E6 report". | ||
| 429 | * ALPS should return 0,0,10 or 0,0,100 | ||
| 430 | */ | ||
| 431 | param[0] = 0; | ||
| 432 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || | ||
| 433 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 434 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 435 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) | ||
| 436 | return NULL; | ||
| 437 | |||
| 438 | param[0] = param[1] = param[2] = 0xff; | ||
| 439 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
| 440 | return NULL; | ||
| 441 | |||
| 442 | dbg("E6 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); | ||
| 443 | |||
| 444 | if (param[0] != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) | ||
| 445 | return NULL; | ||
| 446 | |||
| 447 | /* | ||
| 448 | * Now try "E7 report". Allowed responses are in | ||
| 449 | * alps_model_data[].signature | ||
| 450 | */ | ||
| 451 | param[0] = 0; | ||
| 452 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || | ||
| 453 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 454 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 455 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) | ||
| 456 | return NULL; | ||
| 457 | |||
| 458 | param[0] = param[1] = param[2] = 0xff; | ||
| 459 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
| 460 | return NULL; | ||
| 461 | |||
| 462 | dbg("E7 report: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); | ||
| 463 | |||
| 464 | if (version) { | ||
| 465 | for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) | ||
| 466 | /* empty */; | ||
| 467 | *version = (param[0] << 8) | (param[1] << 4) | i; | ||
| 468 | } | ||
| 469 | |||
| 470 | for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) | ||
| 471 | if (!memcmp(param, alps_model_data[i].signature, | ||
| 472 | sizeof(alps_model_data[i].signature))) | ||
| 473 | return alps_model_data + i; | ||
| 474 | |||
| 475 | return NULL; | ||
| 476 | } | ||
| 477 | |||
| 478 | /* | ||
| 479 | * For DualPoint devices select the device that should respond to | ||
| 480 | * subsequent commands. It looks like glidepad is behind stickpointer, | ||
| 481 | * I'd thought it would be other way around... | ||
| 482 | */ | ||
| 483 | static int alps_passthrough_mode(struct psmouse *psmouse, bool enable) | ||
| 484 | { | ||
| 485 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 486 | int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; | ||
| 487 | |||
| 488 | if (ps2_command(ps2dev, NULL, cmd) || | ||
| 489 | ps2_command(ps2dev, NULL, cmd) || | ||
| 490 | ps2_command(ps2dev, NULL, cmd) || | ||
| 491 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) | ||
| 492 | return -1; | ||
| 493 | |||
| 494 | /* we may get 3 more bytes, just ignore them */ | ||
| 495 | ps2_drain(ps2dev, 3, 100); | ||
| 496 | |||
| 497 | return 0; | ||
| 498 | } | ||
| 499 | |||
| 500 | static int alps_absolute_mode(struct psmouse *psmouse) | ||
| 501 | { | ||
| 502 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 503 | |||
| 504 | /* Try ALPS magic knock - 4 disable before enable */ | ||
| 505 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 506 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 507 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 508 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 509 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) | ||
| 510 | return -1; | ||
| 511 | |||
| 512 | /* | ||
| 513 | * Switch mouse to poll (remote) mode so motion data will not | ||
| 514 | * get in our way | ||
| 515 | */ | ||
| 516 | return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); | ||
| 517 | } | ||
| 518 | |||
| 519 | static int alps_get_status(struct psmouse *psmouse, char *param) | ||
| 520 | { | ||
| 521 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 522 | |||
| 523 | /* Get status: 0xF5 0xF5 0xF5 0xE9 */ | ||
| 524 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 525 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 526 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 527 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
| 528 | return -1; | ||
| 529 | |||
| 530 | dbg("Status: %2.2x %2.2x %2.2x", param[0], param[1], param[2]); | ||
| 531 | |||
| 532 | return 0; | ||
| 533 | } | ||
| 534 | |||
| 535 | /* | ||
| 536 | * Turn touchpad tapping on or off. The sequences are: | ||
| 537 | * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, | ||
| 538 | * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. | ||
| 539 | * My guess that 0xE9 (GetInfo) is here as a sync point. | ||
| 540 | * For models that also have stickpointer (DualPoints) its tapping | ||
| 541 | * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but | ||
| 542 | * we don't fiddle with it. | ||
| 543 | */ | ||
| 544 | static int alps_tap_mode(struct psmouse *psmouse, int enable) | ||
| 545 | { | ||
| 546 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 547 | int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES; | ||
| 548 | unsigned char tap_arg = enable ? 0x0A : 0x00; | ||
| 549 | unsigned char param[4]; | ||
| 550 | |||
| 551 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) || | ||
| 552 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 553 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 554 | ps2_command(ps2dev, &tap_arg, cmd)) | ||
| 555 | return -1; | ||
| 556 | |||
| 557 | if (alps_get_status(psmouse, param)) | ||
| 558 | return -1; | ||
| 559 | |||
| 560 | return 0; | ||
| 561 | } | ||
| 562 | |||
| 563 | /* | ||
| 564 | * alps_poll() - poll the touchpad for current motion packet. | ||
| 565 | * Used in resync. | ||
| 566 | */ | ||
| 567 | static int alps_poll(struct psmouse *psmouse) | ||
| 568 | { | ||
| 569 | struct alps_data *priv = psmouse->private; | ||
| 570 | unsigned char buf[6]; | ||
| 571 | bool poll_failed; | ||
| 572 | |||
| 573 | if (priv->i->flags & ALPS_PASS) | ||
| 574 | alps_passthrough_mode(psmouse, true); | ||
| 575 | |||
| 576 | poll_failed = ps2_command(&psmouse->ps2dev, buf, | ||
| 577 | PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; | ||
| 578 | |||
| 579 | if (priv->i->flags & ALPS_PASS) | ||
| 580 | alps_passthrough_mode(psmouse, false); | ||
| 581 | |||
| 582 | if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) | ||
| 583 | return -1; | ||
| 584 | |||
| 585 | if ((psmouse->badbyte & 0xc8) == 0x08) { | ||
| 586 | /* | ||
| 587 | * Poll the track stick ... | ||
| 588 | */ | ||
| 589 | if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8))) | ||
| 590 | return -1; | ||
| 591 | } | ||
| 592 | |||
| 593 | memcpy(psmouse->packet, buf, sizeof(buf)); | ||
| 594 | return 0; | ||
| 595 | } | ||
| 596 | |||
| 597 | static int alps_hw_init(struct psmouse *psmouse) | ||
| 598 | { | ||
| 599 | struct alps_data *priv = psmouse->private; | ||
| 600 | const struct alps_model_info *model = priv->i; | ||
| 601 | |||
| 602 | if ((model->flags & ALPS_PASS) && | ||
| 603 | alps_passthrough_mode(psmouse, true)) { | ||
| 604 | return -1; | ||
| 605 | } | ||
| 606 | |||
| 607 | if (alps_tap_mode(psmouse, true)) { | ||
| 608 | printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n"); | ||
| 609 | return -1; | ||
| 610 | } | ||
| 611 | |||
| 612 | if (alps_absolute_mode(psmouse)) { | ||
| 613 | printk(KERN_ERR "alps.c: Failed to enable absolute mode\n"); | ||
| 614 | return -1; | ||
| 615 | } | ||
| 616 | |||
| 617 | if ((model->flags & ALPS_PASS) && | ||
| 618 | alps_passthrough_mode(psmouse, false)) { | ||
| 619 | return -1; | ||
| 620 | } | ||
| 621 | |||
| 622 | /* ALPS needs stream mode, otherwise it won't report any data */ | ||
| 623 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { | ||
| 624 | printk(KERN_ERR "alps.c: Failed to enable stream mode\n"); | ||
| 625 | return -1; | ||
| 626 | } | ||
| 627 | |||
| 628 | return 0; | ||
| 629 | } | ||
| 630 | |||
| 631 | static int alps_reconnect(struct psmouse *psmouse) | ||
| 632 | { | ||
| 633 | const struct alps_model_info *model; | ||
| 634 | |||
| 635 | psmouse_reset(psmouse); | ||
| 636 | |||
| 637 | model = alps_get_model(psmouse, NULL); | ||
| 638 | if (!model) | ||
| 639 | return -1; | ||
| 640 | |||
| 641 | return alps_hw_init(psmouse); | ||
| 642 | } | ||
| 643 | |||
| 644 | static void alps_disconnect(struct psmouse *psmouse) | ||
| 645 | { | ||
| 646 | struct alps_data *priv = psmouse->private; | ||
| 647 | |||
| 648 | psmouse_reset(psmouse); | ||
| 649 | del_timer_sync(&priv->timer); | ||
| 650 | input_unregister_device(priv->dev2); | ||
| 651 | kfree(priv); | ||
| 652 | } | ||
| 653 | |||
| 654 | int alps_init(struct psmouse *psmouse) | ||
| 655 | { | ||
| 656 | struct alps_data *priv; | ||
| 657 | const struct alps_model_info *model; | ||
| 658 | struct input_dev *dev1 = psmouse->dev, *dev2; | ||
| 659 | int version; | ||
| 660 | |||
| 661 | priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); | ||
| 662 | dev2 = input_allocate_device(); | ||
| 663 | if (!priv || !dev2) | ||
| 664 | goto init_fail; | ||
| 665 | |||
| 666 | priv->dev2 = dev2; | ||
| 667 | setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse); | ||
| 668 | |||
| 669 | psmouse->private = priv; | ||
| 670 | |||
| 671 | model = alps_get_model(psmouse, &version); | ||
| 672 | if (!model) | ||
| 673 | goto init_fail; | ||
| 674 | |||
| 675 | priv->i = model; | ||
| 676 | |||
| 677 | if (alps_hw_init(psmouse)) | ||
| 678 | goto init_fail; | ||
| 679 | |||
| 680 | /* | ||
| 681 | * Undo part of setup done for us by psmouse core since touchpad | ||
| 682 | * is not a relative device. | ||
| 683 | */ | ||
| 684 | __clear_bit(EV_REL, dev1->evbit); | ||
| 685 | __clear_bit(REL_X, dev1->relbit); | ||
| 686 | __clear_bit(REL_Y, dev1->relbit); | ||
| 687 | |||
| 688 | /* | ||
| 689 | * Now set up our capabilities. | ||
| 690 | */ | ||
| 691 | dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); | ||
| 692 | dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); | ||
| 693 | dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER); | ||
| 694 | dev1->keybit[BIT_WORD(BTN_LEFT)] |= | ||
| 695 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); | ||
| 696 | |||
| 697 | dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); | ||
| 698 | input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); | ||
| 699 | input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); | ||
| 700 | input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); | ||
| 701 | |||
| 702 | if (model->flags & ALPS_WHEEL) { | ||
| 703 | dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); | ||
| 704 | dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); | ||
| 705 | } | ||
| 706 | |||
| 707 | if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { | ||
| 708 | dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); | ||
| 709 | dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); | ||
| 710 | } | ||
| 711 | |||
| 712 | if (model->flags & ALPS_FOUR_BUTTONS) { | ||
| 713 | dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); | ||
| 714 | dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); | ||
| 715 | dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); | ||
| 716 | dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); | ||
| 717 | } else { | ||
| 718 | dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); | ||
| 719 | } | ||
| 720 | |||
| 721 | snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); | ||
| 722 | dev2->phys = priv->phys; | ||
| 723 | dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; | ||
| 724 | dev2->id.bustype = BUS_I8042; | ||
| 725 | dev2->id.vendor = 0x0002; | ||
| 726 | dev2->id.product = PSMOUSE_ALPS; | ||
| 727 | dev2->id.version = 0x0000; | ||
| 728 | dev2->dev.parent = &psmouse->ps2dev.serio->dev; | ||
| 729 | |||
| 730 | dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); | ||
| 731 | dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); | ||
| 732 | dev2->keybit[BIT_WORD(BTN_LEFT)] = | ||
| 733 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); | ||
| 734 | |||
| 735 | if (input_register_device(priv->dev2)) | ||
| 736 | goto init_fail; | ||
| 737 | |||
| 738 | psmouse->protocol_handler = alps_process_byte; | ||
| 739 | psmouse->poll = alps_poll; | ||
| 740 | psmouse->disconnect = alps_disconnect; | ||
| 741 | psmouse->reconnect = alps_reconnect; | ||
| 742 | psmouse->pktsize = 6; | ||
| 743 | |||
| 744 | /* We are having trouble resyncing ALPS touchpads so disable it for now */ | ||
| 745 | psmouse->resync_time = 0; | ||
| 746 | |||
| 747 | return 0; | ||
| 748 | |||
| 749 | init_fail: | ||
| 750 | psmouse_reset(psmouse); | ||
| 751 | input_free_device(dev2); | ||
| 752 | kfree(priv); | ||
| 753 | psmouse->private = NULL; | ||
| 754 | return -1; | ||
| 755 | } | ||
| 756 | |||
| 757 | int alps_detect(struct psmouse *psmouse, bool set_properties) | ||
| 758 | { | ||
| 759 | int version; | ||
| 760 | const struct alps_model_info *model; | ||
| 761 | |||
| 762 | model = alps_get_model(psmouse, &version); | ||
| 763 | if (!model) | ||
| 764 | return -1; | ||
| 765 | |||
| 766 | if (set_properties) { | ||
| 767 | psmouse->vendor = "ALPS"; | ||
| 768 | psmouse->name = model->flags & ALPS_DUALPOINT ? | ||
| 769 | "DualPoint TouchPad" : "GlidePoint"; | ||
| 770 | psmouse->model = version; | ||
| 771 | } | ||
| 772 | return 0; | ||
| 773 | } | ||
| 774 | |||
diff --git a/usr/src/dkms_source_tree/alps.h b/usr/src/dkms_source_tree/alps.h new file mode 100644 index 0000000..904ed8b --- /dev/null +++ b/usr/src/dkms_source_tree/alps.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | /* | ||
| 2 | * ALPS touchpad PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003 Peter Osterlund <petero2@telia.com> | ||
| 5 | * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> | ||
| 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 | #ifndef _ALPS_H | ||
| 13 | #define _ALPS_H | ||
| 14 | |||
| 15 | struct alps_model_info { | ||
| 16 | unsigned char signature[3]; | ||
| 17 | unsigned char byte0, mask0; | ||
| 18 | unsigned char flags; | ||
| 19 | }; | ||
| 20 | |||
| 21 | struct alps_data { | ||
| 22 | struct input_dev *dev2; /* Relative device */ | ||
| 23 | char phys[32]; /* Phys */ | ||
| 24 | const struct alps_model_info *i;/* Info */ | ||
| 25 | int prev_fin; /* Finger bit from previous packet */ | ||
| 26 | struct timer_list timer; | ||
| 27 | }; | ||
| 28 | |||
| 29 | #ifdef CONFIG_MOUSE_PS2_ALPS | ||
| 30 | int alps_detect(struct psmouse *psmouse, bool set_properties); | ||
| 31 | int alps_init(struct psmouse *psmouse); | ||
| 32 | #else | ||
| 33 | inline int alps_detect(struct psmouse *psmouse, bool set_properties) | ||
| 34 | { | ||
| 35 | return -ENOSYS; | ||
| 36 | } | ||
| 37 | inline int alps_init(struct psmouse *psmouse) | ||
| 38 | { | ||
| 39 | return -ENOSYS; | ||
| 40 | } | ||
| 41 | #endif /* CONFIG_MOUSE_PS2_ALPS */ | ||
| 42 | |||
| 43 | #endif | ||
diff --git a/usr/src/dkms_source_tree/dkms.conf b/usr/src/dkms_source_tree/dkms.conf new file mode 100644 index 0000000..c7da472 --- /dev/null +++ b/usr/src/dkms_source_tree/dkms.conf | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | NAME=synaptics | ||
| 2 | VERSION=1.0.0 | ||
| 3 | PACKAGE_NAME="$NAME" | ||
| 4 | PACKAGE_VERSION="$VERSION" | ||
| 5 | MAKE[0]="make -C ${kernel_source_dir} | ||
| 6 | SUBDIRS=${dkms_tree}/${PACKAGE_NAME}/${PACKAGE_VERSION}/build modules" | ||
| 7 | BUILT_MODULE_NAME[0]="psmouse" | ||
| 8 | DEST_MODULE_LOCATION[0]="/kernel/drivers/input/mouse" | ||
| 9 | POST_INSTALL="scripts/synaptics-post-install" | ||
| 10 | AUTOINSTALL="yes" | ||
diff --git a/usr/src/dkms_source_tree/elantech.c b/usr/src/dkms_source_tree/elantech.c new file mode 100644 index 0000000..b18862b --- /dev/null +++ b/usr/src/dkms_source_tree/elantech.c | |||
| @@ -0,0 +1,769 @@ | |||
| 1 | /* | ||
| 2 | * Elantech Touchpad driver (v6) | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published | ||
| 8 | * by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * Trademarks are the property of their respective owners. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #define pr_fmt(fmt) KBUILD_BASENAME ": " fmt | ||
| 14 | |||
| 15 | #include <linux/delay.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/input.h> | ||
| 19 | #include <linux/serio.h> | ||
| 20 | #include <linux/libps2.h> | ||
| 21 | #include "psmouse.h" | ||
| 22 | #include "elantech.h" | ||
| 23 | |||
| 24 | #define elantech_debug(fmt, ...) \ | ||
| 25 | do { \ | ||
| 26 | if (etd->debug) \ | ||
| 27 | printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); \ | ||
| 28 | } while (0) | ||
| 29 | |||
| 30 | static bool force_elantech; | ||
| 31 | module_param_named(force_elantech, force_elantech, bool, 0644); | ||
| 32 | MODULE_PARM_DESC(force_elantech, "Force the Elantech PS/2 protocol extension to be used, 1 = enabled, 0 = disabled (default)."); | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Send a Synaptics style sliced query command | ||
| 36 | */ | ||
| 37 | static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, | ||
| 38 | unsigned char *param) | ||
| 39 | { | ||
| 40 | if (psmouse_sliced_command(psmouse, c) || | ||
| 41 | ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { | ||
| 42 | pr_err("synaptics_send_cmd query 0x%02x failed.\n", c); | ||
| 43 | return -1; | ||
| 44 | } | ||
| 45 | |||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | /* | ||
| 50 | * A retrying version of ps2_command | ||
| 51 | */ | ||
| 52 | static int elantech_ps2_command(struct psmouse *psmouse, | ||
| 53 | unsigned char *param, int command) | ||
| 54 | { | ||
| 55 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 56 | struct elantech_data *etd = psmouse->private; | ||
| 57 | int rc; | ||
| 58 | int tries = ETP_PS2_COMMAND_TRIES; | ||
| 59 | |||
| 60 | do { | ||
| 61 | rc = ps2_command(ps2dev, param, command); | ||
| 62 | if (rc == 0) | ||
| 63 | break; | ||
| 64 | tries--; | ||
| 65 | elantech_debug("retrying ps2 command 0x%02x (%d).\n", | ||
| 66 | command, tries); | ||
| 67 | msleep(ETP_PS2_COMMAND_DELAY); | ||
| 68 | } while (tries > 0); | ||
| 69 | |||
| 70 | if (rc) | ||
| 71 | pr_err("ps2 command 0x%02x failed.\n", command); | ||
| 72 | |||
| 73 | return rc; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Send an Elantech style special command to read a value from a register | ||
| 78 | */ | ||
| 79 | static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, | ||
| 80 | unsigned char *val) | ||
| 81 | { | ||
| 82 | struct elantech_data *etd = psmouse->private; | ||
| 83 | unsigned char param[3]; | ||
| 84 | int rc = 0; | ||
| 85 | |||
| 86 | if (reg < 0x10 || reg > 0x26) | ||
| 87 | return -1; | ||
| 88 | |||
| 89 | if (reg > 0x11 && reg < 0x20) | ||
| 90 | return -1; | ||
| 91 | |||
| 92 | switch (etd->hw_version) { | ||
| 93 | case 1: | ||
| 94 | if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || | ||
| 95 | psmouse_sliced_command(psmouse, reg) || | ||
| 96 | ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) { | ||
| 97 | rc = -1; | ||
| 98 | } | ||
| 99 | break; | ||
| 100 | |||
| 101 | case 2: | ||
| 102 | if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || | ||
| 103 | elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READ) || | ||
| 104 | elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || | ||
| 105 | elantech_ps2_command(psmouse, NULL, reg) || | ||
| 106 | elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { | ||
| 107 | rc = -1; | ||
| 108 | } | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | |||
| 112 | if (rc) | ||
| 113 | pr_err("failed to read register 0x%02x.\n", reg); | ||
| 114 | else | ||
| 115 | *val = param[0]; | ||
| 116 | |||
| 117 | return rc; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* | ||
| 121 | * Send an Elantech style special command to write a register with a value | ||
| 122 | */ | ||
| 123 | static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, | ||
| 124 | unsigned char val) | ||
| 125 | { | ||
| 126 | struct elantech_data *etd = psmouse->private; | ||
| 127 | int rc = 0; | ||
| 128 | |||
| 129 | if (reg < 0x10 || reg > 0x26) | ||
| 130 | return -1; | ||
| 131 | |||
| 132 | if (reg > 0x11 && reg < 0x20) | ||
| 133 | return -1; | ||
| 134 | |||
| 135 | switch (etd->hw_version) { | ||
| 136 | case 1: | ||
| 137 | if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || | ||
| 138 | psmouse_sliced_command(psmouse, reg) || | ||
| 139 | psmouse_sliced_command(psmouse, val) || | ||
| 140 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) { | ||
| 141 | rc = -1; | ||
| 142 | } | ||
| 143 | break; | ||
| 144 | |||
| 145 | case 2: | ||
| 146 | if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || | ||
| 147 | elantech_ps2_command(psmouse, NULL, ETP_REGISTER_WRITE) || | ||
| 148 | elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || | ||
| 149 | elantech_ps2_command(psmouse, NULL, reg) || | ||
| 150 | elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || | ||
| 151 | elantech_ps2_command(psmouse, NULL, val) || | ||
| 152 | elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { | ||
| 153 | rc = -1; | ||
| 154 | } | ||
| 155 | break; | ||
| 156 | } | ||
| 157 | |||
| 158 | if (rc) | ||
| 159 | pr_err("failed to write register 0x%02x with value 0x%02x.\n", | ||
| 160 | reg, val); | ||
| 161 | |||
| 162 | return rc; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * Dump a complete mouse movement packet to the syslog | ||
| 167 | */ | ||
| 168 | static void elantech_packet_dump(unsigned char *packet, int size) | ||
| 169 | { | ||
| 170 | int i; | ||
| 171 | |||
| 172 | printk(KERN_DEBUG pr_fmt("PS/2 packet [")); | ||
| 173 | for (i = 0; i < size; i++) | ||
| 174 | printk("%s0x%02x ", (i) ? ", " : " ", packet[i]); | ||
| 175 | printk("]\n"); | ||
| 176 | } | ||
| 177 | |||
| 178 | /* | ||
| 179 | * Interpret complete data packets and report absolute mode input events for | ||
| 180 | * hardware version 1. (4 byte packets) | ||
| 181 | */ | ||
| 182 | static void elantech_report_absolute_v1(struct psmouse *psmouse) | ||
| 183 | { | ||
| 184 | struct input_dev *dev = psmouse->dev; | ||
| 185 | struct elantech_data *etd = psmouse->private; | ||
| 186 | unsigned char *packet = psmouse->packet; | ||
| 187 | int fingers; | ||
| 188 | static int old_fingers; | ||
| 189 | |||
| 190 | if (etd->fw_version < 0x020000) { | ||
| 191 | /* | ||
| 192 | * byte 0: D U p1 p2 1 p3 R L | ||
| 193 | * byte 1: f 0 th tw x9 x8 y9 y8 | ||
| 194 | */ | ||
| 195 | fingers = ((packet[1] & 0x80) >> 7) + | ||
| 196 | ((packet[1] & 0x30) >> 4); | ||
| 197 | } else { | ||
| 198 | /* | ||
| 199 | * byte 0: n1 n0 p2 p1 1 p3 R L | ||
| 200 | * byte 1: 0 0 0 0 x9 x8 y9 y8 | ||
| 201 | */ | ||
| 202 | fingers = (packet[0] & 0xc0) >> 6; | ||
| 203 | } | ||
| 204 | |||
| 205 | if (etd->jumpy_cursor) { | ||
| 206 | /* Discard packets that are likely to have bogus coordinates */ | ||
| 207 | if (fingers > old_fingers) { | ||
| 208 | elantech_debug("discarding packet\n"); | ||
| 209 | goto discard_packet_v1; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | input_report_key(dev, BTN_TOUCH, fingers != 0); | ||
| 214 | |||
| 215 | /* | ||
| 216 | * byte 2: x7 x6 x5 x4 x3 x2 x1 x0 | ||
| 217 | * byte 3: y7 y6 y5 y4 y3 y2 y1 y0 | ||
| 218 | */ | ||
| 219 | if (fingers) { | ||
| 220 | input_report_abs(dev, ABS_X, | ||
| 221 | ((packet[1] & 0x0c) << 6) | packet[2]); | ||
| 222 | input_report_abs(dev, ABS_Y, | ||
| 223 | ETP_YMAX_V1 - (((packet[1] & 0x03) << 8) | packet[3])); | ||
| 224 | } | ||
| 225 | |||
| 226 | input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); | ||
| 227 | input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); | ||
| 228 | input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); | ||
| 229 | input_report_key(dev, BTN_LEFT, packet[0] & 0x01); | ||
| 230 | input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); | ||
| 231 | |||
| 232 | if (etd->fw_version < 0x020000 && | ||
| 233 | (etd->capabilities & ETP_CAP_HAS_ROCKER)) { | ||
| 234 | /* rocker up */ | ||
| 235 | input_report_key(dev, BTN_FORWARD, packet[0] & 0x40); | ||
| 236 | /* rocker down */ | ||
| 237 | input_report_key(dev, BTN_BACK, packet[0] & 0x80); | ||
| 238 | } | ||
| 239 | |||
| 240 | input_sync(dev); | ||
| 241 | |||
| 242 | discard_packet_v1: | ||
| 243 | old_fingers = fingers; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* | ||
| 247 | * Interpret complete data packets and report absolute mode input events for | ||
| 248 | * hardware version 2. (6 byte packets) | ||
| 249 | */ | ||
| 250 | static void elantech_report_absolute_v2(struct psmouse *psmouse) | ||
| 251 | { | ||
| 252 | struct input_dev *dev = psmouse->dev; | ||
| 253 | unsigned char *packet = psmouse->packet; | ||
| 254 | int fingers, x1, y1, x2, y2; | ||
| 255 | |||
| 256 | /* byte 0: n1 n0 . . . . R L */ | ||
| 257 | fingers = (packet[0] & 0xc0) >> 6; | ||
| 258 | input_report_key(dev, BTN_TOUCH, fingers != 0); | ||
| 259 | |||
| 260 | switch (fingers) { | ||
| 261 | case 1: | ||
| 262 | /* | ||
| 263 | * byte 1: . . . . . x10 x9 x8 | ||
| 264 | * byte 2: x7 x6 x5 x4 x4 x2 x1 x0 | ||
| 265 | */ | ||
| 266 | input_report_abs(dev, ABS_X, | ||
| 267 | ((packet[1] & 0x07) << 8) | packet[2]); | ||
| 268 | /* | ||
| 269 | * byte 4: . . . . . . y9 y8 | ||
| 270 | * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 | ||
| 271 | */ | ||
| 272 | input_report_abs(dev, ABS_Y, | ||
| 273 | ETP_YMAX_V2 - (((packet[4] & 0x03) << 8) | packet[5])); | ||
| 274 | break; | ||
| 275 | |||
| 276 | case 2: | ||
| 277 | /* | ||
| 278 | * The coordinate of each finger is reported separately | ||
| 279 | * with a lower resolution for two finger touches: | ||
| 280 | * byte 0: . . ay8 ax8 . . . . | ||
| 281 | * byte 1: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 | ||
| 282 | */ | ||
| 283 | x1 = ((packet[0] & 0x10) << 4) | packet[1]; | ||
| 284 | /* byte 2: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 */ | ||
| 285 | y1 = ETP_2FT_YMAX - (((packet[0] & 0x20) << 3) | packet[2]); | ||
| 286 | /* | ||
| 287 | * byte 3: . . by8 bx8 . . . . | ||
| 288 | * byte 4: bx7 bx6 bx5 bx4 bx3 bx2 bx1 bx0 | ||
| 289 | */ | ||
| 290 | x2 = ((packet[3] & 0x10) << 4) | packet[4]; | ||
| 291 | /* byte 5: by7 by8 by5 by4 by3 by2 by1 by0 */ | ||
| 292 | y2 = ETP_2FT_YMAX - (((packet[3] & 0x20) << 3) | packet[5]); | ||
| 293 | /* | ||
| 294 | * For compatibility with the X Synaptics driver scale up | ||
| 295 | * one coordinate and report as ordinary mouse movent | ||
| 296 | */ | ||
| 297 | input_report_abs(dev, ABS_X, x1 << 2); | ||
| 298 | input_report_abs(dev, ABS_Y, y1 << 2); | ||
| 299 | /* | ||
| 300 | * For compatibility with the proprietary X Elantech driver | ||
| 301 | * report both coordinates as hat coordinates | ||
| 302 | */ | ||
| 303 | input_report_abs(dev, ABS_HAT0X, x1); | ||
| 304 | input_report_abs(dev, ABS_HAT0Y, y1); | ||
| 305 | input_report_abs(dev, ABS_HAT1X, x2); | ||
| 306 | input_report_abs(dev, ABS_HAT1Y, y2); | ||
| 307 | break; | ||
| 308 | } | ||
| 309 | |||
| 310 | input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); | ||
| 311 | input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); | ||
| 312 | input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); | ||
| 313 | input_report_key(dev, BTN_LEFT, packet[0] & 0x01); | ||
| 314 | input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); | ||
| 315 | |||
| 316 | input_sync(dev); | ||
| 317 | } | ||
| 318 | |||
| 319 | static int elantech_check_parity_v1(struct psmouse *psmouse) | ||
| 320 | { | ||
| 321 | struct elantech_data *etd = psmouse->private; | ||
| 322 | unsigned char *packet = psmouse->packet; | ||
| 323 | unsigned char p1, p2, p3; | ||
| 324 | |||
| 325 | /* Parity bits are placed differently */ | ||
| 326 | if (etd->fw_version < 0x020000) { | ||
| 327 | /* byte 0: D U p1 p2 1 p3 R L */ | ||
| 328 | p1 = (packet[0] & 0x20) >> 5; | ||
| 329 | p2 = (packet[0] & 0x10) >> 4; | ||
| 330 | } else { | ||
| 331 | /* byte 0: n1 n0 p2 p1 1 p3 R L */ | ||
| 332 | p1 = (packet[0] & 0x10) >> 4; | ||
| 333 | p2 = (packet[0] & 0x20) >> 5; | ||
| 334 | } | ||
| 335 | |||
| 336 | p3 = (packet[0] & 0x04) >> 2; | ||
| 337 | |||
| 338 | return etd->parity[packet[1]] == p1 && | ||
| 339 | etd->parity[packet[2]] == p2 && | ||
| 340 | etd->parity[packet[3]] == p3; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* | ||
| 344 | * Process byte stream from mouse and handle complete packets | ||
| 345 | */ | ||
| 346 | static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) | ||
| 347 | { | ||
| 348 | struct elantech_data *etd = psmouse->private; | ||
| 349 | |||
| 350 | if (psmouse->pktcnt < psmouse->pktsize) | ||
| 351 | return PSMOUSE_GOOD_DATA; | ||
| 352 | |||
| 353 | if (etd->debug > 1) | ||
| 354 | elantech_packet_dump(psmouse->packet, psmouse->pktsize); | ||
| 355 | |||
| 356 | switch (etd->hw_version) { | ||
| 357 | case 1: | ||
| 358 | if (etd->paritycheck && !elantech_check_parity_v1(psmouse)) | ||
| 359 | return PSMOUSE_BAD_DATA; | ||
| 360 | |||
| 361 | elantech_report_absolute_v1(psmouse); | ||
| 362 | break; | ||
| 363 | |||
| 364 | case 2: | ||
| 365 | /* We don't know how to check parity in protocol v2 */ | ||
| 366 | elantech_report_absolute_v2(psmouse); | ||
| 367 | break; | ||
| 368 | } | ||
| 369 | |||
| 370 | return PSMOUSE_FULL_PACKET; | ||
| 371 | } | ||
| 372 | |||
| 373 | /* | ||
| 374 | * Put the touchpad into absolute mode | ||
| 375 | */ | ||
| 376 | static int elantech_set_absolute_mode(struct psmouse *psmouse) | ||
| 377 | { | ||
| 378 | struct elantech_data *etd = psmouse->private; | ||
| 379 | unsigned char val; | ||
| 380 | int tries = ETP_READ_BACK_TRIES; | ||
| 381 | int rc = 0; | ||
| 382 | |||
| 383 | switch (etd->hw_version) { | ||
| 384 | case 1: | ||
| 385 | etd->reg_10 = 0x16; | ||
| 386 | etd->reg_11 = 0x8f; | ||
| 387 | if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || | ||
| 388 | elantech_write_reg(psmouse, 0x11, etd->reg_11)) { | ||
| 389 | rc = -1; | ||
| 390 | } | ||
| 391 | break; | ||
| 392 | |||
| 393 | case 2: | ||
| 394 | /* Windows driver values */ | ||
| 395 | etd->reg_10 = 0x54; | ||
| 396 | etd->reg_11 = 0x88; /* 0x8a */ | ||
| 397 | etd->reg_21 = 0x60; /* 0x00 */ | ||
| 398 | if (elantech_write_reg(psmouse, 0x10, etd->reg_10) || | ||
| 399 | elantech_write_reg(psmouse, 0x11, etd->reg_11) || | ||
| 400 | elantech_write_reg(psmouse, 0x21, etd->reg_21)) { | ||
| 401 | rc = -1; | ||
| 402 | break; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | if (rc == 0) { | ||
| 407 | /* | ||
| 408 | * Read back reg 0x10. For hardware version 1 we must make | ||
| 409 | * sure the absolute mode bit is set. For hardware version 2 | ||
| 410 | * the touchpad is probably initalising and not ready until | ||
| 411 | * we read back the value we just wrote. | ||
| 412 | */ | ||
| 413 | do { | ||
| 414 | rc = elantech_read_reg(psmouse, 0x10, &val); | ||
| 415 | if (rc == 0) | ||
| 416 | break; | ||
| 417 | tries--; | ||
| 418 | elantech_debug("retrying read (%d).\n", tries); | ||
| 419 | msleep(ETP_READ_BACK_DELAY); | ||
| 420 | } while (tries > 0); | ||
| 421 | |||
| 422 | if (rc) { | ||
| 423 | pr_err("failed to read back register 0x10.\n"); | ||
| 424 | } else if (etd->hw_version == 1 && | ||
| 425 | !(val & ETP_R10_ABSOLUTE_MODE)) { | ||
| 426 | pr_err("touchpad refuses to switch to absolute mode.\n"); | ||
| 427 | rc = -1; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | if (rc) | ||
| 432 | pr_err("failed to initialise registers.\n"); | ||
| 433 | |||
| 434 | return rc; | ||
| 435 | } | ||
| 436 | |||
| 437 | /* | ||
| 438 | * Set the appropriate event bits for the input subsystem | ||
| 439 | */ | ||
| 440 | static void elantech_set_input_params(struct psmouse *psmouse) | ||
| 441 | { | ||
| 442 | struct input_dev *dev = psmouse->dev; | ||
| 443 | struct elantech_data *etd = psmouse->private; | ||
| 444 | |||
| 445 | __set_bit(EV_KEY, dev->evbit); | ||
| 446 | __set_bit(EV_ABS, dev->evbit); | ||
| 447 | __clear_bit(EV_REL, dev->evbit); | ||
| 448 | |||
| 449 | __set_bit(BTN_LEFT, dev->keybit); | ||
| 450 | __set_bit(BTN_RIGHT, dev->keybit); | ||
| 451 | |||
| 452 | __set_bit(BTN_TOUCH, dev->keybit); | ||
| 453 | __set_bit(BTN_TOOL_FINGER, dev->keybit); | ||
| 454 | __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); | ||
| 455 | __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); | ||
| 456 | |||
| 457 | switch (etd->hw_version) { | ||
| 458 | case 1: | ||
| 459 | /* Rocker button */ | ||
| 460 | if (etd->fw_version < 0x020000 && | ||
| 461 | (etd->capabilities & ETP_CAP_HAS_ROCKER)) { | ||
| 462 | __set_bit(BTN_FORWARD, dev->keybit); | ||
| 463 | __set_bit(BTN_BACK, dev->keybit); | ||
| 464 | } | ||
| 465 | input_set_abs_params(dev, ABS_X, ETP_XMIN_V1, ETP_XMAX_V1, 0, 0); | ||
| 466 | input_set_abs_params(dev, ABS_Y, ETP_YMIN_V1, ETP_YMAX_V1, 0, 0); | ||
| 467 | break; | ||
| 468 | |||
| 469 | case 2: | ||
| 470 | input_set_abs_params(dev, ABS_X, ETP_XMIN_V2, ETP_XMAX_V2, 0, 0); | ||
| 471 | input_set_abs_params(dev, ABS_Y, ETP_YMIN_V2, ETP_YMAX_V2, 0, 0); | ||
| 472 | input_set_abs_params(dev, ABS_HAT0X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); | ||
| 473 | input_set_abs_params(dev, ABS_HAT0Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); | ||
| 474 | input_set_abs_params(dev, ABS_HAT1X, ETP_2FT_XMIN, ETP_2FT_XMAX, 0, 0); | ||
| 475 | input_set_abs_params(dev, ABS_HAT1Y, ETP_2FT_YMIN, ETP_2FT_YMAX, 0, 0); | ||
| 476 | break; | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | struct elantech_attr_data { | ||
| 481 | size_t field_offset; | ||
| 482 | unsigned char reg; | ||
| 483 | }; | ||
| 484 | |||
| 485 | /* | ||
| 486 | * Display a register value by reading a sysfs entry | ||
| 487 | */ | ||
| 488 | static ssize_t elantech_show_int_attr(struct psmouse *psmouse, void *data, | ||
| 489 | char *buf) | ||
| 490 | { | ||
| 491 | struct elantech_data *etd = psmouse->private; | ||
| 492 | struct elantech_attr_data *attr = data; | ||
| 493 | unsigned char *reg = (unsigned char *) etd + attr->field_offset; | ||
| 494 | int rc = 0; | ||
| 495 | |||
| 496 | if (attr->reg) | ||
| 497 | rc = elantech_read_reg(psmouse, attr->reg, reg); | ||
| 498 | |||
| 499 | return sprintf(buf, "0x%02x\n", (attr->reg && rc) ? -1 : *reg); | ||
| 500 | } | ||
| 501 | |||
| 502 | /* | ||
| 503 | * Write a register value by writing a sysfs entry | ||
| 504 | */ | ||
| 505 | static ssize_t elantech_set_int_attr(struct psmouse *psmouse, | ||
| 506 | void *data, const char *buf, size_t count) | ||
| 507 | { | ||
| 508 | struct elantech_data *etd = psmouse->private; | ||
| 509 | struct elantech_attr_data *attr = data; | ||
| 510 | unsigned char *reg = (unsigned char *) etd + attr->field_offset; | ||
| 511 | unsigned long value; | ||
| 512 | int err; | ||
| 513 | |||
| 514 | err = strict_strtoul(buf, 16, &value); | ||
| 515 | if (err) | ||
| 516 | return err; | ||
| 517 | |||
| 518 | if (value > 0xff) | ||
| 519 | return -EINVAL; | ||
| 520 | |||
| 521 | /* Do we need to preserve some bits for version 2 hardware too? */ | ||
| 522 | if (etd->hw_version == 1) { | ||
| 523 | if (attr->reg == 0x10) | ||
| 524 | /* Force absolute mode always on */ | ||
| 525 | value |= ETP_R10_ABSOLUTE_MODE; | ||
| 526 | else if (attr->reg == 0x11) | ||
| 527 | /* Force 4 byte mode always on */ | ||
| 528 | value |= ETP_R11_4_BYTE_MODE; | ||
| 529 | } | ||
| 530 | |||
| 531 | if (!attr->reg || elantech_write_reg(psmouse, attr->reg, value) == 0) | ||
| 532 | *reg = value; | ||
| 533 | |||
| 534 | return count; | ||
| 535 | } | ||
| 536 | |||
| 537 | #define ELANTECH_INT_ATTR(_name, _register) \ | ||
| 538 | static struct elantech_attr_data elantech_attr_##_name = { \ | ||
| 539 | .field_offset = offsetof(struct elantech_data, _name), \ | ||
| 540 | .reg = _register, \ | ||
| 541 | }; \ | ||
| 542 | PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ | ||
| 543 | &elantech_attr_##_name, \ | ||
| 544 | elantech_show_int_attr, \ | ||
| 545 | elantech_set_int_attr) | ||
| 546 | |||
| 547 | ELANTECH_INT_ATTR(reg_10, 0x10); | ||
| 548 | ELANTECH_INT_ATTR(reg_11, 0x11); | ||
| 549 | ELANTECH_INT_ATTR(reg_20, 0x20); | ||
| 550 | ELANTECH_INT_ATTR(reg_21, 0x21); | ||
| 551 | ELANTECH_INT_ATTR(reg_22, 0x22); | ||
| 552 | ELANTECH_INT_ATTR(reg_23, 0x23); | ||
| 553 | ELANTECH_INT_ATTR(reg_24, 0x24); | ||
| 554 | ELANTECH_INT_ATTR(reg_25, 0x25); | ||
| 555 | ELANTECH_INT_ATTR(reg_26, 0x26); | ||
| 556 | ELANTECH_INT_ATTR(debug, 0); | ||
| 557 | ELANTECH_INT_ATTR(paritycheck, 0); | ||
| 558 | |||
| 559 | static struct attribute *elantech_attrs[] = { | ||
| 560 | &psmouse_attr_reg_10.dattr.attr, | ||
| 561 | &psmouse_attr_reg_11.dattr.attr, | ||
| 562 | &psmouse_attr_reg_20.dattr.attr, | ||
| 563 | &psmouse_attr_reg_21.dattr.attr, | ||
| 564 | &psmouse_attr_reg_22.dattr.attr, | ||
| 565 | &psmouse_attr_reg_23.dattr.attr, | ||
| 566 | &psmouse_attr_reg_24.dattr.attr, | ||
| 567 | &psmouse_attr_reg_25.dattr.attr, | ||
| 568 | &psmouse_attr_reg_26.dattr.attr, | ||
| 569 | &psmouse_attr_debug.dattr.attr, | ||
| 570 | &psmouse_attr_paritycheck.dattr.attr, | ||
| 571 | NULL | ||
| 572 | }; | ||
| 573 | |||
| 574 | static struct attribute_group elantech_attr_group = { | ||
| 575 | .attrs = elantech_attrs, | ||
| 576 | }; | ||
| 577 | |||
| 578 | static bool elantech_is_signature_valid(const unsigned char *param) | ||
| 579 | { | ||
| 580 | static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10 }; | ||
| 581 | int i; | ||
| 582 | |||
| 583 | if (param[0] == 0) | ||
| 584 | return false; | ||
| 585 | |||
| 586 | if (param[1] == 0) | ||
| 587 | return true; | ||
| 588 | |||
| 589 | for (i = 0; i < ARRAY_SIZE(rates); i++) | ||
| 590 | if (param[2] == rates[i]) | ||
| 591 | return false; | ||
| 592 | |||
| 593 | return true; | ||
| 594 | } | ||
| 595 | |||
| 596 | /* | ||
| 597 | * Use magic knock to detect Elantech touchpad | ||
| 598 | */ | ||
| 599 | int elantech_detect(struct psmouse *psmouse, bool set_properties) | ||
| 600 | { | ||
| 601 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 602 | unsigned char param[3]; | ||
| 603 | |||
| 604 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
| 605 | |||
| 606 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 607 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 608 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 609 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 610 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { | ||
| 611 | pr_debug("sending Elantech magic knock failed.\n"); | ||
| 612 | return -1; | ||
| 613 | } | ||
| 614 | |||
| 615 | /* | ||
| 616 | * Report this in case there are Elantech models that use a different | ||
| 617 | * set of magic numbers | ||
| 618 | */ | ||
| 619 | if (param[0] != 0x3c || param[1] != 0x03 || param[2] != 0xc8) { | ||
| 620 | pr_debug("unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n", | ||
| 621 | param[0], param[1], param[2]); | ||
| 622 | return -1; | ||
| 623 | } | ||
| 624 | |||
| 625 | /* | ||
| 626 | * Query touchpad's firmware version and see if it reports known | ||
| 627 | * value to avoid mis-detection. Logitech mice are known to respond | ||
| 628 | * to Elantech magic knock and there might be more. | ||
| 629 | */ | ||
| 630 | if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { | ||
| 631 | pr_debug("failed to query firmware version.\n"); | ||
| 632 | return -1; | ||
| 633 | } | ||
| 634 | |||
| 635 | pr_debug("Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n", | ||
| 636 | param[0], param[1], param[2]); | ||
| 637 | |||
| 638 | if (!elantech_is_signature_valid(param)) { | ||
| 639 | if (!force_elantech) { | ||
| 640 | pr_debug("Probably not a real Elantech touchpad. Aborting.\n"); | ||
| 641 | return -1; | ||
| 642 | } | ||
| 643 | |||
| 644 | pr_debug("Probably not a real Elantech touchpad. Enabling anyway due to force_elantech.\n"); | ||
| 645 | } | ||
| 646 | |||
| 647 | if (set_properties) { | ||
| 648 | psmouse->vendor = "Elantech"; | ||
| 649 | psmouse->name = "Touchpad"; | ||
| 650 | } | ||
| 651 | |||
| 652 | return 0; | ||
| 653 | } | ||
| 654 | |||
| 655 | /* | ||
| 656 | * Clean up sysfs entries when disconnecting | ||
| 657 | */ | ||
| 658 | static void elantech_disconnect(struct psmouse *psmouse) | ||
| 659 | { | ||
| 660 | sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, | ||
| 661 | &elantech_attr_group); | ||
| 662 | kfree(psmouse->private); | ||
| 663 | psmouse->private = NULL; | ||
| 664 | } | ||
| 665 | |||
| 666 | /* | ||
| 667 | * Put the touchpad back into absolute mode when reconnecting | ||
| 668 | */ | ||
| 669 | static int elantech_reconnect(struct psmouse *psmouse) | ||
| 670 | { | ||
| 671 | if (elantech_detect(psmouse, 0)) | ||
| 672 | return -1; | ||
| 673 | |||
| 674 | if (elantech_set_absolute_mode(psmouse)) { | ||
| 675 | pr_err("failed to put touchpad back into absolute mode.\n"); | ||
| 676 | return -1; | ||
| 677 | } | ||
| 678 | |||
| 679 | return 0; | ||
| 680 | } | ||
| 681 | |||
| 682 | /* | ||
| 683 | * Initialize the touchpad and create sysfs entries | ||
| 684 | */ | ||
| 685 | int elantech_init(struct psmouse *psmouse) | ||
| 686 | { | ||
| 687 | struct elantech_data *etd; | ||
| 688 | int i, error; | ||
| 689 | unsigned char param[3]; | ||
| 690 | |||
| 691 | psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL); | ||
| 692 | if (!etd) | ||
| 693 | return -1; | ||
| 694 | |||
| 695 | etd->parity[0] = 1; | ||
| 696 | for (i = 1; i < 256; i++) | ||
| 697 | etd->parity[i] = etd->parity[i & (i - 1)] ^ 1; | ||
| 698 | |||
| 699 | /* | ||
| 700 | * Do the version query again so we can store the result | ||
| 701 | */ | ||
| 702 | if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) { | ||
| 703 | pr_err("failed to query firmware version.\n"); | ||
| 704 | goto init_fail; | ||
| 705 | } | ||
| 706 | |||
| 707 | etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2]; | ||
| 708 | |||
| 709 | /* | ||
| 710 | * Assume every version greater than this is new EeePC style | ||
| 711 | * hardware with 6 byte packets | ||
| 712 | */ | ||
| 713 | if (etd->fw_version >= 0x020030) { | ||
| 714 | etd->hw_version = 2; | ||
| 715 | /* For now show extra debug information */ | ||
| 716 | etd->debug = 1; | ||
| 717 | /* Don't know how to do parity checking for version 2 */ | ||
| 718 | etd->paritycheck = 0; | ||
| 719 | } else { | ||
| 720 | etd->hw_version = 1; | ||
| 721 | etd->paritycheck = 1; | ||
| 722 | } | ||
| 723 | |||
| 724 | pr_info("assuming hardware version %d, firmware version %d.%d.%d\n", | ||
| 725 | etd->hw_version, param[0], param[1], param[2]); | ||
| 726 | |||
| 727 | if (synaptics_send_cmd(psmouse, ETP_CAPABILITIES_QUERY, param)) { | ||
| 728 | pr_err("failed to query capabilities.\n"); | ||
| 729 | goto init_fail; | ||
| 730 | } | ||
| 731 | pr_info("Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n", | ||
| 732 | param[0], param[1], param[2]); | ||
| 733 | etd->capabilities = param[0]; | ||
| 734 | |||
| 735 | /* | ||
| 736 | * This firmware seems to suffer from misreporting coordinates when | ||
| 737 | * a touch action starts causing the mouse cursor or scrolled page | ||
| 738 | * to jump. Enable a workaround. | ||
| 739 | */ | ||
| 740 | if (etd->fw_version == 0x020022) { | ||
| 741 | pr_info("firmware version 2.0.34 detected, enabling jumpy cursor workaround\n"); | ||
| 742 | etd->jumpy_cursor = 1; | ||
| 743 | } | ||
| 744 | |||
| 745 | if (elantech_set_absolute_mode(psmouse)) { | ||
| 746 | pr_err("failed to put touchpad into absolute mode.\n"); | ||
| 747 | goto init_fail; | ||
| 748 | } | ||
| 749 | |||
| 750 | elantech_set_input_params(psmouse); | ||
| 751 | |||
| 752 | error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, | ||
| 753 | &elantech_attr_group); | ||
| 754 | if (error) { | ||
| 755 | pr_err("failed to create sysfs attributes, error: %d.\n", error); | ||
| 756 | goto init_fail; | ||
| 757 | } | ||
| 758 | |||
| 759 | psmouse->protocol_handler = elantech_process_byte; | ||
| 760 | psmouse->disconnect = elantech_disconnect; | ||
| 761 | psmouse->reconnect = elantech_reconnect; | ||
| 762 | psmouse->pktsize = etd->hw_version == 2 ? 6 : 4; | ||
| 763 | |||
| 764 | return 0; | ||
| 765 | |||
| 766 | init_fail: | ||
| 767 | kfree(etd); | ||
| 768 | return -1; | ||
| 769 | } | ||
diff --git a/usr/src/dkms_source_tree/elantech.h b/usr/src/dkms_source_tree/elantech.h new file mode 100644 index 0000000..ac57bde --- /dev/null +++ b/usr/src/dkms_source_tree/elantech.h | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* | ||
| 2 | * Elantech Touchpad driver (v6) | ||
| 3 | * | ||
| 4 | * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published | ||
| 8 | * by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * Trademarks are the property of their respective owners. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef _ELANTECH_H | ||
| 14 | #define _ELANTECH_H | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Command values for Synaptics style queries | ||
| 18 | */ | ||
| 19 | #define ETP_FW_VERSION_QUERY 0x01 | ||
| 20 | #define ETP_CAPABILITIES_QUERY 0x02 | ||
| 21 | |||
| 22 | /* | ||
| 23 | * Command values for register reading or writing | ||
| 24 | */ | ||
| 25 | #define ETP_REGISTER_READ 0x10 | ||
| 26 | #define ETP_REGISTER_WRITE 0x11 | ||
| 27 | |||
| 28 | /* | ||
| 29 | * Hardware version 2 custom PS/2 command value | ||
| 30 | */ | ||
| 31 | #define ETP_PS2_CUSTOM_COMMAND 0xf8 | ||
| 32 | |||
| 33 | /* | ||
| 34 | * Times to retry a ps2_command and millisecond delay between tries | ||
| 35 | */ | ||
| 36 | #define ETP_PS2_COMMAND_TRIES 3 | ||
| 37 | #define ETP_PS2_COMMAND_DELAY 500 | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Times to try to read back a register and millisecond delay between tries | ||
| 41 | */ | ||
| 42 | #define ETP_READ_BACK_TRIES 5 | ||
| 43 | #define ETP_READ_BACK_DELAY 2000 | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Register bitmasks for hardware version 1 | ||
| 47 | */ | ||
| 48 | #define ETP_R10_ABSOLUTE_MODE 0x04 | ||
| 49 | #define ETP_R11_4_BYTE_MODE 0x02 | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Capability bitmasks | ||
| 53 | */ | ||
| 54 | #define ETP_CAP_HAS_ROCKER 0x04 | ||
| 55 | |||
| 56 | /* | ||
| 57 | * One hard to find application note states that X axis range is 0 to 576 | ||
| 58 | * and Y axis range is 0 to 384 for harware version 1. | ||
| 59 | * Edge fuzz might be necessary because of bezel around the touchpad | ||
| 60 | */ | ||
| 61 | #define ETP_EDGE_FUZZ_V1 32 | ||
| 62 | |||
| 63 | #define ETP_XMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) | ||
| 64 | #define ETP_XMAX_V1 (576 - ETP_EDGE_FUZZ_V1) | ||
| 65 | #define ETP_YMIN_V1 ( 0 + ETP_EDGE_FUZZ_V1) | ||
| 66 | #define ETP_YMAX_V1 (384 - ETP_EDGE_FUZZ_V1) | ||
| 67 | |||
| 68 | /* | ||
| 69 | * It seems the resolution for hardware version 2 doubled. | ||
| 70 | * Hence the X and Y ranges are doubled too. | ||
| 71 | * The bezel around the pad also appears to be smaller | ||
| 72 | */ | ||
| 73 | #define ETP_EDGE_FUZZ_V2 8 | ||
| 74 | |||
| 75 | #define ETP_XMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2) | ||
| 76 | #define ETP_XMAX_V2 (1152 - ETP_EDGE_FUZZ_V2) | ||
| 77 | #define ETP_YMIN_V2 ( 0 + ETP_EDGE_FUZZ_V2) | ||
| 78 | #define ETP_YMAX_V2 ( 768 - ETP_EDGE_FUZZ_V2) | ||
| 79 | |||
| 80 | /* | ||
| 81 | * For two finger touches the coordinate of each finger gets reported | ||
| 82 | * separately but with reduced resolution. | ||
| 83 | */ | ||
| 84 | #define ETP_2FT_FUZZ 4 | ||
| 85 | |||
| 86 | #define ETP_2FT_XMIN ( 0 + ETP_2FT_FUZZ) | ||
| 87 | #define ETP_2FT_XMAX (288 - ETP_2FT_FUZZ) | ||
| 88 | #define ETP_2FT_YMIN ( 0 + ETP_2FT_FUZZ) | ||
| 89 | #define ETP_2FT_YMAX (192 - ETP_2FT_FUZZ) | ||
| 90 | |||
| 91 | struct elantech_data { | ||
| 92 | unsigned char reg_10; | ||
| 93 | unsigned char reg_11; | ||
| 94 | unsigned char reg_20; | ||
| 95 | unsigned char reg_21; | ||
| 96 | unsigned char reg_22; | ||
| 97 | unsigned char reg_23; | ||
| 98 | unsigned char reg_24; | ||
| 99 | unsigned char reg_25; | ||
| 100 | unsigned char reg_26; | ||
| 101 | unsigned char debug; | ||
| 102 | unsigned char capabilities; | ||
| 103 | unsigned char paritycheck; | ||
| 104 | unsigned char jumpy_cursor; | ||
| 105 | unsigned char hw_version; | ||
| 106 | unsigned int fw_version; | ||
| 107 | unsigned char parity[256]; | ||
| 108 | }; | ||
| 109 | |||
| 110 | #ifdef CONFIG_MOUSE_PS2_ELANTECH | ||
| 111 | int elantech_detect(struct psmouse *psmouse, bool set_properties); | ||
| 112 | int elantech_init(struct psmouse *psmouse); | ||
| 113 | #else | ||
| 114 | static inline int elantech_detect(struct psmouse *psmouse, bool set_properties) | ||
| 115 | { | ||
| 116 | return -ENOSYS; | ||
| 117 | } | ||
| 118 | static inline int elantech_init(struct psmouse *psmouse) | ||
| 119 | { | ||
| 120 | return -ENOSYS; | ||
| 121 | } | ||
| 122 | #endif /* CONFIG_MOUSE_PS2_ELANTECH */ | ||
| 123 | |||
| 124 | #endif | ||
diff --git a/usr/src/dkms_source_tree/hgpk.c b/usr/src/dkms_source_tree/hgpk.c new file mode 100644 index 0000000..1d2205b --- /dev/null +++ b/usr/src/dkms_source_tree/hgpk.c | |||
| @@ -0,0 +1,533 @@ | |||
| 1 | /* | ||
| 2 | * OLPC HGPK (XO-1) touchpad PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2006-2008 One Laptop Per Child | ||
| 5 | * Authors: | ||
| 6 | * Zephaniah E. Hull | ||
| 7 | * Andres Salomon <dilinger@debian.org> | ||
| 8 | * | ||
| 9 | * This driver is partly based on the ALPS driver, which is: | ||
| 10 | * | ||
| 11 | * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au> | ||
| 12 | * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com> | ||
| 13 | * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru> | ||
| 14 | * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> | ||
| 15 | * | ||
| 16 | * This program is free software; you can redistribute it and/or modify | ||
| 17 | * it under the terms of the GNU General Public License version 2 as | ||
| 18 | * published by the Free Software Foundation. | ||
| 19 | */ | ||
| 20 | |||
| 21 | /* | ||
| 22 | * The spec from ALPS is available from | ||
| 23 | * <http://wiki.laptop.org/go/Touch_Pad/Tablet>. It refers to this | ||
| 24 | * device as HGPK (Hybrid GS, PT, and Keymatrix). | ||
| 25 | * | ||
| 26 | * The earliest versions of the device had simultaneous reporting; that | ||
| 27 | * was removed. After that, the device used the Advanced Mode GS/PT streaming | ||
| 28 | * stuff. That turned out to be too buggy to support, so we've finally | ||
| 29 | * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad). | ||
| 30 | */ | ||
| 31 | |||
| 32 | #define DEBUG | ||
| 33 | #include <linux/slab.h> | ||
| 34 | #include <linux/input.h> | ||
| 35 | #include <linux/serio.h> | ||
| 36 | #include <linux/libps2.h> | ||
| 37 | #include <linux/delay.h> | ||
| 38 | #include <asm/olpc.h> | ||
| 39 | |||
| 40 | #include "psmouse.h" | ||
| 41 | #include "hgpk.h" | ||
| 42 | |||
| 43 | static bool tpdebug; | ||
| 44 | module_param(tpdebug, bool, 0644); | ||
| 45 | MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); | ||
| 46 | |||
| 47 | static int recalib_delta = 100; | ||
| 48 | module_param(recalib_delta, int, 0644); | ||
| 49 | MODULE_PARM_DESC(recalib_delta, | ||
| 50 | "packets containing a delta this large will cause a recalibration."); | ||
| 51 | |||
| 52 | static int jumpy_delay = 1000; | ||
| 53 | module_param(jumpy_delay, int, 0644); | ||
| 54 | MODULE_PARM_DESC(jumpy_delay, | ||
| 55 | "delay (ms) before recal after jumpiness detected"); | ||
| 56 | |||
| 57 | static int spew_delay = 1000; | ||
| 58 | module_param(spew_delay, int, 0644); | ||
| 59 | MODULE_PARM_DESC(spew_delay, | ||
| 60 | "delay (ms) before recal after packet spew detected"); | ||
| 61 | |||
| 62 | static int recal_guard_time = 2000; | ||
| 63 | module_param(recal_guard_time, int, 0644); | ||
| 64 | MODULE_PARM_DESC(recal_guard_time, | ||
| 65 | "interval (ms) during which recal will be restarted if packet received"); | ||
| 66 | |||
| 67 | static int post_interrupt_delay = 1000; | ||
| 68 | module_param(post_interrupt_delay, int, 0644); | ||
| 69 | MODULE_PARM_DESC(post_interrupt_delay, | ||
| 70 | "delay (ms) before recal after recal interrupt detected"); | ||
| 71 | |||
| 72 | /* | ||
| 73 | * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" | ||
| 74 | * above the pad and still have it send packets. This causes a jump cursor | ||
| 75 | * when one places their finger on the pad. We can probably detect the | ||
| 76 | * jump as we see a large deltas (>= 100px). In mouse mode, I've been | ||
| 77 | * unable to even come close to 100px deltas during normal usage, so I think | ||
| 78 | * this threshold is safe. If a large delta occurs, trigger a recalibration. | ||
| 79 | */ | ||
| 80 | static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) | ||
| 81 | { | ||
| 82 | struct hgpk_data *priv = psmouse->private; | ||
| 83 | |||
| 84 | if (abs(x) > recalib_delta || abs(y) > recalib_delta) { | ||
| 85 | hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", | ||
| 86 | recalib_delta, x, y); | ||
| 87 | /* My car gets forty rods to the hogshead and that's the | ||
| 88 | * way I likes it! */ | ||
| 89 | psmouse_queue_work(psmouse, &priv->recalib_wq, | ||
| 90 | msecs_to_jiffies(jumpy_delay)); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | /* | ||
| 95 | * We have no idea why this particular hardware bug occurs. The touchpad | ||
| 96 | * will randomly start spewing packets without anything touching the | ||
| 97 | * pad. This wouldn't necessarily be bad, but it's indicative of a | ||
| 98 | * severely miscalibrated pad; attempting to use the touchpad while it's | ||
| 99 | * spewing means the cursor will jump all over the place, and act "drunk". | ||
| 100 | * | ||
| 101 | * The packets that are spewed tend to all have deltas between -2 and 2, and | ||
| 102 | * the cursor will move around without really going very far. It will | ||
| 103 | * tend to end up in the same location; if we tally up the changes over | ||
| 104 | * 100 packets, we end up w/ a final delta of close to 0. This happens | ||
| 105 | * pretty regularly when the touchpad is spewing, and is pretty hard to | ||
| 106 | * manually trigger (at least for *my* fingers). So, it makes a perfect | ||
| 107 | * scheme for detecting spews. | ||
| 108 | */ | ||
| 109 | static void hgpk_spewing_hack(struct psmouse *psmouse, | ||
| 110 | int l, int r, int x, int y) | ||
| 111 | { | ||
| 112 | struct hgpk_data *priv = psmouse->private; | ||
| 113 | |||
| 114 | /* ignore button press packets; many in a row could trigger | ||
| 115 | * a false-positive! */ | ||
| 116 | if (l || r) | ||
| 117 | return; | ||
| 118 | |||
| 119 | priv->x_tally += x; | ||
| 120 | priv->y_tally += y; | ||
| 121 | |||
| 122 | if (++priv->count > 100) { | ||
| 123 | if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { | ||
| 124 | hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n", | ||
| 125 | priv->x_tally, priv->y_tally); | ||
| 126 | psmouse_queue_work(psmouse, &priv->recalib_wq, | ||
| 127 | msecs_to_jiffies(spew_delay)); | ||
| 128 | } | ||
| 129 | /* reset every 100 packets */ | ||
| 130 | priv->count = 0; | ||
| 131 | priv->x_tally = 0; | ||
| 132 | priv->y_tally = 0; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | /* | ||
| 137 | * HGPK Mouse Mode format (standard mouse format, sans middle button) | ||
| 138 | * | ||
| 139 | * byte 0: y-over x-over y-neg x-neg 1 0 swr swl | ||
| 140 | * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 | ||
| 141 | * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 | ||
| 142 | * | ||
| 143 | * swr/swl are the left/right buttons. | ||
| 144 | * x-neg/y-neg are the x and y delta negative bits | ||
| 145 | * x-over/y-over are the x and y overflow bits | ||
| 146 | */ | ||
| 147 | static int hgpk_validate_byte(unsigned char *packet) | ||
| 148 | { | ||
| 149 | return (packet[0] & 0x0C) != 0x08; | ||
| 150 | } | ||
| 151 | |||
| 152 | static void hgpk_process_packet(struct psmouse *psmouse) | ||
| 153 | { | ||
| 154 | struct input_dev *dev = psmouse->dev; | ||
| 155 | unsigned char *packet = psmouse->packet; | ||
| 156 | int x, y, left, right; | ||
| 157 | |||
| 158 | left = packet[0] & 1; | ||
| 159 | right = (packet[0] >> 1) & 1; | ||
| 160 | |||
| 161 | x = packet[1] - ((packet[0] << 4) & 0x100); | ||
| 162 | y = ((packet[0] << 3) & 0x100) - packet[2]; | ||
| 163 | |||
| 164 | hgpk_jumpy_hack(psmouse, x, y); | ||
| 165 | hgpk_spewing_hack(psmouse, left, right, x, y); | ||
| 166 | |||
| 167 | if (tpdebug) | ||
| 168 | hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); | ||
| 169 | |||
| 170 | input_report_key(dev, BTN_LEFT, left); | ||
| 171 | input_report_key(dev, BTN_RIGHT, right); | ||
| 172 | |||
| 173 | input_report_rel(dev, REL_X, x); | ||
| 174 | input_report_rel(dev, REL_Y, y); | ||
| 175 | |||
| 176 | input_sync(dev); | ||
| 177 | } | ||
| 178 | |||
| 179 | static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) | ||
| 180 | { | ||
| 181 | struct hgpk_data *priv = psmouse->private; | ||
| 182 | |||
| 183 | if (hgpk_validate_byte(psmouse->packet)) { | ||
| 184 | hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n", | ||
| 185 | __func__, psmouse->pktcnt, psmouse->packet[0], | ||
| 186 | psmouse->packet[1], psmouse->packet[2]); | ||
| 187 | return PSMOUSE_BAD_DATA; | ||
| 188 | } | ||
| 189 | |||
| 190 | if (psmouse->pktcnt >= psmouse->pktsize) { | ||
| 191 | hgpk_process_packet(psmouse); | ||
| 192 | return PSMOUSE_FULL_PACKET; | ||
| 193 | } | ||
| 194 | |||
| 195 | if (priv->recalib_window) { | ||
| 196 | if (time_before(jiffies, priv->recalib_window)) { | ||
| 197 | /* | ||
| 198 | * ugh, got a packet inside our recalibration | ||
| 199 | * window, schedule another recalibration. | ||
| 200 | */ | ||
| 201 | hgpk_dbg(psmouse, | ||
| 202 | "packet inside calibration window, " | ||
| 203 | "queueing another recalibration\n"); | ||
| 204 | psmouse_queue_work(psmouse, &priv->recalib_wq, | ||
| 205 | msecs_to_jiffies(post_interrupt_delay)); | ||
| 206 | } | ||
| 207 | priv->recalib_window = 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | return PSMOUSE_GOOD_DATA; | ||
| 211 | } | ||
| 212 | |||
| 213 | static int hgpk_force_recalibrate(struct psmouse *psmouse) | ||
| 214 | { | ||
| 215 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 216 | struct hgpk_data *priv = psmouse->private; | ||
| 217 | |||
| 218 | /* C-series touchpads added the recalibrate command */ | ||
| 219 | if (psmouse->model < HGPK_MODEL_C) | ||
| 220 | return 0; | ||
| 221 | |||
| 222 | /* we don't want to race with the irq handler, nor with resyncs */ | ||
| 223 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 224 | |||
| 225 | /* start by resetting the device */ | ||
| 226 | psmouse_reset(psmouse); | ||
| 227 | |||
| 228 | /* send the recalibrate request */ | ||
| 229 | if (ps2_command(ps2dev, NULL, 0xf5) || | ||
| 230 | ps2_command(ps2dev, NULL, 0xf5) || | ||
| 231 | ps2_command(ps2dev, NULL, 0xe6) || | ||
| 232 | ps2_command(ps2dev, NULL, 0xf5)) { | ||
| 233 | return -1; | ||
| 234 | } | ||
| 235 | |||
| 236 | /* according to ALPS, 150mS is required for recalibration */ | ||
| 237 | msleep(150); | ||
| 238 | |||
| 239 | /* XXX: If a finger is down during this delay, recalibration will | ||
| 240 | * detect capacitance incorrectly. This is a hardware bug, and | ||
| 241 | * we don't have a good way to deal with it. The 2s window stuff | ||
| 242 | * (below) is our best option for now. | ||
| 243 | */ | ||
| 244 | |||
| 245 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) | ||
| 246 | return -1; | ||
| 247 | |||
| 248 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
| 249 | |||
| 250 | /* After we recalibrate, we shouldn't get any packets for 2s. If | ||
| 251 | * we do, it's likely that someone's finger was on the touchpad. | ||
| 252 | * If someone's finger *was* on the touchpad, it's probably | ||
| 253 | * miscalibrated. So, we should schedule another recalibration | ||
| 254 | */ | ||
| 255 | priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time); | ||
| 256 | |||
| 257 | return 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* | ||
| 261 | * This kills power to the touchpad; according to ALPS, current consumption | ||
| 262 | * goes down to 50uA after running this. To turn power back on, we drive | ||
| 263 | * MS-DAT low. | ||
| 264 | */ | ||
| 265 | static int hgpk_toggle_power(struct psmouse *psmouse, int enable) | ||
| 266 | { | ||
| 267 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 268 | int timeo; | ||
| 269 | |||
| 270 | /* Added on D-series touchpads */ | ||
| 271 | if (psmouse->model < HGPK_MODEL_D) | ||
| 272 | return 0; | ||
| 273 | |||
| 274 | if (enable) { | ||
| 275 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 276 | |||
| 277 | /* | ||
| 278 | * Sending a byte will drive MS-DAT low; this will wake up | ||
| 279 | * the controller. Once we get an ACK back from it, it | ||
| 280 | * means we can continue with the touchpad re-init. ALPS | ||
| 281 | * tells us that 1s should be long enough, so set that as | ||
| 282 | * the upper bound. | ||
| 283 | */ | ||
| 284 | for (timeo = 20; timeo > 0; timeo--) { | ||
| 285 | if (!ps2_sendbyte(&psmouse->ps2dev, | ||
| 286 | PSMOUSE_CMD_DISABLE, 20)) | ||
| 287 | break; | ||
| 288 | msleep(50); | ||
| 289 | } | ||
| 290 | |||
| 291 | psmouse_reset(psmouse); | ||
| 292 | |||
| 293 | /* should be all set, enable the touchpad */ | ||
| 294 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); | ||
| 295 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
| 296 | |||
| 297 | } else { | ||
| 298 | hgpk_dbg(psmouse, "Powering off touchpad.\n"); | ||
| 299 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 300 | |||
| 301 | if (ps2_command(ps2dev, NULL, 0xec) || | ||
| 302 | ps2_command(ps2dev, NULL, 0xec) || | ||
| 303 | ps2_command(ps2dev, NULL, 0xea)) { | ||
| 304 | return -1; | ||
| 305 | } | ||
| 306 | |||
| 307 | /* probably won't see an ACK, the touchpad will be off */ | ||
| 308 | ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); | ||
| 309 | } | ||
| 310 | |||
| 311 | return 0; | ||
| 312 | } | ||
| 313 | |||
| 314 | static int hgpk_poll(struct psmouse *psmouse) | ||
| 315 | { | ||
| 316 | /* We can't poll, so always return failure. */ | ||
| 317 | return -1; | ||
| 318 | } | ||
| 319 | |||
| 320 | static int hgpk_reconnect(struct psmouse *psmouse) | ||
| 321 | { | ||
| 322 | /* During suspend/resume the ps2 rails remain powered. We don't want | ||
| 323 | * to do a reset because it's flush data out of buffers; however, | ||
| 324 | * earlier prototypes (B1) had some brokenness that required a reset. */ | ||
| 325 | if (olpc_board_at_least(olpc_board(0xb2))) | ||
| 326 | if (psmouse->ps2dev.serio->dev.power.power_state.event != | ||
| 327 | PM_EVENT_ON) | ||
| 328 | return 0; | ||
| 329 | |||
| 330 | psmouse_reset(psmouse); | ||
| 331 | |||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | |||
| 335 | static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) | ||
| 336 | { | ||
| 337 | struct hgpk_data *priv = psmouse->private; | ||
| 338 | |||
| 339 | return sprintf(buf, "%d\n", priv->powered); | ||
| 340 | } | ||
| 341 | |||
| 342 | static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, | ||
| 343 | const char *buf, size_t count) | ||
| 344 | { | ||
| 345 | struct hgpk_data *priv = psmouse->private; | ||
| 346 | unsigned long value; | ||
| 347 | int err; | ||
| 348 | |||
| 349 | err = strict_strtoul(buf, 10, &value); | ||
| 350 | if (err || value > 1) | ||
| 351 | return -EINVAL; | ||
| 352 | |||
| 353 | if (value != priv->powered) { | ||
| 354 | /* | ||
| 355 | * hgpk_toggle_power will deal w/ state so | ||
| 356 | * we're not racing w/ irq | ||
| 357 | */ | ||
| 358 | err = hgpk_toggle_power(psmouse, value); | ||
| 359 | if (!err) | ||
| 360 | priv->powered = value; | ||
| 361 | } | ||
| 362 | |||
| 363 | return err ? err : count; | ||
| 364 | } | ||
| 365 | |||
| 366 | __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, | ||
| 367 | hgpk_show_powered, hgpk_set_powered, false); | ||
| 368 | |||
| 369 | static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, | ||
| 370 | void *data, char *buf) | ||
| 371 | { | ||
| 372 | return -EINVAL; | ||
| 373 | } | ||
| 374 | |||
| 375 | static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, | ||
| 376 | const char *buf, size_t count) | ||
| 377 | { | ||
| 378 | struct hgpk_data *priv = psmouse->private; | ||
| 379 | unsigned long value; | ||
| 380 | int err; | ||
| 381 | |||
| 382 | err = strict_strtoul(buf, 10, &value); | ||
| 383 | if (err || value != 1) | ||
| 384 | return -EINVAL; | ||
| 385 | |||
| 386 | /* | ||
| 387 | * We queue work instead of doing recalibration right here | ||
| 388 | * to avoid adding locking to to hgpk_force_recalibrate() | ||
| 389 | * since workqueue provides serialization. | ||
| 390 | */ | ||
| 391 | psmouse_queue_work(psmouse, &priv->recalib_wq, 0); | ||
| 392 | return count; | ||
| 393 | } | ||
| 394 | |||
| 395 | __PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL, | ||
| 396 | hgpk_trigger_recal_show, hgpk_trigger_recal, false); | ||
| 397 | |||
| 398 | static void hgpk_disconnect(struct psmouse *psmouse) | ||
| 399 | { | ||
| 400 | struct hgpk_data *priv = psmouse->private; | ||
| 401 | |||
| 402 | device_remove_file(&psmouse->ps2dev.serio->dev, | ||
| 403 | &psmouse_attr_powered.dattr); | ||
| 404 | |||
| 405 | if (psmouse->model >= HGPK_MODEL_C) | ||
| 406 | device_remove_file(&psmouse->ps2dev.serio->dev, | ||
| 407 | &psmouse_attr_recalibrate.dattr); | ||
| 408 | |||
| 409 | psmouse_reset(psmouse); | ||
| 410 | kfree(priv); | ||
| 411 | } | ||
| 412 | |||
| 413 | static void hgpk_recalib_work(struct work_struct *work) | ||
| 414 | { | ||
| 415 | struct delayed_work *w = to_delayed_work(work); | ||
| 416 | struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); | ||
| 417 | struct psmouse *psmouse = priv->psmouse; | ||
| 418 | |||
| 419 | hgpk_dbg(psmouse, "recalibrating touchpad..\n"); | ||
| 420 | |||
| 421 | if (hgpk_force_recalibrate(psmouse)) | ||
| 422 | hgpk_err(psmouse, "recalibration failed!\n"); | ||
| 423 | } | ||
| 424 | |||
| 425 | static int hgpk_register(struct psmouse *psmouse) | ||
| 426 | { | ||
| 427 | int err; | ||
| 428 | |||
| 429 | /* register handlers */ | ||
| 430 | psmouse->protocol_handler = hgpk_process_byte; | ||
| 431 | psmouse->poll = hgpk_poll; | ||
| 432 | psmouse->disconnect = hgpk_disconnect; | ||
| 433 | psmouse->reconnect = hgpk_reconnect; | ||
| 434 | psmouse->pktsize = 3; | ||
| 435 | |||
| 436 | /* Disable the idle resync. */ | ||
| 437 | psmouse->resync_time = 0; | ||
| 438 | /* Reset after a lot of bad bytes. */ | ||
| 439 | psmouse->resetafter = 1024; | ||
| 440 | |||
| 441 | err = device_create_file(&psmouse->ps2dev.serio->dev, | ||
| 442 | &psmouse_attr_powered.dattr); | ||
| 443 | if (err) { | ||
| 444 | hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n"); | ||
| 445 | return err; | ||
| 446 | } | ||
| 447 | |||
| 448 | /* C-series touchpads added the recalibrate command */ | ||
| 449 | if (psmouse->model >= HGPK_MODEL_C) { | ||
| 450 | err = device_create_file(&psmouse->ps2dev.serio->dev, | ||
| 451 | &psmouse_attr_recalibrate.dattr); | ||
| 452 | if (err) { | ||
| 453 | hgpk_err(psmouse, | ||
| 454 | "Failed creating 'recalibrate' sysfs node\n"); | ||
| 455 | device_remove_file(&psmouse->ps2dev.serio->dev, | ||
| 456 | &psmouse_attr_powered.dattr); | ||
| 457 | return err; | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | return 0; | ||
| 462 | } | ||
| 463 | |||
| 464 | int hgpk_init(struct psmouse *psmouse) | ||
| 465 | { | ||
| 466 | struct hgpk_data *priv; | ||
| 467 | int err = -ENOMEM; | ||
| 468 | |||
| 469 | priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); | ||
| 470 | if (!priv) | ||
| 471 | goto alloc_fail; | ||
| 472 | |||
| 473 | psmouse->private = priv; | ||
| 474 | priv->psmouse = psmouse; | ||
| 475 | priv->powered = true; | ||
| 476 | INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); | ||
| 477 | |||
| 478 | err = psmouse_reset(psmouse); | ||
| 479 | if (err) | ||
| 480 | goto init_fail; | ||
| 481 | |||
| 482 | err = hgpk_register(psmouse); | ||
| 483 | if (err) | ||
| 484 | goto init_fail; | ||
| 485 | |||
| 486 | return 0; | ||
| 487 | |||
| 488 | init_fail: | ||
| 489 | kfree(priv); | ||
| 490 | alloc_fail: | ||
| 491 | return err; | ||
| 492 | } | ||
| 493 | |||
| 494 | static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) | ||
| 495 | { | ||
| 496 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 497 | unsigned char param[3]; | ||
| 498 | |||
| 499 | /* E7, E7, E7, E9 gets us a 3 byte identifier */ | ||
| 500 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 501 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 502 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 503 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { | ||
| 504 | return -EIO; | ||
| 505 | } | ||
| 506 | |||
| 507 | hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]); | ||
| 508 | |||
| 509 | /* HGPK signature: 0x67, 0x00, 0x<model> */ | ||
| 510 | if (param[0] != 0x67 || param[1] != 0x00) | ||
| 511 | return -ENODEV; | ||
| 512 | |||
| 513 | hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); | ||
| 514 | |||
| 515 | return param[2]; | ||
| 516 | } | ||
| 517 | |||
| 518 | int hgpk_detect(struct psmouse *psmouse, bool set_properties) | ||
| 519 | { | ||
| 520 | int version; | ||
| 521 | |||
| 522 | version = hgpk_get_model(psmouse); | ||
| 523 | if (version < 0) | ||
| 524 | return version; | ||
| 525 | |||
| 526 | if (set_properties) { | ||
| 527 | psmouse->vendor = "ALPS"; | ||
| 528 | psmouse->name = "HGPK"; | ||
| 529 | psmouse->model = version; | ||
| 530 | } | ||
| 531 | |||
| 532 | return 0; | ||
| 533 | } | ||
diff --git a/usr/src/dkms_source_tree/hgpk.h b/usr/src/dkms_source_tree/hgpk.h new file mode 100644 index 0000000..d61cfd3 --- /dev/null +++ b/usr/src/dkms_source_tree/hgpk.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * OLPC HGPK (XO-1) touchpad PS/2 mouse driver | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef _HGPK_H | ||
| 6 | #define _HGPK_H | ||
| 7 | |||
| 8 | enum hgpk_model_t { | ||
| 9 | HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ | ||
| 10 | HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ | ||
| 11 | HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */ | ||
| 12 | HGPK_MODEL_C = 0x3c, | ||
| 13 | HGPK_MODEL_D = 0x50, /* C1, mass production */ | ||
| 14 | }; | ||
| 15 | |||
| 16 | struct hgpk_data { | ||
| 17 | struct psmouse *psmouse; | ||
| 18 | bool powered; | ||
| 19 | int count, x_tally, y_tally; /* hardware workaround stuff */ | ||
| 20 | unsigned long recalib_window; | ||
| 21 | struct delayed_work recalib_wq; | ||
| 22 | }; | ||
| 23 | |||
| 24 | #define hgpk_dbg(psmouse, format, arg...) \ | ||
| 25 | dev_dbg(&(psmouse)->ps2dev.serio->dev, format, ## arg) | ||
| 26 | #define hgpk_err(psmouse, format, arg...) \ | ||
| 27 | dev_err(&(psmouse)->ps2dev.serio->dev, format, ## arg) | ||
| 28 | #define hgpk_info(psmouse, format, arg...) \ | ||
| 29 | dev_info(&(psmouse)->ps2dev.serio->dev, format, ## arg) | ||
| 30 | #define hgpk_warn(psmouse, format, arg...) \ | ||
| 31 | dev_warn(&(psmouse)->ps2dev.serio->dev, format, ## arg) | ||
| 32 | #define hgpk_notice(psmouse, format, arg...) \ | ||
| 33 | dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg) | ||
| 34 | |||
| 35 | #ifdef CONFIG_MOUSE_PS2_OLPC | ||
| 36 | int hgpk_detect(struct psmouse *psmouse, bool set_properties); | ||
| 37 | int hgpk_init(struct psmouse *psmouse); | ||
| 38 | #else | ||
| 39 | static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties) | ||
| 40 | { | ||
| 41 | return -ENODEV; | ||
| 42 | } | ||
| 43 | static inline int hgpk_init(struct psmouse *psmouse) | ||
| 44 | { | ||
| 45 | return -ENODEV; | ||
| 46 | } | ||
| 47 | #endif | ||
| 48 | |||
| 49 | #endif | ||
diff --git a/usr/src/dkms_source_tree/lifebook.c b/usr/src/dkms_source_tree/lifebook.c new file mode 100644 index 0000000..c31ad11 --- /dev/null +++ b/usr/src/dkms_source_tree/lifebook.c | |||
| @@ -0,0 +1,352 @@ | |||
| 1 | /* | ||
| 2 | * Fujitsu B-series Lifebook PS/2 TouchScreen driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> | ||
| 5 | * Copyright (c) 2005 Kenan Esau <kenan.esau@conan.de> | ||
| 6 | * | ||
| 7 | * TouchScreen detection, absolute mode setting and packet layout is taken from | ||
| 8 | * Harald Hoyer's description of the device. | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify it | ||
| 11 | * under the terms of the GNU General Public License version 2 as published by | ||
| 12 | * the Free Software Foundation. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/input.h> | ||
| 16 | #include <linux/serio.h> | ||
| 17 | #include <linux/libps2.h> | ||
| 18 | #include <linux/dmi.h> | ||
| 19 | #include <linux/slab.h> | ||
| 20 | |||
| 21 | #include "psmouse.h" | ||
| 22 | #include "lifebook.h" | ||
| 23 | |||
| 24 | struct lifebook_data { | ||
| 25 | struct input_dev *dev2; /* Relative device */ | ||
| 26 | char phys[32]; | ||
| 27 | }; | ||
| 28 | |||
| 29 | static bool lifebook_present; | ||
| 30 | |||
| 31 | static const char *desired_serio_phys; | ||
| 32 | |||
| 33 | static int lifebook_limit_serio3(const struct dmi_system_id *d) | ||
| 34 | { | ||
| 35 | desired_serio_phys = "isa0060/serio3"; | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | static bool lifebook_use_6byte_proto; | ||
| 40 | |||
| 41 | static int lifebook_set_6byte_proto(const struct dmi_system_id *d) | ||
| 42 | { | ||
| 43 | lifebook_use_6byte_proto = true; | ||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 47 | static const struct dmi_system_id __initconst lifebook_dmi_table[] = { | ||
| 48 | { | ||
| 49 | /* FLORA-ie 55mi */ | ||
| 50 | .matches = { | ||
| 51 | DMI_MATCH(DMI_PRODUCT_NAME, "FLORA-ie 55mi"), | ||
| 52 | }, | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | /* LifeBook B */ | ||
| 56 | .matches = { | ||
| 57 | DMI_MATCH(DMI_PRODUCT_NAME, "Lifebook B Series"), | ||
| 58 | }, | ||
| 59 | }, | ||
| 60 | { | ||
| 61 | /* LifeBook B */ | ||
| 62 | .matches = { | ||
| 63 | DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B Series"), | ||
| 64 | }, | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | /* Lifebook B */ | ||
| 68 | .matches = { | ||
| 69 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK B Series"), | ||
| 70 | }, | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | /* Lifebook B-2130 */ | ||
| 74 | .matches = { | ||
| 75 | DMI_MATCH(DMI_BOARD_NAME, "ZEPHYR"), | ||
| 76 | }, | ||
| 77 | }, | ||
| 78 | { | ||
| 79 | /* Lifebook B213x/B2150 */ | ||
| 80 | .matches = { | ||
| 81 | DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B2131/B2133/B2150"), | ||
| 82 | }, | ||
| 83 | }, | ||
| 84 | { | ||
| 85 | /* Zephyr */ | ||
| 86 | .matches = { | ||
| 87 | DMI_MATCH(DMI_PRODUCT_NAME, "ZEPHYR"), | ||
| 88 | }, | ||
| 89 | }, | ||
| 90 | { | ||
| 91 | /* Panasonic CF-18 */ | ||
| 92 | .matches = { | ||
| 93 | DMI_MATCH(DMI_PRODUCT_NAME, "CF-18"), | ||
| 94 | }, | ||
| 95 | .callback = lifebook_limit_serio3, | ||
| 96 | }, | ||
| 97 | { | ||
| 98 | /* Panasonic CF-28 */ | ||
| 99 | .matches = { | ||
| 100 | DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), | ||
| 101 | DMI_MATCH(DMI_PRODUCT_NAME, "CF-28"), | ||
| 102 | }, | ||
| 103 | .callback = lifebook_set_6byte_proto, | ||
| 104 | }, | ||
| 105 | { | ||
| 106 | /* Panasonic CF-29 */ | ||
| 107 | .matches = { | ||
| 108 | DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"), | ||
| 109 | DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"), | ||
| 110 | }, | ||
| 111 | .callback = lifebook_set_6byte_proto, | ||
| 112 | }, | ||
| 113 | { | ||
| 114 | /* Panasonic CF-72 */ | ||
| 115 | .matches = { | ||
| 116 | DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"), | ||
| 117 | }, | ||
| 118 | .callback = lifebook_set_6byte_proto, | ||
| 119 | }, | ||
| 120 | { | ||
| 121 | /* Lifebook B142 */ | ||
| 122 | .matches = { | ||
| 123 | DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"), | ||
| 124 | }, | ||
| 125 | }, | ||
| 126 | { } | ||
| 127 | }; | ||
| 128 | |||
| 129 | void __init lifebook_module_init(void) | ||
| 130 | { | ||
| 131 | lifebook_present = dmi_check_system(lifebook_dmi_table); | ||
| 132 | } | ||
| 133 | |||
| 134 | static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse) | ||
| 135 | { | ||
| 136 | struct lifebook_data *priv = psmouse->private; | ||
| 137 | struct input_dev *dev1 = psmouse->dev; | ||
| 138 | struct input_dev *dev2 = priv ? priv->dev2 : NULL; | ||
| 139 | unsigned char *packet = psmouse->packet; | ||
| 140 | bool relative_packet = packet[0] & 0x08; | ||
| 141 | |||
| 142 | if (relative_packet || !lifebook_use_6byte_proto) { | ||
| 143 | if (psmouse->pktcnt != 3) | ||
| 144 | return PSMOUSE_GOOD_DATA; | ||
| 145 | } else { | ||
| 146 | switch (psmouse->pktcnt) { | ||
| 147 | case 1: | ||
| 148 | return (packet[0] & 0xf8) == 0x00 ? | ||
| 149 | PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | ||
| 150 | case 2: | ||
| 151 | return PSMOUSE_GOOD_DATA; | ||
| 152 | case 3: | ||
| 153 | return ((packet[2] & 0x30) << 2) == (packet[2] & 0xc0) ? | ||
| 154 | PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | ||
| 155 | case 4: | ||
| 156 | return (packet[3] & 0xf8) == 0xc0 ? | ||
| 157 | PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | ||
| 158 | case 5: | ||
| 159 | return (packet[4] & 0xc0) == (packet[2] & 0xc0) ? | ||
| 160 | PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | ||
| 161 | case 6: | ||
| 162 | if (((packet[5] & 0x30) << 2) != (packet[5] & 0xc0)) | ||
| 163 | return PSMOUSE_BAD_DATA; | ||
| 164 | if ((packet[5] & 0xc0) != (packet[1] & 0xc0)) | ||
| 165 | return PSMOUSE_BAD_DATA; | ||
| 166 | break; /* report data */ | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | if (relative_packet) { | ||
| 171 | if (!dev2) | ||
| 172 | printk(KERN_WARNING "lifebook.c: got relative packet " | ||
| 173 | "but no relative device set up\n"); | ||
| 174 | } else { | ||
| 175 | if (lifebook_use_6byte_proto) { | ||
| 176 | input_report_abs(dev1, ABS_X, | ||
| 177 | ((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f)); | ||
| 178 | input_report_abs(dev1, ABS_Y, | ||
| 179 | 4096 - (((packet[4] & 0x3f) << 6) | (packet[5] & 0x3f))); | ||
| 180 | } else { | ||
| 181 | input_report_abs(dev1, ABS_X, | ||
| 182 | (packet[1] | ((packet[0] & 0x30) << 4))); | ||
| 183 | input_report_abs(dev1, ABS_Y, | ||
| 184 | 1024 - (packet[2] | ((packet[0] & 0xC0) << 2))); | ||
| 185 | } | ||
| 186 | input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04); | ||
| 187 | input_sync(dev1); | ||
| 188 | } | ||
| 189 | |||
| 190 | if (dev2) { | ||
| 191 | if (relative_packet) { | ||
| 192 | input_report_rel(dev2, REL_X, | ||
| 193 | ((packet[0] & 0x10) ? packet[1] - 256 : packet[1])); | ||
| 194 | input_report_rel(dev2, REL_Y, | ||
| 195 | -(int)((packet[0] & 0x20) ? packet[2] - 256 : packet[2])); | ||
| 196 | } | ||
| 197 | input_report_key(dev2, BTN_LEFT, packet[0] & 0x01); | ||
| 198 | input_report_key(dev2, BTN_RIGHT, packet[0] & 0x02); | ||
| 199 | input_sync(dev2); | ||
| 200 | } | ||
| 201 | |||
| 202 | return PSMOUSE_FULL_PACKET; | ||
| 203 | } | ||
| 204 | |||
| 205 | static int lifebook_absolute_mode(struct psmouse *psmouse) | ||
| 206 | { | ||
| 207 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 208 | unsigned char param; | ||
| 209 | |||
| 210 | if (psmouse_reset(psmouse)) | ||
| 211 | return -1; | ||
| 212 | |||
| 213 | /* | ||
| 214 | * Enable absolute output -- ps2_command fails always but if | ||
| 215 | * you leave this call out the touchsreen will never send | ||
| 216 | * absolute coordinates | ||
| 217 | */ | ||
| 218 | param = lifebook_use_6byte_proto ? 0x08 : 0x07; | ||
| 219 | ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); | ||
| 220 | |||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | static void lifebook_relative_mode(struct psmouse *psmouse) | ||
| 225 | { | ||
| 226 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 227 | unsigned char param = 0x06; | ||
| 228 | |||
| 229 | ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); | ||
| 230 | } | ||
| 231 | |||
| 232 | static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution) | ||
| 233 | { | ||
| 234 | static const unsigned char params[] = { 0, 1, 2, 2, 3 }; | ||
| 235 | unsigned char p; | ||
| 236 | |||
| 237 | if (resolution == 0 || resolution > 400) | ||
| 238 | resolution = 400; | ||
| 239 | |||
| 240 | p = params[resolution / 100]; | ||
| 241 | ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); | ||
| 242 | psmouse->resolution = 50 << p; | ||
| 243 | } | ||
| 244 | |||
| 245 | static void lifebook_disconnect(struct psmouse *psmouse) | ||
| 246 | { | ||
| 247 | struct lifebook_data *priv = psmouse->private; | ||
| 248 | |||
| 249 | psmouse_reset(psmouse); | ||
| 250 | if (priv) { | ||
| 251 | input_unregister_device(priv->dev2); | ||
| 252 | kfree(priv); | ||
| 253 | } | ||
| 254 | psmouse->private = NULL; | ||
| 255 | } | ||
| 256 | |||
| 257 | int lifebook_detect(struct psmouse *psmouse, bool set_properties) | ||
| 258 | { | ||
| 259 | if (!lifebook_present) | ||
| 260 | return -1; | ||
| 261 | |||
| 262 | if (desired_serio_phys && | ||
| 263 | strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) | ||
| 264 | return -1; | ||
| 265 | |||
| 266 | if (set_properties) { | ||
| 267 | psmouse->vendor = "Fujitsu"; | ||
| 268 | psmouse->name = "Lifebook TouchScreen"; | ||
| 269 | } | ||
| 270 | |||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | |||
| 274 | static int lifebook_create_relative_device(struct psmouse *psmouse) | ||
| 275 | { | ||
| 276 | struct input_dev *dev2; | ||
| 277 | struct lifebook_data *priv; | ||
| 278 | int error = -ENOMEM; | ||
| 279 | |||
| 280 | priv = kzalloc(sizeof(struct lifebook_data), GFP_KERNEL); | ||
| 281 | dev2 = input_allocate_device(); | ||
| 282 | if (!priv || !dev2) | ||
| 283 | goto err_out; | ||
| 284 | |||
| 285 | priv->dev2 = dev2; | ||
| 286 | snprintf(priv->phys, sizeof(priv->phys), | ||
| 287 | "%s/input1", psmouse->ps2dev.serio->phys); | ||
| 288 | |||
| 289 | dev2->phys = priv->phys; | ||
| 290 | dev2->name = "PS/2 Touchpad"; | ||
| 291 | dev2->id.bustype = BUS_I8042; | ||
| 292 | dev2->id.vendor = 0x0002; | ||
| 293 | dev2->id.product = PSMOUSE_LIFEBOOK; | ||
| 294 | dev2->id.version = 0x0000; | ||
| 295 | dev2->dev.parent = &psmouse->ps2dev.serio->dev; | ||
| 296 | |||
| 297 | dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); | ||
| 298 | dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); | ||
| 299 | dev2->keybit[BIT_WORD(BTN_LEFT)] = | ||
| 300 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); | ||
| 301 | |||
| 302 | error = input_register_device(priv->dev2); | ||
| 303 | if (error) | ||
| 304 | goto err_out; | ||
| 305 | |||
| 306 | psmouse->private = priv; | ||
| 307 | return 0; | ||
| 308 | |||
| 309 | err_out: | ||
| 310 | input_free_device(dev2); | ||
| 311 | kfree(priv); | ||
| 312 | return error; | ||
| 313 | } | ||
| 314 | |||
| 315 | int lifebook_init(struct psmouse *psmouse) | ||
| 316 | { | ||
| 317 | struct input_dev *dev1 = psmouse->dev; | ||
| 318 | int max_coord = lifebook_use_6byte_proto ? 4096 : 1024; | ||
| 319 | |||
| 320 | if (lifebook_absolute_mode(psmouse)) | ||
| 321 | return -1; | ||
| 322 | |||
| 323 | dev1->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); | ||
| 324 | dev1->relbit[0] = 0; | ||
| 325 | dev1->keybit[BIT_WORD(BTN_MOUSE)] = 0; | ||
| 326 | dev1->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
| 327 | input_set_abs_params(dev1, ABS_X, 0, max_coord, 0, 0); | ||
| 328 | input_set_abs_params(dev1, ABS_Y, 0, max_coord, 0, 0); | ||
| 329 | |||
| 330 | if (!desired_serio_phys) { | ||
| 331 | if (lifebook_create_relative_device(psmouse)) { | ||
| 332 | lifebook_relative_mode(psmouse); | ||
| 333 | return -1; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | psmouse->protocol_handler = lifebook_process_byte; | ||
| 338 | psmouse->set_resolution = lifebook_set_resolution; | ||
| 339 | psmouse->disconnect = lifebook_disconnect; | ||
| 340 | psmouse->reconnect = lifebook_absolute_mode; | ||
| 341 | |||
| 342 | psmouse->model = lifebook_use_6byte_proto ? 6 : 3; | ||
| 343 | |||
| 344 | /* | ||
| 345 | * Use packet size = 3 even when using 6-byte protocol because | ||
| 346 | * that's what POLL will return on Lifebooks (according to spec). | ||
| 347 | */ | ||
| 348 | psmouse->pktsize = 3; | ||
| 349 | |||
| 350 | return 0; | ||
| 351 | } | ||
| 352 | |||
diff --git a/usr/src/dkms_source_tree/lifebook.h b/usr/src/dkms_source_tree/lifebook.h new file mode 100644 index 0000000..4c4326c --- /dev/null +++ b/usr/src/dkms_source_tree/lifebook.h | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* | ||
| 2 | * Fujitsu B-series Lifebook PS/2 TouchScreen driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2005 Vojtech Pavlik | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published by | ||
| 8 | * the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef _LIFEBOOK_H | ||
| 12 | #define _LIFEBOOK_H | ||
| 13 | |||
| 14 | #ifdef CONFIG_MOUSE_PS2_LIFEBOOK | ||
| 15 | void lifebook_module_init(void); | ||
| 16 | int lifebook_detect(struct psmouse *psmouse, bool set_properties); | ||
| 17 | int lifebook_init(struct psmouse *psmouse); | ||
| 18 | #else | ||
| 19 | inline void lifebook_module_init(void) | ||
| 20 | { | ||
| 21 | } | ||
| 22 | inline int lifebook_detect(struct psmouse *psmouse, bool set_properties) | ||
| 23 | { | ||
| 24 | return -ENOSYS; | ||
| 25 | } | ||
| 26 | inline int lifebook_init(struct psmouse *psmouse) | ||
| 27 | { | ||
| 28 | return -ENOSYS; | ||
| 29 | } | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #endif | ||
diff --git a/usr/src/dkms_source_tree/logips2pp.c b/usr/src/dkms_source_tree/logips2pp.c new file mode 100644 index 0000000..c9983ae --- /dev/null +++ b/usr/src/dkms_source_tree/logips2pp.c | |||
| @@ -0,0 +1,420 @@ | |||
| 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 | static psmouse_ret_t ps2pp_process_byte(struct psmouse *psmouse) | ||
| 43 | { | ||
| 44 | struct input_dev *dev = psmouse->dev; | ||
| 45 | unsigned char *packet = psmouse->packet; | ||
| 46 | |||
| 47 | if (psmouse->pktcnt < 3) | ||
| 48 | return PSMOUSE_GOOD_DATA; | ||
| 49 | |||
| 50 | /* | ||
| 51 | * Full packet accumulated, process it | ||
| 52 | */ | ||
| 53 | |||
| 54 | if ((packet[0] & 0x48) == 0x48 && (packet[1] & 0x02) == 0x02) { | ||
| 55 | |||
| 56 | /* Logitech extended packet */ | ||
| 57 | switch ((packet[1] >> 4) | (packet[0] & 0x30)) { | ||
| 58 | |||
| 59 | case 0x0d: /* Mouse extra info */ | ||
| 60 | |||
| 61 | input_report_rel(dev, packet[2] & 0x80 ? REL_HWHEEL : REL_WHEEL, | ||
| 62 | (int) (packet[2] & 8) - (int) (packet[2] & 7)); | ||
| 63 | input_report_key(dev, BTN_SIDE, (packet[2] >> 4) & 1); | ||
| 64 | 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 | input_report_key(dev, BTN_SIDE, (packet[2]) & 1); | ||
| 71 | input_report_key(dev, BTN_EXTRA, (packet[2] >> 1) & 1); | ||
| 72 | input_report_key(dev, BTN_BACK, (packet[2] >> 3) & 1); | ||
| 73 | input_report_key(dev, BTN_FORWARD, (packet[2] >> 4) & 1); | ||
| 74 | input_report_key(dev, BTN_TASK, (packet[2] >> 2) & 1); | ||
| 75 | |||
| 76 | break; | ||
| 77 | |||
| 78 | case 0x0f: /* TouchPad extra info */ | ||
| 79 | |||
| 80 | input_report_rel(dev, packet[2] & 0x08 ? REL_HWHEEL : REL_WHEEL, | ||
| 81 | (int) ((packet[2] >> 4) & 8) - (int) ((packet[2] >> 4) & 7)); | ||
| 82 | packet[0] = packet[2] | 0x08; | ||
| 83 | break; | ||
| 84 | |||
| 85 | #ifdef DEBUG | ||
| 86 | default: | ||
| 87 | printk(KERN_WARNING "psmouse.c: Received PS2++ packet #%x, but don't know how to handle.\n", | ||
| 88 | (packet[1] >> 4) | (packet[0] & 0x30)); | ||
| 89 | #endif | ||
| 90 | } | ||
| 91 | } else { | ||
| 92 | /* Standard PS/2 motion data */ | ||
| 93 | input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); | ||
| 94 | input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); | ||
| 95 | } | ||
| 96 | |||
| 97 | input_report_key(dev, BTN_LEFT, packet[0] & 1); | ||
| 98 | input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); | ||
| 99 | input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); | ||
| 100 | |||
| 101 | input_sync(dev); | ||
| 102 | |||
| 103 | 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 | static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned char command) | ||
| 115 | { | ||
| 116 | if (psmouse_sliced_command(psmouse, command)) | ||
| 117 | return -1; | ||
| 118 | |||
| 119 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_POLL | 0x0300)) | ||
| 120 | return -1; | ||
| 121 | |||
| 122 | 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 | static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll) | ||
| 134 | { | ||
| 135 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 136 | unsigned char param[4]; | ||
| 137 | |||
| 138 | ps2pp_cmd(psmouse, param, 0x32); | ||
| 139 | |||
| 140 | param[0] = 0; | ||
| 141 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 142 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 143 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 144 | |||
| 145 | param[0] = smartscroll; | ||
| 146 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 147 | } | ||
| 148 | |||
| 149 | static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, | ||
| 150 | void *data, char *buf) | ||
| 151 | { | ||
| 152 | return sprintf(buf, "%d\n", psmouse->smartscroll); | ||
| 153 | } | ||
| 154 | |||
| 155 | static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, | ||
| 156 | const char *buf, size_t count) | ||
| 157 | { | ||
| 158 | unsigned long value; | ||
| 159 | |||
| 160 | if (strict_strtoul(buf, 10, &value) || value > 1) | ||
| 161 | return -EINVAL; | ||
| 162 | |||
| 163 | ps2pp_set_smartscroll(psmouse, value); | ||
| 164 | psmouse->smartscroll = value; | ||
| 165 | return count; | ||
| 166 | } | ||
| 167 | |||
| 168 | PSMOUSE_DEFINE_ATTR(smartscroll, S_IWUSR | S_IRUGO, NULL, | ||
| 169 | ps2pp_attr_show_smartscroll, ps2pp_attr_set_smartscroll); | ||
| 170 | |||
| 171 | /* | ||
| 172 | * Support 800 dpi resolution _only_ if the user wants it (there are good | ||
| 173 | * reasons to not use it even if the mouse supports it, and of course there are | ||
| 174 | * also good reasons to use it, let the user decide). | ||
| 175 | */ | ||
| 176 | |||
| 177 | static void ps2pp_set_resolution(struct psmouse *psmouse, unsigned int resolution) | ||
| 178 | { | ||
| 179 | if (resolution > 400) { | ||
| 180 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 181 | unsigned char param = 3; | ||
| 182 | |||
| 183 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 184 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 185 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 186 | ps2_command(ps2dev, ¶m, PSMOUSE_CMD_SETRES); | ||
| 187 | psmouse->resolution = 800; | ||
| 188 | } else | ||
| 189 | psmouse_set_resolution(psmouse, resolution); | ||
| 190 | } | ||
| 191 | |||
| 192 | static void ps2pp_disconnect(struct psmouse *psmouse) | ||
| 193 | { | ||
| 194 | device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr); | ||
| 195 | } | ||
| 196 | |||
| 197 | static const struct ps2pp_info *get_model_info(unsigned char model) | ||
| 198 | { | ||
| 199 | static const struct ps2pp_info ps2pp_list[] = { | ||
| 200 | { 1, 0, 0 }, /* Simple 2-button mouse */ | ||
| 201 | { 12, 0, PS2PP_SIDE_BTN}, | ||
| 202 | { 13, 0, 0 }, | ||
| 203 | { 15, PS2PP_KIND_MX, /* MX1000 */ | ||
| 204 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
| 205 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, | ||
| 206 | { 40, 0, PS2PP_SIDE_BTN }, | ||
| 207 | { 41, 0, PS2PP_SIDE_BTN }, | ||
| 208 | { 42, 0, PS2PP_SIDE_BTN }, | ||
| 209 | { 43, 0, PS2PP_SIDE_BTN }, | ||
| 210 | { 50, 0, 0 }, | ||
| 211 | { 51, 0, 0 }, | ||
| 212 | { 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, | ||
| 213 | { 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 214 | { 56, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */ | ||
| 215 | { 61, PS2PP_KIND_MX, /* MX700 */ | ||
| 216 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
| 217 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, | ||
| 218 | { 66, PS2PP_KIND_MX, /* MX3100 reciver */ | ||
| 219 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
| 220 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN | PS2PP_HWHEEL }, | ||
| 221 | { 72, PS2PP_KIND_TRACKMAN, 0 }, /* T-CH11: TrackMan Marble */ | ||
| 222 | { 73, PS2PP_KIND_TRACKMAN, PS2PP_SIDE_BTN }, /* TrackMan FX */ | ||
| 223 | { 75, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 224 | { 76, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 225 | { 79, PS2PP_KIND_TRACKMAN, PS2PP_WHEEL }, /* TrackMan with wheel */ | ||
| 226 | { 80, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, | ||
| 227 | { 81, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 228 | { 83, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 229 | { 85, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 230 | { 86, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 231 | { 87, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 232 | { 88, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 233 | { 96, 0, 0 }, | ||
| 234 | { 97, PS2PP_KIND_TP3, PS2PP_WHEEL | PS2PP_HWHEEL }, | ||
| 235 | { 99, PS2PP_KIND_WHEEL, PS2PP_WHEEL }, | ||
| 236 | { 100, PS2PP_KIND_MX, /* MX510 */ | ||
| 237 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
| 238 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, | ||
| 239 | { 111, PS2PP_KIND_MX, PS2PP_WHEEL | PS2PP_SIDE_BTN }, /* MX300 reports task button as side */ | ||
| 240 | { 112, PS2PP_KIND_MX, /* MX500 */ | ||
| 241 | PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN | | ||
| 242 | PS2PP_EXTRA_BTN | PS2PP_NAV_BTN }, | ||
| 243 | { 114, PS2PP_KIND_MX, /* MX310 */ | ||
| 244 | PS2PP_WHEEL | PS2PP_SIDE_BTN | | ||
| 245 | PS2PP_TASK_BTN | PS2PP_EXTRA_BTN } | ||
| 246 | }; | ||
| 247 | int i; | ||
| 248 | |||
| 249 | for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++) | ||
| 250 | if (model == ps2pp_list[i].model) | ||
| 251 | return &ps2pp_list[i]; | ||
| 252 | |||
| 253 | return NULL; | ||
| 254 | } | ||
| 255 | |||
| 256 | /* | ||
| 257 | * Set up input device's properties based on the detected mouse model. | ||
| 258 | */ | ||
| 259 | |||
| 260 | static void ps2pp_set_model_properties(struct psmouse *psmouse, | ||
| 261 | const struct ps2pp_info *model_info, | ||
| 262 | bool using_ps2pp) | ||
| 263 | { | ||
| 264 | struct input_dev *input_dev = psmouse->dev; | ||
| 265 | |||
| 266 | if (model_info->features & PS2PP_SIDE_BTN) | ||
| 267 | __set_bit(BTN_SIDE, input_dev->keybit); | ||
| 268 | |||
| 269 | if (model_info->features & PS2PP_EXTRA_BTN) | ||
| 270 | __set_bit(BTN_EXTRA, input_dev->keybit); | ||
| 271 | |||
| 272 | if (model_info->features & PS2PP_TASK_BTN) | ||
| 273 | __set_bit(BTN_TASK, input_dev->keybit); | ||
| 274 | |||
| 275 | if (model_info->features & PS2PP_NAV_BTN) { | ||
| 276 | __set_bit(BTN_FORWARD, input_dev->keybit); | ||
| 277 | __set_bit(BTN_BACK, input_dev->keybit); | ||
| 278 | } | ||
| 279 | |||
| 280 | if (model_info->features & PS2PP_WHEEL) | ||
| 281 | __set_bit(REL_WHEEL, input_dev->relbit); | ||
| 282 | |||
| 283 | if (model_info->features & PS2PP_HWHEEL) | ||
| 284 | __set_bit(REL_HWHEEL, input_dev->relbit); | ||
| 285 | |||
| 286 | switch (model_info->kind) { | ||
| 287 | |||
| 288 | case PS2PP_KIND_WHEEL: | ||
| 289 | psmouse->name = "Wheel Mouse"; | ||
| 290 | break; | ||
| 291 | |||
| 292 | case PS2PP_KIND_MX: | ||
| 293 | psmouse->name = "MX Mouse"; | ||
| 294 | break; | ||
| 295 | |||
| 296 | case PS2PP_KIND_TP3: | ||
| 297 | psmouse->name = "TouchPad 3"; | ||
| 298 | break; | ||
| 299 | |||
| 300 | case PS2PP_KIND_TRACKMAN: | ||
| 301 | psmouse->name = "TrackMan"; | ||
| 302 | break; | ||
| 303 | |||
| 304 | default: | ||
| 305 | /* | ||
| 306 | * Set name to "Mouse" only when using PS2++, | ||
| 307 | * otherwise let other protocols define suitable | ||
| 308 | * name | ||
| 309 | */ | ||
| 310 | if (using_ps2pp) | ||
| 311 | psmouse->name = "Mouse"; | ||
| 312 | break; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | |||
| 317 | /* | ||
| 318 | * Logitech magic init. Detect whether the mouse is a Logitech one | ||
| 319 | * and its exact model and try turning on extended protocol for ones | ||
| 320 | * that support it. | ||
| 321 | */ | ||
| 322 | |||
| 323 | int ps2pp_init(struct psmouse *psmouse, bool set_properties) | ||
| 324 | { | ||
| 325 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 326 | unsigned char param[4]; | ||
| 327 | unsigned char model, buttons; | ||
| 328 | const struct ps2pp_info *model_info; | ||
| 329 | bool use_ps2pp = false; | ||
| 330 | int error; | ||
| 331 | |||
| 332 | param[0] = 0; | ||
| 333 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 334 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 335 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 336 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 337 | param[1] = 0; | ||
| 338 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | ||
| 339 | |||
| 340 | model = ((param[0] >> 4) & 0x07) | ((param[0] << 3) & 0x78); | ||
| 341 | buttons = param[1]; | ||
| 342 | |||
| 343 | if (!model || !buttons) | ||
| 344 | return -1; | ||
| 345 | |||
| 346 | model_info = get_model_info(model); | ||
| 347 | if (model_info) { | ||
| 348 | |||
| 349 | /* | ||
| 350 | * Do Logitech PS2++ / PS2T++ magic init. | ||
| 351 | */ | ||
| 352 | if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */ | ||
| 353 | |||
| 354 | /* Unprotect RAM */ | ||
| 355 | param[0] = 0x11; param[1] = 0x04; param[2] = 0x68; | ||
| 356 | ps2_command(ps2dev, param, 0x30d1); | ||
| 357 | /* Enable features */ | ||
| 358 | param[0] = 0x11; param[1] = 0x05; param[2] = 0x0b; | ||
| 359 | ps2_command(ps2dev, param, 0x30d1); | ||
| 360 | /* Enable PS2++ */ | ||
| 361 | param[0] = 0x11; param[1] = 0x09; param[2] = 0xc3; | ||
| 362 | ps2_command(ps2dev, param, 0x30d1); | ||
| 363 | |||
| 364 | param[0] = 0; | ||
| 365 | if (!ps2_command(ps2dev, param, 0x13d1) && | ||
| 366 | param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) { | ||
| 367 | use_ps2pp = true; | ||
| 368 | } | ||
| 369 | |||
| 370 | } else { | ||
| 371 | |||
| 372 | param[0] = param[1] = param[2] = 0; | ||
| 373 | ps2pp_cmd(psmouse, param, 0x39); /* Magic knock */ | ||
| 374 | ps2pp_cmd(psmouse, param, 0xDB); | ||
| 375 | |||
| 376 | if ((param[0] & 0x78) == 0x48 && | ||
| 377 | (param[1] & 0xf3) == 0xc2 && | ||
| 378 | (param[2] & 0x03) == ((param[1] >> 2) & 3)) { | ||
| 379 | ps2pp_set_smartscroll(psmouse, false); | ||
| 380 | use_ps2pp = true; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | } else { | ||
| 385 | printk(KERN_WARNING "logips2pp: Detected unknown logitech mouse model %d\n", model); | ||
| 386 | } | ||
| 387 | |||
| 388 | if (set_properties) { | ||
| 389 | psmouse->vendor = "Logitech"; | ||
| 390 | psmouse->model = model; | ||
| 391 | |||
| 392 | if (use_ps2pp) { | ||
| 393 | psmouse->protocol_handler = ps2pp_process_byte; | ||
| 394 | psmouse->pktsize = 3; | ||
| 395 | |||
| 396 | if (model_info->kind != PS2PP_KIND_TP3) { | ||
| 397 | psmouse->set_resolution = ps2pp_set_resolution; | ||
| 398 | psmouse->disconnect = ps2pp_disconnect; | ||
| 399 | |||
| 400 | error = device_create_file(&psmouse->ps2dev.serio->dev, | ||
| 401 | &psmouse_attr_smartscroll.dattr); | ||
| 402 | if (error) { | ||
| 403 | printk(KERN_ERR | ||
| 404 | "logips2pp.c: failed to create smartscroll " | ||
| 405 | "sysfs attribute, error: %d\n", error); | ||
| 406 | return -1; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | if (buttons >= 3) | ||
| 412 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 413 | |||
| 414 | if (model_info) | ||
| 415 | ps2pp_set_model_properties(psmouse, model_info, use_ps2pp); | ||
| 416 | } | ||
| 417 | |||
| 418 | return use_ps2pp ? 0 : -1; | ||
| 419 | } | ||
| 420 | |||
diff --git a/usr/src/dkms_source_tree/logips2pp.h b/usr/src/dkms_source_tree/logips2pp.h new file mode 100644 index 0000000..0c186f0 --- /dev/null +++ b/usr/src/dkms_source_tree/logips2pp.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* | ||
| 2 | * Logitech PS/2++ mouse driver header | ||
| 3 | * | ||
| 4 | * Copyright (c) 2003 Vojtech Pavlik <vojtech@suse.cz> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published by | ||
| 8 | * the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef _LOGIPS2PP_H | ||
| 12 | #define _LOGIPS2PP_H | ||
| 13 | |||
| 14 | #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP | ||
| 15 | int ps2pp_init(struct psmouse *psmouse, bool set_properties); | ||
| 16 | #else | ||
| 17 | inline int ps2pp_init(struct psmouse *psmouse, bool set_properties) | ||
| 18 | { | ||
| 19 | return -ENOSYS; | ||
| 20 | } | ||
| 21 | #endif /* CONFIG_MOUSE_PS2_LOGIPS2PP */ | ||
| 22 | |||
| 23 | #endif | ||
diff --git a/usr/src/dkms_source_tree/psmouse-base.c b/usr/src/dkms_source_tree/psmouse-base.c new file mode 100644 index 0000000..979c502 --- /dev/null +++ b/usr/src/dkms_source_tree/psmouse-base.c | |||
| @@ -0,0 +1,1733 @@ | |||
| 1 | /* | ||
| 2 | * PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 1999-2002 Vojtech Pavlik | ||
| 5 | * Copyright (c) 2003-2004 Dmitry Torokhov | ||
| 6 | */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * This program is free software; you can redistribute it and/or modify it | ||
| 10 | * under the terms of the GNU General Public License version 2 as published by | ||
| 11 | * the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/delay.h> | ||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/interrupt.h> | ||
| 18 | #include <linux/input.h> | ||
| 19 | #include <linux/serio.h> | ||
| 20 | #include <linux/init.h> | ||
| 21 | #include <linux/libps2.h> | ||
| 22 | #include <linux/mutex.h> | ||
| 23 | |||
| 24 | #include "psmouse.h" | ||
| 25 | #include "synaptics.h" | ||
| 26 | #include "logips2pp.h" | ||
| 27 | #include "alps.h" | ||
| 28 | #include "hgpk.h" | ||
| 29 | #include "lifebook.h" | ||
| 30 | #include "trackpoint.h" | ||
| 31 | #include "touchkit_ps2.h" | ||
| 32 | #include "elantech.h" | ||
| 33 | #include "sentelic.h" | ||
| 34 | |||
| 35 | #define DRIVER_DESC "PS/2 mouse driver" | ||
| 36 | |||
| 37 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); | ||
| 38 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 39 | MODULE_LICENSE("GPL"); | ||
| 40 | |||
| 41 | static unsigned int psmouse_max_proto = PSMOUSE_AUTO; | ||
| 42 | static int psmouse_set_maxproto(const char *val, struct kernel_param *kp); | ||
| 43 | static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp); | ||
| 44 | #define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) | ||
| 45 | #define param_set_proto_abbrev psmouse_set_maxproto | ||
| 46 | #define param_get_proto_abbrev psmouse_get_maxproto | ||
| 47 | module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644); | ||
| 48 | MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches."); | ||
| 49 | |||
| 50 | static unsigned int psmouse_resolution = 200; | ||
| 51 | module_param_named(resolution, psmouse_resolution, uint, 0644); | ||
| 52 | MODULE_PARM_DESC(resolution, "Resolution, in dpi."); | ||
| 53 | |||
| 54 | static unsigned int psmouse_rate = 100; | ||
| 55 | module_param_named(rate, psmouse_rate, uint, 0644); | ||
| 56 | MODULE_PARM_DESC(rate, "Report rate, in reports per second."); | ||
| 57 | |||
| 58 | static unsigned int psmouse_smartscroll = 1; | ||
| 59 | module_param_named(smartscroll, psmouse_smartscroll, bool, 0644); | ||
| 60 | MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled."); | ||
| 61 | |||
| 62 | static unsigned int psmouse_resetafter = 5; | ||
| 63 | module_param_named(resetafter, psmouse_resetafter, uint, 0644); | ||
| 64 | MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never)."); | ||
| 65 | |||
| 66 | static unsigned int psmouse_resync_time; | ||
| 67 | module_param_named(resync_time, psmouse_resync_time, uint, 0644); | ||
| 68 | MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never)."); | ||
| 69 | |||
| 70 | PSMOUSE_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO, | ||
| 71 | NULL, | ||
| 72 | psmouse_attr_show_protocol, psmouse_attr_set_protocol); | ||
| 73 | PSMOUSE_DEFINE_ATTR(rate, S_IWUSR | S_IRUGO, | ||
| 74 | (void *) offsetof(struct psmouse, rate), | ||
| 75 | psmouse_show_int_attr, psmouse_attr_set_rate); | ||
| 76 | PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO, | ||
| 77 | (void *) offsetof(struct psmouse, resolution), | ||
| 78 | psmouse_show_int_attr, psmouse_attr_set_resolution); | ||
| 79 | PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO, | ||
| 80 | (void *) offsetof(struct psmouse, resetafter), | ||
| 81 | psmouse_show_int_attr, psmouse_set_int_attr); | ||
| 82 | PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO, | ||
| 83 | (void *) offsetof(struct psmouse, resync_time), | ||
| 84 | psmouse_show_int_attr, psmouse_set_int_attr); | ||
| 85 | |||
| 86 | static struct attribute *psmouse_attributes[] = { | ||
| 87 | &psmouse_attr_protocol.dattr.attr, | ||
| 88 | &psmouse_attr_rate.dattr.attr, | ||
| 89 | &psmouse_attr_resolution.dattr.attr, | ||
| 90 | &psmouse_attr_resetafter.dattr.attr, | ||
| 91 | &psmouse_attr_resync_time.dattr.attr, | ||
| 92 | NULL | ||
| 93 | }; | ||
| 94 | |||
| 95 | static struct attribute_group psmouse_attribute_group = { | ||
| 96 | .attrs = psmouse_attributes, | ||
| 97 | }; | ||
| 98 | |||
| 99 | /* | ||
| 100 | * psmouse_mutex protects all operations changing state of mouse | ||
| 101 | * (connecting, disconnecting, changing rate or resolution via | ||
| 102 | * sysfs). We could use a per-device semaphore but since there | ||
| 103 | * rarely more than one PS/2 mouse connected and since semaphore | ||
| 104 | * is taken in "slow" paths it is not worth it. | ||
| 105 | */ | ||
| 106 | static DEFINE_MUTEX(psmouse_mutex); | ||
| 107 | |||
| 108 | static struct workqueue_struct *kpsmoused_wq; | ||
| 109 | |||
| 110 | struct psmouse_protocol { | ||
| 111 | enum psmouse_type type; | ||
| 112 | bool maxproto; | ||
| 113 | bool ignore_parity; /* Protocol should ignore parity errors from KBC */ | ||
| 114 | const char *name; | ||
| 115 | const char *alias; | ||
| 116 | int (*detect)(struct psmouse *, bool); | ||
| 117 | int (*init)(struct psmouse *); | ||
| 118 | }; | ||
| 119 | |||
| 120 | /* | ||
| 121 | * psmouse_process_byte() analyzes the PS/2 data stream and reports | ||
| 122 | * relevant events to the input module once full packet has arrived. | ||
| 123 | */ | ||
| 124 | |||
| 125 | static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) | ||
| 126 | { | ||
| 127 | struct input_dev *dev = psmouse->dev; | ||
| 128 | unsigned char *packet = psmouse->packet; | ||
| 129 | |||
| 130 | if (psmouse->pktcnt < psmouse->pktsize) | ||
| 131 | return PSMOUSE_GOOD_DATA; | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Full packet accumulated, process it | ||
| 135 | */ | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Scroll wheel on IntelliMice, scroll buttons on NetMice | ||
| 139 | */ | ||
| 140 | |||
| 141 | if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS) | ||
| 142 | input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); | ||
| 143 | |||
| 144 | /* | ||
| 145 | * Scroll wheel and buttons on IntelliMouse Explorer | ||
| 146 | */ | ||
| 147 | |||
| 148 | if (psmouse->type == PSMOUSE_IMEX) { | ||
| 149 | switch (packet[3] & 0xC0) { | ||
| 150 | case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */ | ||
| 151 | input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); | ||
| 152 | break; | ||
| 153 | case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ | ||
| 154 | input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); | ||
| 155 | break; | ||
| 156 | case 0x00: | ||
| 157 | case 0xC0: | ||
| 158 | input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); | ||
| 159 | input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); | ||
| 160 | input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | /* | ||
| 166 | * Extra buttons on Genius NewNet 3D | ||
| 167 | */ | ||
| 168 | |||
| 169 | if (psmouse->type == PSMOUSE_GENPS) { | ||
| 170 | input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); | ||
| 171 | input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); | ||
| 172 | } | ||
| 173 | |||
| 174 | /* | ||
| 175 | * Extra button on ThinkingMouse | ||
| 176 | */ | ||
| 177 | if (psmouse->type == PSMOUSE_THINKPS) { | ||
| 178 | input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); | ||
| 179 | /* Without this bit of weirdness moving up gives wildly high Y changes. */ | ||
| 180 | packet[1] |= (packet[0] & 0x40) << 1; | ||
| 181 | } | ||
| 182 | |||
| 183 | /* | ||
| 184 | * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first | ||
| 185 | * byte. | ||
| 186 | */ | ||
| 187 | if (psmouse->type == PSMOUSE_CORTRON) { | ||
| 188 | input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1); | ||
| 189 | packet[0] |= 0x08; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* | ||
| 193 | * Generic PS/2 Mouse | ||
| 194 | */ | ||
| 195 | |||
| 196 | input_report_key(dev, BTN_LEFT, packet[0] & 1); | ||
| 197 | input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); | ||
| 198 | input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); | ||
| 199 | |||
| 200 | input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); | ||
| 201 | input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); | ||
| 202 | |||
| 203 | input_sync(dev); | ||
| 204 | |||
| 205 | return PSMOUSE_FULL_PACKET; | ||
| 206 | } | ||
| 207 | |||
| 208 | void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, | ||
| 209 | unsigned long delay) | ||
| 210 | { | ||
| 211 | queue_delayed_work(kpsmoused_wq, work, delay); | ||
| 212 | } | ||
| 213 | |||
| 214 | /* | ||
| 215 | * __psmouse_set_state() sets new psmouse state and resets all flags. | ||
| 216 | */ | ||
| 217 | |||
| 218 | static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) | ||
| 219 | { | ||
| 220 | psmouse->state = new_state; | ||
| 221 | psmouse->pktcnt = psmouse->out_of_sync_cnt = 0; | ||
| 222 | psmouse->ps2dev.flags = 0; | ||
| 223 | psmouse->last = jiffies; | ||
| 224 | } | ||
| 225 | |||
| 226 | |||
| 227 | /* | ||
| 228 | * psmouse_set_state() sets new psmouse state and resets all flags and | ||
| 229 | * counters while holding serio lock so fighting with interrupt handler | ||
| 230 | * is not a concern. | ||
| 231 | */ | ||
| 232 | |||
| 233 | void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state) | ||
| 234 | { | ||
| 235 | serio_pause_rx(psmouse->ps2dev.serio); | ||
| 236 | __psmouse_set_state(psmouse, new_state); | ||
| 237 | serio_continue_rx(psmouse->ps2dev.serio); | ||
| 238 | } | ||
| 239 | |||
| 240 | /* | ||
| 241 | * psmouse_handle_byte() processes one byte of the input data stream | ||
| 242 | * by calling corresponding protocol handler. | ||
| 243 | */ | ||
| 244 | |||
| 245 | static int psmouse_handle_byte(struct psmouse *psmouse) | ||
| 246 | { | ||
| 247 | psmouse_ret_t rc = psmouse->protocol_handler(psmouse); | ||
| 248 | |||
| 249 | switch (rc) { | ||
| 250 | case PSMOUSE_BAD_DATA: | ||
| 251 | if (psmouse->state == PSMOUSE_ACTIVATED) { | ||
| 252 | printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n", | ||
| 253 | psmouse->name, psmouse->phys, psmouse->pktcnt); | ||
| 254 | if (++psmouse->out_of_sync_cnt == psmouse->resetafter) { | ||
| 255 | __psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 256 | printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n"); | ||
| 257 | serio_reconnect(psmouse->ps2dev.serio); | ||
| 258 | return -1; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | psmouse->pktcnt = 0; | ||
| 262 | break; | ||
| 263 | |||
| 264 | case PSMOUSE_FULL_PACKET: | ||
| 265 | psmouse->pktcnt = 0; | ||
| 266 | if (psmouse->out_of_sync_cnt) { | ||
| 267 | psmouse->out_of_sync_cnt = 0; | ||
| 268 | printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n", | ||
| 269 | psmouse->name, psmouse->phys); | ||
| 270 | } | ||
| 271 | break; | ||
| 272 | |||
| 273 | case PSMOUSE_GOOD_DATA: | ||
| 274 | break; | ||
| 275 | } | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | /* | ||
| 280 | * psmouse_interrupt() handles incoming characters, either passing them | ||
| 281 | * for normal processing or gathering them as command response. | ||
| 282 | */ | ||
| 283 | |||
| 284 | static irqreturn_t psmouse_interrupt(struct serio *serio, | ||
| 285 | unsigned char data, unsigned int flags) | ||
| 286 | { | ||
| 287 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
| 288 | |||
| 289 | if (psmouse->state == PSMOUSE_IGNORE) | ||
| 290 | goto out; | ||
| 291 | |||
| 292 | if (unlikely((flags & SERIO_TIMEOUT) || | ||
| 293 | ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) { | ||
| 294 | |||
| 295 | if (psmouse->state == PSMOUSE_ACTIVATED) | ||
| 296 | printk(KERN_WARNING "psmouse.c: bad data from KBC -%s%s\n", | ||
| 297 | flags & SERIO_TIMEOUT ? " timeout" : "", | ||
| 298 | flags & SERIO_PARITY ? " bad parity" : ""); | ||
| 299 | ps2_cmd_aborted(&psmouse->ps2dev); | ||
| 300 | goto out; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK)) | ||
| 304 | if (ps2_handle_ack(&psmouse->ps2dev, data)) | ||
| 305 | goto out; | ||
| 306 | |||
| 307 | if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD)) | ||
| 308 | if (ps2_handle_response(&psmouse->ps2dev, data)) | ||
| 309 | goto out; | ||
| 310 | |||
| 311 | if (psmouse->state <= PSMOUSE_RESYNCING) | ||
| 312 | goto out; | ||
| 313 | |||
| 314 | if (psmouse->state == PSMOUSE_ACTIVATED && | ||
| 315 | psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { | ||
| 316 | printk(KERN_INFO "psmouse.c: %s at %s lost synchronization, throwing %d bytes away.\n", | ||
| 317 | psmouse->name, psmouse->phys, psmouse->pktcnt); | ||
| 318 | psmouse->badbyte = psmouse->packet[0]; | ||
| 319 | __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); | ||
| 320 | psmouse_queue_work(psmouse, &psmouse->resync_work, 0); | ||
| 321 | goto out; | ||
| 322 | } | ||
| 323 | |||
| 324 | psmouse->packet[psmouse->pktcnt++] = data; | ||
| 325 | /* | ||
| 326 | * Check if this is a new device announcement (0xAA 0x00) | ||
| 327 | */ | ||
| 328 | if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { | ||
| 329 | if (psmouse->pktcnt == 1) { | ||
| 330 | psmouse->last = jiffies; | ||
| 331 | goto out; | ||
| 332 | } | ||
| 333 | |||
| 334 | if (psmouse->packet[1] == PSMOUSE_RET_ID || | ||
| 335 | (psmouse->type == PSMOUSE_HGPK && | ||
| 336 | psmouse->packet[1] == PSMOUSE_RET_BAT)) { | ||
| 337 | __psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 338 | serio_reconnect(serio); | ||
| 339 | goto out; | ||
| 340 | } | ||
| 341 | /* | ||
| 342 | * Not a new device, try processing first byte normally | ||
| 343 | */ | ||
| 344 | psmouse->pktcnt = 1; | ||
| 345 | if (psmouse_handle_byte(psmouse)) | ||
| 346 | goto out; | ||
| 347 | |||
| 348 | psmouse->packet[psmouse->pktcnt++] = data; | ||
| 349 | } | ||
| 350 | |||
| 351 | /* | ||
| 352 | * See if we need to force resync because mouse was idle for too long | ||
| 353 | */ | ||
| 354 | if (psmouse->state == PSMOUSE_ACTIVATED && | ||
| 355 | psmouse->pktcnt == 1 && psmouse->resync_time && | ||
| 356 | time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) { | ||
| 357 | psmouse->badbyte = psmouse->packet[0]; | ||
| 358 | __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); | ||
| 359 | psmouse_queue_work(psmouse, &psmouse->resync_work, 0); | ||
| 360 | goto out; | ||
| 361 | } | ||
| 362 | |||
| 363 | psmouse->last = jiffies; | ||
| 364 | psmouse_handle_byte(psmouse); | ||
| 365 | |||
| 366 | out: | ||
| 367 | return IRQ_HANDLED; | ||
| 368 | } | ||
| 369 | |||
| 370 | |||
| 371 | /* | ||
| 372 | * psmouse_sliced_command() sends an extended PS/2 command to the mouse | ||
| 373 | * using sliced syntax, understood by advanced devices, such as Logitech | ||
| 374 | * or Synaptics touchpads. The command is encoded as: | ||
| 375 | * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu | ||
| 376 | * is the command. | ||
| 377 | */ | ||
| 378 | int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) | ||
| 379 | { | ||
| 380 | int i; | ||
| 381 | |||
| 382 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) | ||
| 383 | return -1; | ||
| 384 | |||
| 385 | for (i = 6; i >= 0; i -= 2) { | ||
| 386 | unsigned char d = (command >> i) & 3; | ||
| 387 | if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES)) | ||
| 388 | return -1; | ||
| 389 | } | ||
| 390 | |||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | |||
| 394 | |||
| 395 | /* | ||
| 396 | * psmouse_reset() resets the mouse into power-on state. | ||
| 397 | */ | ||
| 398 | int psmouse_reset(struct psmouse *psmouse) | ||
| 399 | { | ||
| 400 | unsigned char param[2]; | ||
| 401 | |||
| 402 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) | ||
| 403 | return -1; | ||
| 404 | |||
| 405 | if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) | ||
| 406 | return -1; | ||
| 407 | |||
| 408 | return 0; | ||
| 409 | } | ||
| 410 | |||
| 411 | |||
| 412 | /* | ||
| 413 | * Genius NetMouse magic init. | ||
| 414 | */ | ||
| 415 | static int genius_detect(struct psmouse *psmouse, bool set_properties) | ||
| 416 | { | ||
| 417 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 418 | unsigned char param[4]; | ||
| 419 | |||
| 420 | param[0] = 3; | ||
| 421 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 422 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 423 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 424 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 425 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | ||
| 426 | |||
| 427 | if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) | ||
| 428 | return -1; | ||
| 429 | |||
| 430 | if (set_properties) { | ||
| 431 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 432 | __set_bit(BTN_EXTRA, psmouse->dev->keybit); | ||
| 433 | __set_bit(BTN_SIDE, psmouse->dev->keybit); | ||
| 434 | __set_bit(REL_WHEEL, psmouse->dev->relbit); | ||
| 435 | |||
| 436 | psmouse->vendor = "Genius"; | ||
| 437 | psmouse->name = "Mouse"; | ||
| 438 | psmouse->pktsize = 4; | ||
| 439 | } | ||
| 440 | |||
| 441 | return 0; | ||
| 442 | } | ||
| 443 | |||
| 444 | /* | ||
| 445 | * IntelliMouse magic init. | ||
| 446 | */ | ||
| 447 | static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) | ||
| 448 | { | ||
| 449 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 450 | unsigned char param[2]; | ||
| 451 | |||
| 452 | param[0] = 200; | ||
| 453 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 454 | param[0] = 100; | ||
| 455 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 456 | param[0] = 80; | ||
| 457 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 458 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
| 459 | |||
| 460 | if (param[0] != 3) | ||
| 461 | return -1; | ||
| 462 | |||
| 463 | if (set_properties) { | ||
| 464 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 465 | __set_bit(REL_WHEEL, psmouse->dev->relbit); | ||
| 466 | |||
| 467 | if (!psmouse->vendor) | ||
| 468 | psmouse->vendor = "Generic"; | ||
| 469 | if (!psmouse->name) | ||
| 470 | psmouse->name = "Wheel Mouse"; | ||
| 471 | psmouse->pktsize = 4; | ||
| 472 | } | ||
| 473 | |||
| 474 | return 0; | ||
| 475 | } | ||
| 476 | |||
| 477 | /* | ||
| 478 | * Try IntelliMouse/Explorer magic init. | ||
| 479 | */ | ||
| 480 | static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) | ||
| 481 | { | ||
| 482 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 483 | unsigned char param[2]; | ||
| 484 | |||
| 485 | intellimouse_detect(psmouse, 0); | ||
| 486 | |||
| 487 | param[0] = 200; | ||
| 488 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 489 | param[0] = 200; | ||
| 490 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 491 | param[0] = 80; | ||
| 492 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 493 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
| 494 | |||
| 495 | if (param[0] != 4) | ||
| 496 | return -1; | ||
| 497 | |||
| 498 | /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ | ||
| 499 | param[0] = 200; | ||
| 500 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 501 | param[0] = 80; | ||
| 502 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 503 | param[0] = 40; | ||
| 504 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 505 | |||
| 506 | if (set_properties) { | ||
| 507 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 508 | __set_bit(REL_WHEEL, psmouse->dev->relbit); | ||
| 509 | __set_bit(REL_HWHEEL, psmouse->dev->relbit); | ||
| 510 | __set_bit(BTN_SIDE, psmouse->dev->keybit); | ||
| 511 | __set_bit(BTN_EXTRA, psmouse->dev->keybit); | ||
| 512 | |||
| 513 | if (!psmouse->vendor) | ||
| 514 | psmouse->vendor = "Generic"; | ||
| 515 | if (!psmouse->name) | ||
| 516 | psmouse->name = "Explorer Mouse"; | ||
| 517 | psmouse->pktsize = 4; | ||
| 518 | } | ||
| 519 | |||
| 520 | return 0; | ||
| 521 | } | ||
| 522 | |||
| 523 | /* | ||
| 524 | * Kensington ThinkingMouse / ExpertMouse magic init. | ||
| 525 | */ | ||
| 526 | static int thinking_detect(struct psmouse *psmouse, bool set_properties) | ||
| 527 | { | ||
| 528 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 529 | unsigned char param[2]; | ||
| 530 | static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; | ||
| 531 | int i; | ||
| 532 | |||
| 533 | param[0] = 10; | ||
| 534 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 535 | param[0] = 0; | ||
| 536 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 537 | for (i = 0; i < ARRAY_SIZE(seq); i++) { | ||
| 538 | param[0] = seq[i]; | ||
| 539 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 540 | } | ||
| 541 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
| 542 | |||
| 543 | if (param[0] != 2) | ||
| 544 | return -1; | ||
| 545 | |||
| 546 | if (set_properties) { | ||
| 547 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 548 | __set_bit(BTN_EXTRA, psmouse->dev->keybit); | ||
| 549 | |||
| 550 | psmouse->vendor = "Kensington"; | ||
| 551 | psmouse->name = "ThinkingMouse"; | ||
| 552 | } | ||
| 553 | |||
| 554 | return 0; | ||
| 555 | } | ||
| 556 | |||
| 557 | /* | ||
| 558 | * Bare PS/2 protocol "detection". Always succeeds. | ||
| 559 | */ | ||
| 560 | static int ps2bare_detect(struct psmouse *psmouse, bool set_properties) | ||
| 561 | { | ||
| 562 | if (set_properties) { | ||
| 563 | if (!psmouse->vendor) | ||
| 564 | psmouse->vendor = "Generic"; | ||
| 565 | if (!psmouse->name) | ||
| 566 | psmouse->name = "Mouse"; | ||
| 567 | |||
| 568 | /* | ||
| 569 | * We have no way of figuring true number of buttons so let's | ||
| 570 | * assume that the device has 3. | ||
| 571 | */ | ||
| 572 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 573 | } | ||
| 574 | |||
| 575 | return 0; | ||
| 576 | } | ||
| 577 | |||
| 578 | /* | ||
| 579 | * Cortron PS/2 protocol detection. There's no special way to detect it, so it | ||
| 580 | * must be forced by sysfs protocol writing. | ||
| 581 | */ | ||
| 582 | static int cortron_detect(struct psmouse *psmouse, bool set_properties) | ||
| 583 | { | ||
| 584 | if (set_properties) { | ||
| 585 | psmouse->vendor = "Cortron"; | ||
| 586 | psmouse->name = "PS/2 Trackball"; | ||
| 587 | |||
| 588 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 589 | __set_bit(BTN_SIDE, psmouse->dev->keybit); | ||
| 590 | } | ||
| 591 | |||
| 592 | return 0; | ||
| 593 | } | ||
| 594 | |||
| 595 | /* | ||
| 596 | * psmouse_extensions() probes for any extensions to the basic PS/2 protocol | ||
| 597 | * the mouse may have. | ||
| 598 | */ | ||
| 599 | |||
| 600 | static int psmouse_extensions(struct psmouse *psmouse, | ||
| 601 | unsigned int max_proto, bool set_properties) | ||
| 602 | { | ||
| 603 | bool synaptics_hardware = false; | ||
| 604 | |||
| 605 | /* | ||
| 606 | * We always check for lifebook because it does not disturb mouse | ||
| 607 | * (it only checks DMI information). | ||
| 608 | */ | ||
| 609 | if (lifebook_detect(psmouse, set_properties) == 0) { | ||
| 610 | if (max_proto > PSMOUSE_IMEX) { | ||
| 611 | if (!set_properties || lifebook_init(psmouse) == 0) | ||
| 612 | return PSMOUSE_LIFEBOOK; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | /* | ||
| 617 | * Try Kensington ThinkingMouse (we try first, because synaptics probe | ||
| 618 | * upsets the thinkingmouse). | ||
| 619 | */ | ||
| 620 | |||
| 621 | if (max_proto > PSMOUSE_IMEX && thinking_detect(psmouse, set_properties) == 0) | ||
| 622 | return PSMOUSE_THINKPS; | ||
| 623 | |||
| 624 | /* | ||
| 625 | * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol | ||
| 626 | * support is disabled in config - we need to know if it is synaptics so we | ||
| 627 | * can reset it properly after probing for intellimouse. | ||
| 628 | */ | ||
| 629 | if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) { | ||
| 630 | synaptics_hardware = true; | ||
| 631 | |||
| 632 | if (max_proto > PSMOUSE_IMEX) { | ||
| 633 | /* | ||
| 634 | * Try activating protocol, but check if support is enabled first, since | ||
| 635 | * we try detecting Synaptics even when protocol is disabled. | ||
| 636 | */ | ||
| 637 | if (synaptics_supported() && | ||
| 638 | (!set_properties || synaptics_init(psmouse) == 0)) { | ||
| 639 | return PSMOUSE_SYNAPTICS; | ||
| 640 | } | ||
| 641 | |||
| 642 | /* | ||
| 643 | * Some Synaptics touchpads can emulate extended protocols (like IMPS/2). | ||
| 644 | * Unfortunately Logitech/Genius probes confuse some firmware versions so | ||
| 645 | * we'll have to skip them. | ||
| 646 | */ | ||
| 647 | max_proto = PSMOUSE_IMEX; | ||
| 648 | } | ||
| 649 | /* | ||
| 650 | * Make sure that touchpad is in relative mode, gestures (taps) are enabled | ||
| 651 | */ | ||
| 652 | synaptics_reset(psmouse); | ||
| 653 | } | ||
| 654 | |||
| 655 | /* | ||
| 656 | * Try ALPS TouchPad | ||
| 657 | */ | ||
| 658 | if (max_proto > PSMOUSE_IMEX) { | ||
| 659 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
| 660 | if (alps_detect(psmouse, set_properties) == 0) { | ||
| 661 | if (!set_properties || alps_init(psmouse) == 0) | ||
| 662 | return PSMOUSE_ALPS; | ||
| 663 | /* | ||
| 664 | * Init failed, try basic relative protocols | ||
| 665 | */ | ||
| 666 | max_proto = PSMOUSE_IMEX; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | /* | ||
| 671 | * Try OLPC HGPK touchpad. | ||
| 672 | */ | ||
| 673 | if (max_proto > PSMOUSE_IMEX && | ||
| 674 | hgpk_detect(psmouse, set_properties) == 0) { | ||
| 675 | if (!set_properties || hgpk_init(psmouse) == 0) | ||
| 676 | return PSMOUSE_HGPK; | ||
| 677 | /* | ||
| 678 | * Init failed, try basic relative protocols | ||
| 679 | */ | ||
| 680 | max_proto = PSMOUSE_IMEX; | ||
| 681 | } | ||
| 682 | |||
| 683 | /* | ||
| 684 | * Try Elantech touchpad. | ||
| 685 | */ | ||
| 686 | if (max_proto > PSMOUSE_IMEX && | ||
| 687 | elantech_detect(psmouse, set_properties) == 0) { | ||
| 688 | if (!set_properties || elantech_init(psmouse) == 0) | ||
| 689 | return PSMOUSE_ELANTECH; | ||
| 690 | /* | ||
| 691 | * Init failed, try basic relative protocols | ||
| 692 | */ | ||
| 693 | max_proto = PSMOUSE_IMEX; | ||
| 694 | } | ||
| 695 | |||
| 696 | |||
| 697 | if (max_proto > PSMOUSE_IMEX) { | ||
| 698 | if (genius_detect(psmouse, set_properties) == 0) | ||
| 699 | return PSMOUSE_GENPS; | ||
| 700 | |||
| 701 | if (ps2pp_init(psmouse, set_properties) == 0) | ||
| 702 | return PSMOUSE_PS2PP; | ||
| 703 | |||
| 704 | if (trackpoint_detect(psmouse, set_properties) == 0) | ||
| 705 | return PSMOUSE_TRACKPOINT; | ||
| 706 | |||
| 707 | if (touchkit_ps2_detect(psmouse, set_properties) == 0) | ||
| 708 | return PSMOUSE_TOUCHKIT_PS2; | ||
| 709 | } | ||
| 710 | |||
| 711 | /* | ||
| 712 | * Try Finger Sensing Pad. We do it here because its probe upsets | ||
| 713 | * Trackpoint devices (causing TP_READ_ID command to time out). | ||
| 714 | */ | ||
| 715 | if (max_proto > PSMOUSE_IMEX) { | ||
| 716 | if (fsp_detect(psmouse, set_properties) == 0) { | ||
| 717 | if (!set_properties || fsp_init(psmouse) == 0) | ||
| 718 | return PSMOUSE_FSP; | ||
| 719 | /* | ||
| 720 | * Init failed, try basic relative protocols | ||
| 721 | */ | ||
| 722 | max_proto = PSMOUSE_IMEX; | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | /* | ||
| 727 | * Reset to defaults in case the device got confused by extended | ||
| 728 | * protocol probes. Note that we follow up with full reset because | ||
| 729 | * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. | ||
| 730 | */ | ||
| 731 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
| 732 | psmouse_reset(psmouse); | ||
| 733 | |||
| 734 | if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0) | ||
| 735 | return PSMOUSE_IMEX; | ||
| 736 | |||
| 737 | if (max_proto >= PSMOUSE_IMPS && intellimouse_detect(psmouse, set_properties) == 0) | ||
| 738 | return PSMOUSE_IMPS; | ||
| 739 | |||
| 740 | /* | ||
| 741 | * Okay, all failed, we have a standard mouse here. The number of the buttons | ||
| 742 | * is still a question, though. We assume 3. | ||
| 743 | */ | ||
| 744 | ps2bare_detect(psmouse, set_properties); | ||
| 745 | |||
| 746 | if (synaptics_hardware) { | ||
| 747 | /* | ||
| 748 | * We detected Synaptics hardware but it did not respond to IMPS/2 probes. | ||
| 749 | * We need to reset the touchpad because if there is a track point on the | ||
| 750 | * pass through port it could get disabled while probing for protocol | ||
| 751 | * extensions. | ||
| 752 | */ | ||
| 753 | psmouse_reset(psmouse); | ||
| 754 | } | ||
| 755 | |||
| 756 | return PSMOUSE_PS2; | ||
| 757 | } | ||
| 758 | |||
| 759 | static const struct psmouse_protocol psmouse_protocols[] = { | ||
| 760 | { | ||
| 761 | .type = PSMOUSE_PS2, | ||
| 762 | .name = "PS/2", | ||
| 763 | .alias = "bare", | ||
| 764 | .maxproto = true, | ||
| 765 | .ignore_parity = true, | ||
| 766 | .detect = ps2bare_detect, | ||
| 767 | }, | ||
| 768 | #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP | ||
| 769 | { | ||
| 770 | .type = PSMOUSE_PS2PP, | ||
| 771 | .name = "PS2++", | ||
| 772 | .alias = "logitech", | ||
| 773 | .detect = ps2pp_init, | ||
| 774 | }, | ||
| 775 | #endif | ||
| 776 | { | ||
| 777 | .type = PSMOUSE_THINKPS, | ||
| 778 | .name = "ThinkPS/2", | ||
| 779 | .alias = "thinkps", | ||
| 780 | .detect = thinking_detect, | ||
| 781 | }, | ||
| 782 | { | ||
| 783 | .type = PSMOUSE_GENPS, | ||
| 784 | .name = "GenPS/2", | ||
| 785 | .alias = "genius", | ||
| 786 | .detect = genius_detect, | ||
| 787 | }, | ||
| 788 | { | ||
| 789 | .type = PSMOUSE_IMPS, | ||
| 790 | .name = "ImPS/2", | ||
| 791 | .alias = "imps", | ||
| 792 | .maxproto = true, | ||
| 793 | .ignore_parity = true, | ||
| 794 | .detect = intellimouse_detect, | ||
| 795 | }, | ||
| 796 | { | ||
| 797 | .type = PSMOUSE_IMEX, | ||
| 798 | .name = "ImExPS/2", | ||
| 799 | .alias = "exps", | ||
| 800 | .maxproto = true, | ||
| 801 | .ignore_parity = true, | ||
| 802 | .detect = im_explorer_detect, | ||
| 803 | }, | ||
| 804 | #ifdef CONFIG_MOUSE_PS2_SYNAPTICS | ||
| 805 | { | ||
| 806 | .type = PSMOUSE_SYNAPTICS, | ||
| 807 | .name = "SynPS/2", | ||
| 808 | .alias = "synaptics", | ||
| 809 | .detect = synaptics_detect, | ||
| 810 | .init = synaptics_init, | ||
| 811 | }, | ||
| 812 | #endif | ||
| 813 | #ifdef CONFIG_MOUSE_PS2_ALPS | ||
| 814 | { | ||
| 815 | .type = PSMOUSE_ALPS, | ||
| 816 | .name = "AlpsPS/2", | ||
| 817 | .alias = "alps", | ||
| 818 | .detect = alps_detect, | ||
| 819 | .init = alps_init, | ||
| 820 | }, | ||
| 821 | #endif | ||
| 822 | #ifdef CONFIG_MOUSE_PS2_LIFEBOOK | ||
| 823 | { | ||
| 824 | .type = PSMOUSE_LIFEBOOK, | ||
| 825 | .name = "LBPS/2", | ||
| 826 | .alias = "lifebook", | ||
| 827 | .init = lifebook_init, | ||
| 828 | }, | ||
| 829 | #endif | ||
| 830 | #ifdef CONFIG_MOUSE_PS2_TRACKPOINT | ||
| 831 | { | ||
| 832 | .type = PSMOUSE_TRACKPOINT, | ||
| 833 | .name = "TPPS/2", | ||
| 834 | .alias = "trackpoint", | ||
| 835 | .detect = trackpoint_detect, | ||
| 836 | }, | ||
| 837 | #endif | ||
| 838 | #ifdef CONFIG_MOUSE_PS2_TOUCHKIT | ||
| 839 | { | ||
| 840 | .type = PSMOUSE_TOUCHKIT_PS2, | ||
| 841 | .name = "touchkitPS/2", | ||
| 842 | .alias = "touchkit", | ||
| 843 | .detect = touchkit_ps2_detect, | ||
| 844 | }, | ||
| 845 | #endif | ||
| 846 | #ifdef CONFIG_MOUSE_PS2_OLPC | ||
| 847 | { | ||
| 848 | .type = PSMOUSE_HGPK, | ||
| 849 | .name = "OLPC HGPK", | ||
| 850 | .alias = "hgpk", | ||
| 851 | .detect = hgpk_detect, | ||
| 852 | }, | ||
| 853 | #endif | ||
| 854 | #ifdef CONFIG_MOUSE_PS2_ELANTECH | ||
| 855 | { | ||
| 856 | .type = PSMOUSE_ELANTECH, | ||
| 857 | .name = "ETPS/2", | ||
| 858 | .alias = "elantech", | ||
| 859 | .detect = elantech_detect, | ||
| 860 | .init = elantech_init, | ||
| 861 | }, | ||
| 862 | #endif | ||
| 863 | #ifdef CONFIG_MOUSE_PS2_SENTELIC | ||
| 864 | { | ||
| 865 | .type = PSMOUSE_FSP, | ||
| 866 | .name = "FSPPS/2", | ||
| 867 | .alias = "fsp", | ||
| 868 | .detect = fsp_detect, | ||
| 869 | .init = fsp_init, | ||
| 870 | }, | ||
| 871 | #endif | ||
| 872 | { | ||
| 873 | .type = PSMOUSE_CORTRON, | ||
| 874 | .name = "CortronPS/2", | ||
| 875 | .alias = "cortps", | ||
| 876 | .detect = cortron_detect, | ||
| 877 | }, | ||
| 878 | { | ||
| 879 | .type = PSMOUSE_AUTO, | ||
| 880 | .name = "auto", | ||
| 881 | .alias = "any", | ||
| 882 | .maxproto = true, | ||
| 883 | }, | ||
| 884 | }; | ||
| 885 | |||
| 886 | static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) | ||
| 887 | { | ||
| 888 | int i; | ||
| 889 | |||
| 890 | for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) | ||
| 891 | if (psmouse_protocols[i].type == type) | ||
| 892 | return &psmouse_protocols[i]; | ||
| 893 | |||
| 894 | WARN_ON(1); | ||
| 895 | return &psmouse_protocols[0]; | ||
| 896 | } | ||
| 897 | |||
| 898 | static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) | ||
| 899 | { | ||
| 900 | const struct psmouse_protocol *p; | ||
| 901 | int i; | ||
| 902 | |||
| 903 | for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) { | ||
| 904 | p = &psmouse_protocols[i]; | ||
| 905 | |||
| 906 | if ((strlen(p->name) == len && !strncmp(p->name, name, len)) || | ||
| 907 | (strlen(p->alias) == len && !strncmp(p->alias, name, len))) | ||
| 908 | return &psmouse_protocols[i]; | ||
| 909 | } | ||
| 910 | |||
| 911 | return NULL; | ||
| 912 | } | ||
| 913 | |||
| 914 | |||
| 915 | /* | ||
| 916 | * psmouse_probe() probes for a PS/2 mouse. | ||
| 917 | */ | ||
| 918 | |||
| 919 | static int psmouse_probe(struct psmouse *psmouse) | ||
| 920 | { | ||
| 921 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 922 | unsigned char param[2]; | ||
| 923 | |||
| 924 | /* | ||
| 925 | * First, we check if it's a mouse. It should send 0x00 or 0x03 | ||
| 926 | * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. | ||
| 927 | * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent | ||
| 928 | * ID queries, probably due to a firmware bug. | ||
| 929 | */ | ||
| 930 | |||
| 931 | param[0] = 0xa5; | ||
| 932 | if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) | ||
| 933 | return -1; | ||
| 934 | |||
| 935 | if (param[0] != 0x00 && param[0] != 0x03 && | ||
| 936 | param[0] != 0x04 && param[0] != 0xff) | ||
| 937 | return -1; | ||
| 938 | |||
| 939 | /* | ||
| 940 | * Then we reset and disable the mouse so that it doesn't generate events. | ||
| 941 | */ | ||
| 942 | |||
| 943 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) | ||
| 944 | printk(KERN_WARNING "psmouse.c: Failed to reset mouse on %s\n", ps2dev->serio->phys); | ||
| 945 | |||
| 946 | return 0; | ||
| 947 | } | ||
| 948 | |||
| 949 | /* | ||
| 950 | * Here we set the mouse resolution. | ||
| 951 | */ | ||
| 952 | |||
| 953 | void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) | ||
| 954 | { | ||
| 955 | static const unsigned char params[] = { 0, 1, 2, 2, 3 }; | ||
| 956 | unsigned char p; | ||
| 957 | |||
| 958 | if (resolution == 0 || resolution > 200) | ||
| 959 | resolution = 200; | ||
| 960 | |||
| 961 | p = params[resolution / 50]; | ||
| 962 | ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); | ||
| 963 | psmouse->resolution = 25 << p; | ||
| 964 | } | ||
| 965 | |||
| 966 | /* | ||
| 967 | * Here we set the mouse report rate. | ||
| 968 | */ | ||
| 969 | |||
| 970 | static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) | ||
| 971 | { | ||
| 972 | static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; | ||
| 973 | unsigned char r; | ||
| 974 | int i = 0; | ||
| 975 | |||
| 976 | while (rates[i] > rate) i++; | ||
| 977 | r = rates[i]; | ||
| 978 | ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); | ||
| 979 | psmouse->rate = r; | ||
| 980 | } | ||
| 981 | |||
| 982 | /* | ||
| 983 | * psmouse_initialize() initializes the mouse to a sane state. | ||
| 984 | */ | ||
| 985 | |||
| 986 | static void psmouse_initialize(struct psmouse *psmouse) | ||
| 987 | { | ||
| 988 | /* | ||
| 989 | * We set the mouse report rate, resolution and scaling. | ||
| 990 | */ | ||
| 991 | |||
| 992 | if (psmouse_max_proto != PSMOUSE_PS2) { | ||
| 993 | psmouse->set_rate(psmouse, psmouse->rate); | ||
| 994 | psmouse->set_resolution(psmouse, psmouse->resolution); | ||
| 995 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); | ||
| 996 | } | ||
| 997 | } | ||
| 998 | |||
| 999 | /* | ||
| 1000 | * psmouse_activate() enables the mouse so that we get motion reports from it. | ||
| 1001 | */ | ||
| 1002 | |||
| 1003 | static void psmouse_activate(struct psmouse *psmouse) | ||
| 1004 | { | ||
| 1005 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) | ||
| 1006 | printk(KERN_WARNING "psmouse.c: Failed to enable mouse on %s\n", | ||
| 1007 | psmouse->ps2dev.serio->phys); | ||
| 1008 | |||
| 1009 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | |||
| 1013 | /* | ||
| 1014 | * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion | ||
| 1015 | * reports from it unless we explicitly request it. | ||
| 1016 | */ | ||
| 1017 | |||
| 1018 | static void psmouse_deactivate(struct psmouse *psmouse) | ||
| 1019 | { | ||
| 1020 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) | ||
| 1021 | printk(KERN_WARNING "psmouse.c: Failed to deactivate mouse on %s\n", | ||
| 1022 | psmouse->ps2dev.serio->phys); | ||
| 1023 | |||
| 1024 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | /* | ||
| 1028 | * psmouse_poll() - default poll hanlder. Everyone except for ALPS uses it. | ||
| 1029 | */ | ||
| 1030 | |||
| 1031 | static int psmouse_poll(struct psmouse *psmouse) | ||
| 1032 | { | ||
| 1033 | return ps2_command(&psmouse->ps2dev, psmouse->packet, | ||
| 1034 | PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | |||
| 1038 | /* | ||
| 1039 | * psmouse_resync() attempts to re-validate current protocol. | ||
| 1040 | */ | ||
| 1041 | |||
| 1042 | static void psmouse_resync(struct work_struct *work) | ||
| 1043 | { | ||
| 1044 | struct psmouse *parent = NULL, *psmouse = | ||
| 1045 | container_of(work, struct psmouse, resync_work.work); | ||
| 1046 | struct serio *serio = psmouse->ps2dev.serio; | ||
| 1047 | psmouse_ret_t rc = PSMOUSE_GOOD_DATA; | ||
| 1048 | bool failed = false, enabled = false; | ||
| 1049 | int i; | ||
| 1050 | |||
| 1051 | mutex_lock(&psmouse_mutex); | ||
| 1052 | |||
| 1053 | if (psmouse->state != PSMOUSE_RESYNCING) | ||
| 1054 | goto out; | ||
| 1055 | |||
| 1056 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1057 | parent = serio_get_drvdata(serio->parent); | ||
| 1058 | psmouse_deactivate(parent); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | /* | ||
| 1062 | * Some mice don't ACK commands sent while they are in the middle of | ||
| 1063 | * transmitting motion packet. To avoid delay we use ps2_sendbyte() | ||
| 1064 | * instead of ps2_command() which would wait for 200ms for an ACK | ||
| 1065 | * that may never come. | ||
| 1066 | * As an additional quirk ALPS touchpads may not only forget to ACK | ||
| 1067 | * disable command but will stop reporting taps, so if we see that | ||
| 1068 | * mouse at least once ACKs disable we will do full reconnect if ACK | ||
| 1069 | * is missing. | ||
| 1070 | */ | ||
| 1071 | psmouse->num_resyncs++; | ||
| 1072 | |||
| 1073 | if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { | ||
| 1074 | if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command) | ||
| 1075 | failed = true; | ||
| 1076 | } else | ||
| 1077 | psmouse->acks_disable_command = true; | ||
| 1078 | |||
| 1079 | /* | ||
| 1080 | * Poll the mouse. If it was reset the packet will be shorter than | ||
| 1081 | * psmouse->pktsize and ps2_command will fail. We do not expect and | ||
| 1082 | * do not handle scenario when mouse "upgrades" its protocol while | ||
| 1083 | * disconnected since it would require additional delay. If we ever | ||
| 1084 | * see a mouse that does it we'll adjust the code. | ||
| 1085 | */ | ||
| 1086 | if (!failed) { | ||
| 1087 | if (psmouse->poll(psmouse)) | ||
| 1088 | failed = true; | ||
| 1089 | else { | ||
| 1090 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1091 | for (i = 0; i < psmouse->pktsize; i++) { | ||
| 1092 | psmouse->pktcnt++; | ||
| 1093 | rc = psmouse->protocol_handler(psmouse); | ||
| 1094 | if (rc != PSMOUSE_GOOD_DATA) | ||
| 1095 | break; | ||
| 1096 | } | ||
| 1097 | if (rc != PSMOUSE_FULL_PACKET) | ||
| 1098 | failed = true; | ||
| 1099 | psmouse_set_state(psmouse, PSMOUSE_RESYNCING); | ||
| 1100 | } | ||
| 1101 | } | ||
| 1102 | /* | ||
| 1103 | * Now try to enable mouse. We try to do that even if poll failed and also | ||
| 1104 | * repeat our attempts 5 times, otherwise we may be left out with disabled | ||
| 1105 | * mouse. | ||
| 1106 | */ | ||
| 1107 | for (i = 0; i < 5; i++) { | ||
| 1108 | if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { | ||
| 1109 | enabled = true; | ||
| 1110 | break; | ||
| 1111 | } | ||
| 1112 | msleep(200); | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | if (!enabled) { | ||
| 1116 | printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n", | ||
| 1117 | psmouse->ps2dev.serio->phys); | ||
| 1118 | failed = true; | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | if (failed) { | ||
| 1122 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 1123 | printk(KERN_INFO "psmouse.c: resync failed, issuing reconnect request\n"); | ||
| 1124 | serio_reconnect(serio); | ||
| 1125 | } else | ||
| 1126 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
| 1127 | |||
| 1128 | if (parent) | ||
| 1129 | psmouse_activate(parent); | ||
| 1130 | out: | ||
| 1131 | mutex_unlock(&psmouse_mutex); | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | /* | ||
| 1135 | * psmouse_cleanup() resets the mouse into power-on state. | ||
| 1136 | */ | ||
| 1137 | |||
| 1138 | static void psmouse_cleanup(struct serio *serio) | ||
| 1139 | { | ||
| 1140 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
| 1141 | struct psmouse *parent = NULL; | ||
| 1142 | |||
| 1143 | mutex_lock(&psmouse_mutex); | ||
| 1144 | |||
| 1145 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1146 | parent = serio_get_drvdata(serio->parent); | ||
| 1147 | psmouse_deactivate(parent); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 1151 | |||
| 1152 | /* | ||
| 1153 | * Disable stream mode so cleanup routine can proceed undisturbed. | ||
| 1154 | */ | ||
| 1155 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) | ||
| 1156 | printk(KERN_WARNING "psmouse.c: Failed to disable mouse on %s\n", | ||
| 1157 | psmouse->ps2dev.serio->phys); | ||
| 1158 | |||
| 1159 | if (psmouse->cleanup) | ||
| 1160 | psmouse->cleanup(psmouse); | ||
| 1161 | |||
| 1162 | /* | ||
| 1163 | * Reset the mouse to defaults (bare PS/2 protocol). | ||
| 1164 | */ | ||
| 1165 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); | ||
| 1166 | |||
| 1167 | /* | ||
| 1168 | * Some boxes, such as HP nx7400, get terribly confused if mouse | ||
| 1169 | * is not fully enabled before suspending/shutting down. | ||
| 1170 | */ | ||
| 1171 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); | ||
| 1172 | |||
| 1173 | if (parent) { | ||
| 1174 | if (parent->pt_deactivate) | ||
| 1175 | parent->pt_deactivate(parent); | ||
| 1176 | |||
| 1177 | psmouse_activate(parent); | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | mutex_unlock(&psmouse_mutex); | ||
| 1181 | } | ||
| 1182 | |||
| 1183 | /* | ||
| 1184 | * psmouse_disconnect() closes and frees. | ||
| 1185 | */ | ||
| 1186 | |||
| 1187 | static void psmouse_disconnect(struct serio *serio) | ||
| 1188 | { | ||
| 1189 | struct psmouse *psmouse, *parent = NULL; | ||
| 1190 | |||
| 1191 | psmouse = serio_get_drvdata(serio); | ||
| 1192 | |||
| 1193 | sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); | ||
| 1194 | |||
| 1195 | mutex_lock(&psmouse_mutex); | ||
| 1196 | |||
| 1197 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1198 | |||
| 1199 | /* make sure we don't have a resync in progress */ | ||
| 1200 | mutex_unlock(&psmouse_mutex); | ||
| 1201 | flush_workqueue(kpsmoused_wq); | ||
| 1202 | mutex_lock(&psmouse_mutex); | ||
| 1203 | |||
| 1204 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1205 | parent = serio_get_drvdata(serio->parent); | ||
| 1206 | psmouse_deactivate(parent); | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | if (psmouse->disconnect) | ||
| 1210 | psmouse->disconnect(psmouse); | ||
| 1211 | |||
| 1212 | if (parent && parent->pt_deactivate) | ||
| 1213 | parent->pt_deactivate(parent); | ||
| 1214 | |||
| 1215 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 1216 | |||
| 1217 | serio_close(serio); | ||
| 1218 | serio_set_drvdata(serio, NULL); | ||
| 1219 | input_unregister_device(psmouse->dev); | ||
| 1220 | kfree(psmouse); | ||
| 1221 | |||
| 1222 | if (parent) | ||
| 1223 | psmouse_activate(parent); | ||
| 1224 | |||
| 1225 | mutex_unlock(&psmouse_mutex); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | static int psmouse_switch_protocol(struct psmouse *psmouse, | ||
| 1229 | const struct psmouse_protocol *proto) | ||
| 1230 | { | ||
| 1231 | const struct psmouse_protocol *selected_proto; | ||
| 1232 | struct input_dev *input_dev = psmouse->dev; | ||
| 1233 | |||
| 1234 | input_dev->dev.parent = &psmouse->ps2dev.serio->dev; | ||
| 1235 | |||
| 1236 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); | ||
| 1237 | input_dev->keybit[BIT_WORD(BTN_MOUSE)] = | ||
| 1238 | BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); | ||
| 1239 | input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); | ||
| 1240 | |||
| 1241 | psmouse->set_rate = psmouse_set_rate; | ||
| 1242 | psmouse->set_resolution = psmouse_set_resolution; | ||
| 1243 | psmouse->poll = psmouse_poll; | ||
| 1244 | psmouse->protocol_handler = psmouse_process_byte; | ||
| 1245 | psmouse->pktsize = 3; | ||
| 1246 | |||
| 1247 | if (proto && (proto->detect || proto->init)) { | ||
| 1248 | if (proto->detect && proto->detect(psmouse, true) < 0) | ||
| 1249 | return -1; | ||
| 1250 | |||
| 1251 | if (proto->init && proto->init(psmouse) < 0) | ||
| 1252 | return -1; | ||
| 1253 | |||
| 1254 | psmouse->type = proto->type; | ||
| 1255 | selected_proto = proto; | ||
| 1256 | } else { | ||
| 1257 | psmouse->type = psmouse_extensions(psmouse, | ||
| 1258 | psmouse_max_proto, true); | ||
| 1259 | selected_proto = psmouse_protocol_by_type(psmouse->type); | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | psmouse->ignore_parity = selected_proto->ignore_parity; | ||
| 1263 | |||
| 1264 | /* | ||
| 1265 | * If mouse's packet size is 3 there is no point in polling the | ||
| 1266 | * device in hopes to detect protocol reset - we won't get less | ||
| 1267 | * than 3 bytes response anyhow. | ||
| 1268 | */ | ||
| 1269 | if (psmouse->pktsize == 3) | ||
| 1270 | psmouse->resync_time = 0; | ||
| 1271 | |||
| 1272 | /* | ||
| 1273 | * Some smart KVMs fake response to POLL command returning just | ||
| 1274 | * 3 bytes and messing up our resync logic, so if initial poll | ||
| 1275 | * fails we won't try polling the device anymore. Hopefully | ||
| 1276 | * such KVM will maintain initially selected protocol. | ||
| 1277 | */ | ||
| 1278 | if (psmouse->resync_time && psmouse->poll(psmouse)) | ||
| 1279 | psmouse->resync_time = 0; | ||
| 1280 | |||
| 1281 | snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s", | ||
| 1282 | selected_proto->name, psmouse->vendor, psmouse->name); | ||
| 1283 | |||
| 1284 | input_dev->name = psmouse->devname; | ||
| 1285 | input_dev->phys = psmouse->phys; | ||
| 1286 | input_dev->id.bustype = BUS_I8042; | ||
| 1287 | input_dev->id.vendor = 0x0002; | ||
| 1288 | input_dev->id.product = psmouse->type; | ||
| 1289 | input_dev->id.version = psmouse->model; | ||
| 1290 | |||
| 1291 | return 0; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | /* | ||
| 1295 | * psmouse_connect() is a callback from the serio module when | ||
| 1296 | * an unhandled serio port is found. | ||
| 1297 | */ | ||
| 1298 | static int psmouse_connect(struct serio *serio, struct serio_driver *drv) | ||
| 1299 | { | ||
| 1300 | struct psmouse *psmouse, *parent = NULL; | ||
| 1301 | struct input_dev *input_dev; | ||
| 1302 | int retval = 0, error = -ENOMEM; | ||
| 1303 | |||
| 1304 | mutex_lock(&psmouse_mutex); | ||
| 1305 | |||
| 1306 | /* | ||
| 1307 | * If this is a pass-through port deactivate parent so the device | ||
| 1308 | * connected to this port can be successfully identified | ||
| 1309 | */ | ||
| 1310 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1311 | parent = serio_get_drvdata(serio->parent); | ||
| 1312 | psmouse_deactivate(parent); | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL); | ||
| 1316 | input_dev = input_allocate_device(); | ||
| 1317 | if (!psmouse || !input_dev) | ||
| 1318 | goto err_free; | ||
| 1319 | |||
| 1320 | ps2_init(&psmouse->ps2dev, serio); | ||
| 1321 | INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync); | ||
| 1322 | psmouse->dev = input_dev; | ||
| 1323 | snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys); | ||
| 1324 | |||
| 1325 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 1326 | |||
| 1327 | serio_set_drvdata(serio, psmouse); | ||
| 1328 | |||
| 1329 | error = serio_open(serio, drv); | ||
| 1330 | if (error) | ||
| 1331 | goto err_clear_drvdata; | ||
| 1332 | |||
| 1333 | if (psmouse_probe(psmouse) < 0) { | ||
| 1334 | error = -ENODEV; | ||
| 1335 | goto err_close_serio; | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | psmouse->rate = psmouse_rate; | ||
| 1339 | psmouse->resolution = psmouse_resolution; | ||
| 1340 | psmouse->resetafter = psmouse_resetafter; | ||
| 1341 | psmouse->resync_time = parent ? 0 : psmouse_resync_time; | ||
| 1342 | psmouse->smartscroll = psmouse_smartscroll; | ||
| 1343 | |||
| 1344 | psmouse_switch_protocol(psmouse, NULL); | ||
| 1345 | |||
| 1346 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1347 | psmouse_initialize(psmouse); | ||
| 1348 | |||
| 1349 | error = input_register_device(psmouse->dev); | ||
| 1350 | if (error) | ||
| 1351 | goto err_protocol_disconnect; | ||
| 1352 | |||
| 1353 | if (parent && parent->pt_activate) | ||
| 1354 | parent->pt_activate(parent); | ||
| 1355 | |||
| 1356 | error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group); | ||
| 1357 | if (error) | ||
| 1358 | goto err_pt_deactivate; | ||
| 1359 | |||
| 1360 | psmouse_activate(psmouse); | ||
| 1361 | |||
| 1362 | out: | ||
| 1363 | /* If this is a pass-through port the parent needs to be re-activated */ | ||
| 1364 | if (parent) | ||
| 1365 | psmouse_activate(parent); | ||
| 1366 | |||
| 1367 | mutex_unlock(&psmouse_mutex); | ||
| 1368 | return retval; | ||
| 1369 | |||
| 1370 | err_pt_deactivate: | ||
| 1371 | if (parent && parent->pt_deactivate) | ||
| 1372 | parent->pt_deactivate(parent); | ||
| 1373 | input_unregister_device(psmouse->dev); | ||
| 1374 | input_dev = NULL; /* so we don't try to free it below */ | ||
| 1375 | err_protocol_disconnect: | ||
| 1376 | if (psmouse->disconnect) | ||
| 1377 | psmouse->disconnect(psmouse); | ||
| 1378 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 1379 | err_close_serio: | ||
| 1380 | serio_close(serio); | ||
| 1381 | err_clear_drvdata: | ||
| 1382 | serio_set_drvdata(serio, NULL); | ||
| 1383 | err_free: | ||
| 1384 | input_free_device(input_dev); | ||
| 1385 | kfree(psmouse); | ||
| 1386 | |||
| 1387 | retval = error; | ||
| 1388 | goto out; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | |||
| 1392 | static int psmouse_reconnect(struct serio *serio) | ||
| 1393 | { | ||
| 1394 | struct psmouse *psmouse = serio_get_drvdata(serio); | ||
| 1395 | struct psmouse *parent = NULL; | ||
| 1396 | struct serio_driver *drv = serio->drv; | ||
| 1397 | unsigned char type; | ||
| 1398 | int rc = -1; | ||
| 1399 | |||
| 1400 | if (!drv || !psmouse) { | ||
| 1401 | printk(KERN_DEBUG "psmouse: reconnect request, but serio is disconnected, ignoring...\n"); | ||
| 1402 | return -1; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | mutex_lock(&psmouse_mutex); | ||
| 1406 | |||
| 1407 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1408 | parent = serio_get_drvdata(serio->parent); | ||
| 1409 | psmouse_deactivate(parent); | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 1413 | |||
| 1414 | if (psmouse->reconnect) { | ||
| 1415 | if (psmouse->reconnect(psmouse)) | ||
| 1416 | goto out; | ||
| 1417 | } else { | ||
| 1418 | psmouse_reset(psmouse); | ||
| 1419 | |||
| 1420 | if (psmouse_probe(psmouse) < 0) | ||
| 1421 | goto out; | ||
| 1422 | |||
| 1423 | type = psmouse_extensions(psmouse, psmouse_max_proto, false); | ||
| 1424 | if (psmouse->type != type) | ||
| 1425 | goto out; | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | /* ok, the device type (and capabilities) match the old one, | ||
| 1429 | * we can continue using it, complete intialization | ||
| 1430 | */ | ||
| 1431 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1432 | |||
| 1433 | psmouse_initialize(psmouse); | ||
| 1434 | |||
| 1435 | if (parent && parent->pt_activate) | ||
| 1436 | parent->pt_activate(parent); | ||
| 1437 | |||
| 1438 | psmouse_activate(psmouse); | ||
| 1439 | rc = 0; | ||
| 1440 | |||
| 1441 | out: | ||
| 1442 | /* If this is a pass-through port the parent waits to be activated */ | ||
| 1443 | if (parent) | ||
| 1444 | psmouse_activate(parent); | ||
| 1445 | |||
| 1446 | mutex_unlock(&psmouse_mutex); | ||
| 1447 | return rc; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | static struct serio_device_id psmouse_serio_ids[] = { | ||
| 1451 | { | ||
| 1452 | .type = SERIO_8042, | ||
| 1453 | .proto = SERIO_ANY, | ||
| 1454 | .id = SERIO_ANY, | ||
| 1455 | .extra = SERIO_ANY, | ||
| 1456 | }, | ||
| 1457 | { | ||
| 1458 | .type = SERIO_PS_PSTHRU, | ||
| 1459 | .proto = SERIO_ANY, | ||
| 1460 | .id = SERIO_ANY, | ||
| 1461 | .extra = SERIO_ANY, | ||
| 1462 | }, | ||
| 1463 | { 0 } | ||
| 1464 | }; | ||
| 1465 | |||
| 1466 | MODULE_DEVICE_TABLE(serio, psmouse_serio_ids); | ||
| 1467 | |||
| 1468 | static struct serio_driver psmouse_drv = { | ||
| 1469 | .driver = { | ||
| 1470 | .name = "psmouse", | ||
| 1471 | }, | ||
| 1472 | .description = DRIVER_DESC, | ||
| 1473 | .id_table = psmouse_serio_ids, | ||
| 1474 | .interrupt = psmouse_interrupt, | ||
| 1475 | .connect = psmouse_connect, | ||
| 1476 | .reconnect = psmouse_reconnect, | ||
| 1477 | .disconnect = psmouse_disconnect, | ||
| 1478 | .cleanup = psmouse_cleanup, | ||
| 1479 | }; | ||
| 1480 | |||
| 1481 | ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr, | ||
| 1482 | char *buf) | ||
| 1483 | { | ||
| 1484 | struct serio *serio = to_serio_port(dev); | ||
| 1485 | struct psmouse_attribute *attr = to_psmouse_attr(devattr); | ||
| 1486 | struct psmouse *psmouse; | ||
| 1487 | |||
| 1488 | psmouse = serio_get_drvdata(serio); | ||
| 1489 | |||
| 1490 | return attr->show(psmouse, attr->data, buf); | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *devattr, | ||
| 1494 | const char *buf, size_t count) | ||
| 1495 | { | ||
| 1496 | struct serio *serio = to_serio_port(dev); | ||
| 1497 | struct psmouse_attribute *attr = to_psmouse_attr(devattr); | ||
| 1498 | struct psmouse *psmouse, *parent = NULL; | ||
| 1499 | int retval; | ||
| 1500 | |||
| 1501 | retval = mutex_lock_interruptible(&psmouse_mutex); | ||
| 1502 | if (retval) | ||
| 1503 | goto out; | ||
| 1504 | |||
| 1505 | psmouse = serio_get_drvdata(serio); | ||
| 1506 | |||
| 1507 | if (attr->protect) { | ||
| 1508 | if (psmouse->state == PSMOUSE_IGNORE) { | ||
| 1509 | retval = -ENODEV; | ||
| 1510 | goto out_unlock; | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1514 | parent = serio_get_drvdata(serio->parent); | ||
| 1515 | psmouse_deactivate(parent); | ||
| 1516 | } | ||
| 1517 | |||
| 1518 | psmouse_deactivate(psmouse); | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | retval = attr->set(psmouse, attr->data, buf, count); | ||
| 1522 | |||
| 1523 | if (attr->protect) { | ||
| 1524 | if (retval != -ENODEV) | ||
| 1525 | psmouse_activate(psmouse); | ||
| 1526 | |||
| 1527 | if (parent) | ||
| 1528 | psmouse_activate(parent); | ||
| 1529 | } | ||
| 1530 | |||
| 1531 | out_unlock: | ||
| 1532 | mutex_unlock(&psmouse_mutex); | ||
| 1533 | out: | ||
| 1534 | return retval; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf) | ||
| 1538 | { | ||
| 1539 | unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); | ||
| 1540 | |||
| 1541 | return sprintf(buf, "%u\n", *field); | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count) | ||
| 1545 | { | ||
| 1546 | unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); | ||
| 1547 | unsigned long value; | ||
| 1548 | |||
| 1549 | if (strict_strtoul(buf, 10, &value)) | ||
| 1550 | return -EINVAL; | ||
| 1551 | |||
| 1552 | if ((unsigned int)value != value) | ||
| 1553 | return -EINVAL; | ||
| 1554 | |||
| 1555 | *field = value; | ||
| 1556 | |||
| 1557 | return count; | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf) | ||
| 1561 | { | ||
| 1562 | return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name); | ||
| 1563 | } | ||
| 1564 | |||
| 1565 | static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count) | ||
| 1566 | { | ||
| 1567 | struct serio *serio = psmouse->ps2dev.serio; | ||
| 1568 | struct psmouse *parent = NULL; | ||
| 1569 | struct input_dev *old_dev, *new_dev; | ||
| 1570 | const struct psmouse_protocol *proto, *old_proto; | ||
| 1571 | int error; | ||
| 1572 | int retry = 0; | ||
| 1573 | |||
| 1574 | proto = psmouse_protocol_by_name(buf, count); | ||
| 1575 | if (!proto) | ||
| 1576 | return -EINVAL; | ||
| 1577 | |||
| 1578 | if (psmouse->type == proto->type) | ||
| 1579 | return count; | ||
| 1580 | |||
| 1581 | new_dev = input_allocate_device(); | ||
| 1582 | if (!new_dev) | ||
| 1583 | return -ENOMEM; | ||
| 1584 | |||
| 1585 | while (serio->child) { | ||
| 1586 | if (++retry > 3) { | ||
| 1587 | printk(KERN_WARNING | ||
| 1588 | "psmouse: failed to destroy child port, " | ||
| 1589 | "protocol change aborted.\n"); | ||
| 1590 | input_free_device(new_dev); | ||
| 1591 | return -EIO; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | mutex_unlock(&psmouse_mutex); | ||
| 1595 | serio_unregister_child_port(serio); | ||
| 1596 | mutex_lock(&psmouse_mutex); | ||
| 1597 | |||
| 1598 | if (serio->drv != &psmouse_drv) { | ||
| 1599 | input_free_device(new_dev); | ||
| 1600 | return -ENODEV; | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | if (psmouse->type == proto->type) { | ||
| 1604 | input_free_device(new_dev); | ||
| 1605 | return count; /* switched by other thread */ | ||
| 1606 | } | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { | ||
| 1610 | parent = serio_get_drvdata(serio->parent); | ||
| 1611 | if (parent->pt_deactivate) | ||
| 1612 | parent->pt_deactivate(parent); | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | old_dev = psmouse->dev; | ||
| 1616 | old_proto = psmouse_protocol_by_type(psmouse->type); | ||
| 1617 | |||
| 1618 | if (psmouse->disconnect) | ||
| 1619 | psmouse->disconnect(psmouse); | ||
| 1620 | |||
| 1621 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 1622 | |||
| 1623 | psmouse->dev = new_dev; | ||
| 1624 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 1625 | |||
| 1626 | if (psmouse_switch_protocol(psmouse, proto) < 0) { | ||
| 1627 | psmouse_reset(psmouse); | ||
| 1628 | /* default to PSMOUSE_PS2 */ | ||
| 1629 | psmouse_switch_protocol(psmouse, &psmouse_protocols[0]); | ||
| 1630 | } | ||
| 1631 | |||
| 1632 | psmouse_initialize(psmouse); | ||
| 1633 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1634 | |||
| 1635 | error = input_register_device(psmouse->dev); | ||
| 1636 | if (error) { | ||
| 1637 | if (psmouse->disconnect) | ||
| 1638 | psmouse->disconnect(psmouse); | ||
| 1639 | |||
| 1640 | psmouse_set_state(psmouse, PSMOUSE_IGNORE); | ||
| 1641 | input_free_device(new_dev); | ||
| 1642 | psmouse->dev = old_dev; | ||
| 1643 | psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); | ||
| 1644 | psmouse_switch_protocol(psmouse, old_proto); | ||
| 1645 | psmouse_initialize(psmouse); | ||
| 1646 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 1647 | |||
| 1648 | return error; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | input_unregister_device(old_dev); | ||
| 1652 | |||
| 1653 | if (parent && parent->pt_activate) | ||
| 1654 | parent->pt_activate(parent); | ||
| 1655 | |||
| 1656 | return count; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count) | ||
| 1660 | { | ||
| 1661 | unsigned long value; | ||
| 1662 | |||
| 1663 | if (strict_strtoul(buf, 10, &value)) | ||
| 1664 | return -EINVAL; | ||
| 1665 | |||
| 1666 | psmouse->set_rate(psmouse, value); | ||
| 1667 | return count; | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count) | ||
| 1671 | { | ||
| 1672 | unsigned long value; | ||
| 1673 | |||
| 1674 | if (strict_strtoul(buf, 10, &value)) | ||
| 1675 | return -EINVAL; | ||
| 1676 | |||
| 1677 | psmouse->set_resolution(psmouse, value); | ||
| 1678 | return count; | ||
| 1679 | } | ||
| 1680 | |||
| 1681 | |||
| 1682 | static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) | ||
| 1683 | { | ||
| 1684 | const struct psmouse_protocol *proto; | ||
| 1685 | |||
| 1686 | if (!val) | ||
| 1687 | return -EINVAL; | ||
| 1688 | |||
| 1689 | proto = psmouse_protocol_by_name(val, strlen(val)); | ||
| 1690 | |||
| 1691 | if (!proto || !proto->maxproto) | ||
| 1692 | return -EINVAL; | ||
| 1693 | |||
| 1694 | *((unsigned int *)kp->arg) = proto->type; | ||
| 1695 | |||
| 1696 | return 0; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) | ||
| 1700 | { | ||
| 1701 | int type = *((unsigned int *)kp->arg); | ||
| 1702 | |||
| 1703 | return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name); | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | static int __init psmouse_init(void) | ||
| 1707 | { | ||
| 1708 | int err; | ||
| 1709 | |||
| 1710 | lifebook_module_init(); | ||
| 1711 | synaptics_module_init(); | ||
| 1712 | |||
| 1713 | kpsmoused_wq = create_singlethread_workqueue("kpsmoused"); | ||
| 1714 | if (!kpsmoused_wq) { | ||
| 1715 | printk(KERN_ERR "psmouse: failed to create kpsmoused workqueue\n"); | ||
| 1716 | return -ENOMEM; | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | err = serio_register_driver(&psmouse_drv); | ||
| 1720 | if (err) | ||
| 1721 | destroy_workqueue(kpsmoused_wq); | ||
| 1722 | |||
| 1723 | return err; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | static void __exit psmouse_exit(void) | ||
| 1727 | { | ||
| 1728 | serio_unregister_driver(&psmouse_drv); | ||
| 1729 | destroy_workqueue(kpsmoused_wq); | ||
| 1730 | } | ||
| 1731 | |||
| 1732 | module_init(psmouse_init); | ||
| 1733 | module_exit(psmouse_exit); | ||
diff --git a/usr/src/dkms_source_tree/psmouse.h b/usr/src/dkms_source_tree/psmouse.h new file mode 100644 index 0000000..593e910 --- /dev/null +++ b/usr/src/dkms_source_tree/psmouse.h | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | #ifndef _PSMOUSE_H | ||
| 2 | #define _PSMOUSE_H | ||
| 3 | |||
| 4 | #define PSMOUSE_CMD_SETSCALE11 0x00e6 | ||
| 5 | #define PSMOUSE_CMD_SETSCALE21 0x00e7 | ||
| 6 | #define PSMOUSE_CMD_SETRES 0x10e8 | ||
| 7 | #define PSMOUSE_CMD_GETINFO 0x03e9 | ||
| 8 | #define PSMOUSE_CMD_SETSTREAM 0x00ea | ||
| 9 | #define PSMOUSE_CMD_SETPOLL 0x00f0 | ||
| 10 | #define PSMOUSE_CMD_POLL 0x00eb /* caller sets number of bytes to receive */ | ||
| 11 | #define PSMOUSE_CMD_GETID 0x02f2 | ||
| 12 | #define PSMOUSE_CMD_SETRATE 0x10f3 | ||
| 13 | #define PSMOUSE_CMD_ENABLE 0x00f4 | ||
| 14 | #define PSMOUSE_CMD_DISABLE 0x00f5 | ||
| 15 | #define PSMOUSE_CMD_RESET_DIS 0x00f6 | ||
| 16 | #define PSMOUSE_CMD_RESET_BAT 0x02ff | ||
| 17 | |||
| 18 | #define PSMOUSE_RET_BAT 0xaa | ||
| 19 | #define PSMOUSE_RET_ID 0x00 | ||
| 20 | #define PSMOUSE_RET_ACK 0xfa | ||
| 21 | #define PSMOUSE_RET_NAK 0xfe | ||
| 22 | |||
| 23 | enum psmouse_state { | ||
| 24 | PSMOUSE_IGNORE, | ||
| 25 | PSMOUSE_INITIALIZING, | ||
| 26 | PSMOUSE_RESYNCING, | ||
| 27 | PSMOUSE_CMD_MODE, | ||
| 28 | PSMOUSE_ACTIVATED, | ||
| 29 | }; | ||
| 30 | |||
| 31 | /* psmouse protocol handler return codes */ | ||
| 32 | typedef enum { | ||
| 33 | PSMOUSE_BAD_DATA, | ||
| 34 | PSMOUSE_GOOD_DATA, | ||
| 35 | PSMOUSE_FULL_PACKET | ||
| 36 | } psmouse_ret_t; | ||
| 37 | |||
| 38 | struct psmouse { | ||
| 39 | void *private; | ||
| 40 | struct input_dev *dev; | ||
| 41 | struct ps2dev ps2dev; | ||
| 42 | struct delayed_work resync_work; | ||
| 43 | char *vendor; | ||
| 44 | char *name; | ||
| 45 | unsigned char packet[8]; | ||
| 46 | unsigned char badbyte; | ||
| 47 | unsigned char pktcnt; | ||
| 48 | unsigned char pktsize; | ||
| 49 | unsigned char type; | ||
| 50 | bool ignore_parity; | ||
| 51 | bool acks_disable_command; | ||
| 52 | unsigned int model; | ||
| 53 | unsigned long last; | ||
| 54 | unsigned long out_of_sync_cnt; | ||
| 55 | unsigned long num_resyncs; | ||
| 56 | enum psmouse_state state; | ||
| 57 | char devname[64]; | ||
| 58 | char phys[32]; | ||
| 59 | |||
| 60 | unsigned int rate; | ||
| 61 | unsigned int resolution; | ||
| 62 | unsigned int resetafter; | ||
| 63 | unsigned int resync_time; | ||
| 64 | bool smartscroll; /* Logitech only */ | ||
| 65 | |||
| 66 | psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse); | ||
| 67 | void (*set_rate)(struct psmouse *psmouse, unsigned int rate); | ||
| 68 | void (*set_resolution)(struct psmouse *psmouse, unsigned int resolution); | ||
| 69 | |||
| 70 | int (*reconnect)(struct psmouse *psmouse); | ||
| 71 | void (*disconnect)(struct psmouse *psmouse); | ||
| 72 | void (*cleanup)(struct psmouse *psmouse); | ||
| 73 | int (*poll)(struct psmouse *psmouse); | ||
| 74 | |||
| 75 | void (*pt_activate)(struct psmouse *psmouse); | ||
| 76 | void (*pt_deactivate)(struct psmouse *psmouse); | ||
| 77 | }; | ||
| 78 | |||
| 79 | enum psmouse_type { | ||
| 80 | PSMOUSE_NONE, | ||
| 81 | PSMOUSE_PS2, | ||
| 82 | PSMOUSE_PS2PP, | ||
| 83 | PSMOUSE_THINKPS, | ||
| 84 | PSMOUSE_GENPS, | ||
| 85 | PSMOUSE_IMPS, | ||
| 86 | PSMOUSE_IMEX, | ||
| 87 | PSMOUSE_SYNAPTICS, | ||
| 88 | PSMOUSE_ALPS, | ||
| 89 | PSMOUSE_LIFEBOOK, | ||
| 90 | PSMOUSE_TRACKPOINT, | ||
| 91 | PSMOUSE_TOUCHKIT_PS2, | ||
| 92 | PSMOUSE_CORTRON, | ||
| 93 | PSMOUSE_HGPK, | ||
| 94 | PSMOUSE_ELANTECH, | ||
| 95 | PSMOUSE_FSP, | ||
| 96 | PSMOUSE_AUTO /* This one should always be last */ | ||
| 97 | }; | ||
| 98 | |||
| 99 | void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, | ||
| 100 | unsigned long delay); | ||
| 101 | int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command); | ||
| 102 | int psmouse_reset(struct psmouse *psmouse); | ||
| 103 | void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state); | ||
| 104 | void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution); | ||
| 105 | |||
| 106 | struct psmouse_attribute { | ||
| 107 | struct device_attribute dattr; | ||
| 108 | void *data; | ||
| 109 | ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf); | ||
| 110 | ssize_t (*set)(struct psmouse *psmouse, void *data, | ||
| 111 | const char *buf, size_t count); | ||
| 112 | bool protect; | ||
| 113 | }; | ||
| 114 | #define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr) | ||
| 115 | |||
| 116 | ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *attr, | ||
| 117 | char *buf); | ||
| 118 | ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr, | ||
| 119 | const char *buf, size_t count); | ||
| 120 | |||
| 121 | #define __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) \ | ||
| 122 | static struct psmouse_attribute psmouse_attr_##_name = { \ | ||
| 123 | .dattr = { \ | ||
| 124 | .attr = { \ | ||
| 125 | .name = __stringify(_name), \ | ||
| 126 | .mode = _mode, \ | ||
| 127 | }, \ | ||
| 128 | .show = psmouse_attr_show_helper, \ | ||
| 129 | .store = psmouse_attr_set_helper, \ | ||
| 130 | }, \ | ||
| 131 | .data = _data, \ | ||
| 132 | .show = _show, \ | ||
| 133 | .set = _set, \ | ||
| 134 | .protect = _protect, \ | ||
| 135 | } | ||
| 136 | |||
| 137 | #define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \ | ||
| 138 | static ssize_t _show(struct psmouse *, void *, char *); \ | ||
| 139 | static ssize_t _set(struct psmouse *, void *, const char *, size_t); \ | ||
| 140 | __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) | ||
| 141 | |||
| 142 | #define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \ | ||
| 143 | __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, true) | ||
| 144 | |||
| 145 | #define PSMOUSE_DEFINE_RO_ATTR(_name, _mode, _data, _show) \ | ||
| 146 | static ssize_t _show(struct psmouse *, void *, char *); \ | ||
| 147 | __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, NULL, true) | ||
| 148 | |||
| 149 | #define PSMOUSE_DEFINE_WO_ATTR(_name, _mode, _data, _set) \ | ||
| 150 | static ssize_t _set(struct psmouse *, void *, const char *, size_t); \ | ||
| 151 | __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true) | ||
| 152 | |||
| 153 | #endif /* _PSMOUSE_H */ | ||
diff --git a/usr/src/dkms_source_tree/sentelic.c b/usr/src/dkms_source_tree/sentelic.c new file mode 100644 index 0000000..1242775 --- /dev/null +++ b/usr/src/dkms_source_tree/sentelic.c | |||
| @@ -0,0 +1,871 @@ | |||
| 1 | /*- | ||
| 2 | * Finger Sensing Pad PS/2 mouse driver. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. | ||
| 5 | * Copyright (C) 2005-2010 Tai-hwa Liang, Sentelic Corporation. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2 | ||
| 10 | * of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/version.h> | ||
| 24 | #include <linux/input.h> | ||
| 25 | #include <linux/ctype.h> | ||
| 26 | #include <linux/libps2.h> | ||
| 27 | #include <linux/serio.h> | ||
| 28 | #include <linux/jiffies.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | |||
| 31 | #include "psmouse.h" | ||
| 32 | #include "sentelic.h" | ||
| 33 | |||
| 34 | /* | ||
| 35 | * Timeout for FSP PS/2 command only (in milliseconds). | ||
| 36 | */ | ||
| 37 | #define FSP_CMD_TIMEOUT 200 | ||
| 38 | #define FSP_CMD_TIMEOUT2 30 | ||
| 39 | |||
| 40 | /** Driver version. */ | ||
| 41 | static const char fsp_drv_ver[] = "1.0.0-K"; | ||
| 42 | |||
| 43 | /* | ||
| 44 | * Make sure that the value being sent to FSP will not conflict with | ||
| 45 | * possible sample rate values. | ||
| 46 | */ | ||
| 47 | static unsigned char fsp_test_swap_cmd(unsigned char reg_val) | ||
| 48 | { | ||
| 49 | switch (reg_val) { | ||
| 50 | case 10: case 20: case 40: case 60: case 80: case 100: case 200: | ||
| 51 | /* | ||
| 52 | * The requested value being sent to FSP matched to possible | ||
| 53 | * sample rates, swap the given value such that the hardware | ||
| 54 | * wouldn't get confused. | ||
| 55 | */ | ||
| 56 | return (reg_val >> 4) | (reg_val << 4); | ||
| 57 | default: | ||
| 58 | return reg_val; /* swap isn't necessary */ | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Make sure that the value being sent to FSP will not conflict with certain | ||
| 64 | * commands. | ||
| 65 | */ | ||
| 66 | static unsigned char fsp_test_invert_cmd(unsigned char reg_val) | ||
| 67 | { | ||
| 68 | switch (reg_val) { | ||
| 69 | case 0xe9: case 0xee: case 0xf2: case 0xff: | ||
| 70 | /* | ||
| 71 | * The requested value being sent to FSP matched to certain | ||
| 72 | * commands, inverse the given value such that the hardware | ||
| 73 | * wouldn't get confused. | ||
| 74 | */ | ||
| 75 | return ~reg_val; | ||
| 76 | default: | ||
| 77 | return reg_val; /* inversion isn't necessary */ | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val) | ||
| 82 | { | ||
| 83 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 84 | unsigned char param[3]; | ||
| 85 | unsigned char addr; | ||
| 86 | int rc = -1; | ||
| 87 | |||
| 88 | /* | ||
| 89 | * We need to shut off the device and switch it into command | ||
| 90 | * mode so we don't confuse our protocol handler. We don't need | ||
| 91 | * to do that for writes because sysfs set helper does this for | ||
| 92 | * us. | ||
| 93 | */ | ||
| 94 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); | ||
| 95 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 96 | |||
| 97 | ps2_begin_command(ps2dev); | ||
| 98 | |||
| 99 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 100 | goto out; | ||
| 101 | |||
| 102 | /* should return 0xfe(request for resending) */ | ||
| 103 | ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); | ||
| 104 | /* should return 0xfc(failed) */ | ||
| 105 | ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); | ||
| 106 | |||
| 107 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 108 | goto out; | ||
| 109 | |||
| 110 | if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) { | ||
| 111 | ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2); | ||
| 112 | } else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) { | ||
| 113 | /* swapping is required */ | ||
| 114 | ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2); | ||
| 115 | /* expect 0xfe */ | ||
| 116 | } else { | ||
| 117 | /* swapping isn't necessary */ | ||
| 118 | ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); | ||
| 119 | /* expect 0xfe */ | ||
| 120 | } | ||
| 121 | /* should return 0xfc(failed) */ | ||
| 122 | ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT); | ||
| 123 | |||
| 124 | if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0) | ||
| 125 | goto out; | ||
| 126 | |||
| 127 | *reg_val = param[2]; | ||
| 128 | rc = 0; | ||
| 129 | |||
| 130 | out: | ||
| 131 | ps2_end_command(ps2dev); | ||
| 132 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); | ||
| 133 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
| 134 | dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n", | ||
| 135 | reg_addr, *reg_val, rc); | ||
| 136 | return rc; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val) | ||
| 140 | { | ||
| 141 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 142 | unsigned char v; | ||
| 143 | int rc = -1; | ||
| 144 | |||
| 145 | ps2_begin_command(ps2dev); | ||
| 146 | |||
| 147 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 148 | goto out; | ||
| 149 | |||
| 150 | if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) { | ||
| 151 | /* inversion is required */ | ||
| 152 | ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2); | ||
| 153 | } else { | ||
| 154 | if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) { | ||
| 155 | /* swapping is required */ | ||
| 156 | ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2); | ||
| 157 | } else { | ||
| 158 | /* swapping isn't necessary */ | ||
| 159 | ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | /* write the register address in correct order */ | ||
| 163 | ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); | ||
| 164 | |||
| 165 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 166 | return -1; | ||
| 167 | |||
| 168 | if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { | ||
| 169 | /* inversion is required */ | ||
| 170 | ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); | ||
| 171 | } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { | ||
| 172 | /* swapping is required */ | ||
| 173 | ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); | ||
| 174 | } else { | ||
| 175 | /* swapping isn't necessary */ | ||
| 176 | ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); | ||
| 177 | } | ||
| 178 | |||
| 179 | /* write the register value in correct order */ | ||
| 180 | ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); | ||
| 181 | rc = 0; | ||
| 182 | |||
| 183 | out: | ||
| 184 | ps2_end_command(ps2dev); | ||
| 185 | dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n", | ||
| 186 | reg_addr, reg_val, rc); | ||
| 187 | return rc; | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Enable register clock gating for writing certain registers */ | ||
| 191 | static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable) | ||
| 192 | { | ||
| 193 | int v, nv; | ||
| 194 | |||
| 195 | if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1) | ||
| 196 | return -1; | ||
| 197 | |||
| 198 | if (enable) | ||
| 199 | nv = v | FSP_BIT_EN_REG_CLK; | ||
| 200 | else | ||
| 201 | nv = v & ~FSP_BIT_EN_REG_CLK; | ||
| 202 | |||
| 203 | /* only write if necessary */ | ||
| 204 | if (nv != v) | ||
| 205 | if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1) | ||
| 206 | return -1; | ||
| 207 | |||
| 208 | return 0; | ||
| 209 | } | ||
| 210 | |||
| 211 | static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val) | ||
| 212 | { | ||
| 213 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 214 | unsigned char param[3]; | ||
| 215 | int rc = -1; | ||
| 216 | |||
| 217 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE); | ||
| 218 | psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); | ||
| 219 | |||
| 220 | ps2_begin_command(ps2dev); | ||
| 221 | |||
| 222 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 223 | goto out; | ||
| 224 | |||
| 225 | ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2); | ||
| 226 | ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); | ||
| 227 | |||
| 228 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 229 | goto out; | ||
| 230 | |||
| 231 | ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2); | ||
| 232 | ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); | ||
| 233 | |||
| 234 | /* get the returned result */ | ||
| 235 | if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
| 236 | goto out; | ||
| 237 | |||
| 238 | *reg_val = param[2]; | ||
| 239 | rc = 0; | ||
| 240 | |||
| 241 | out: | ||
| 242 | ps2_end_command(ps2dev); | ||
| 243 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); | ||
| 244 | psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); | ||
| 245 | dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n", | ||
| 246 | *reg_val, rc); | ||
| 247 | return rc; | ||
| 248 | } | ||
| 249 | |||
| 250 | static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val) | ||
| 251 | { | ||
| 252 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 253 | unsigned char v; | ||
| 254 | int rc = -1; | ||
| 255 | |||
| 256 | ps2_begin_command(ps2dev); | ||
| 257 | |||
| 258 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 259 | goto out; | ||
| 260 | |||
| 261 | ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2); | ||
| 262 | ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2); | ||
| 263 | |||
| 264 | if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0) | ||
| 265 | return -1; | ||
| 266 | |||
| 267 | if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) { | ||
| 268 | ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2); | ||
| 269 | } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) { | ||
| 270 | /* swapping is required */ | ||
| 271 | ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2); | ||
| 272 | } else { | ||
| 273 | /* swapping isn't necessary */ | ||
| 274 | ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2); | ||
| 275 | } | ||
| 276 | |||
| 277 | ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2); | ||
| 278 | rc = 0; | ||
| 279 | |||
| 280 | out: | ||
| 281 | ps2_end_command(ps2dev); | ||
| 282 | dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n", | ||
| 283 | reg_val, rc); | ||
| 284 | return rc; | ||
| 285 | } | ||
| 286 | |||
| 287 | static int fsp_get_version(struct psmouse *psmouse, int *version) | ||
| 288 | { | ||
| 289 | if (fsp_reg_read(psmouse, FSP_REG_VERSION, version)) | ||
| 290 | return -EIO; | ||
| 291 | |||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | |||
| 295 | static int fsp_get_revision(struct psmouse *psmouse, int *rev) | ||
| 296 | { | ||
| 297 | if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev)) | ||
| 298 | return -EIO; | ||
| 299 | |||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | |||
| 303 | static int fsp_get_buttons(struct psmouse *psmouse, int *btn) | ||
| 304 | { | ||
| 305 | static const int buttons[] = { | ||
| 306 | 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */ | ||
| 307 | 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */ | ||
| 308 | 0x04, /* Left/Middle/Right & Scroll Up/Down */ | ||
| 309 | 0x02, /* Left/Middle/Right */ | ||
| 310 | }; | ||
| 311 | int val; | ||
| 312 | |||
| 313 | if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1) | ||
| 314 | return -EIO; | ||
| 315 | |||
| 316 | *btn = buttons[(val & 0x30) >> 4]; | ||
| 317 | return 0; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Enable on-pad command tag output */ | ||
| 321 | static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable) | ||
| 322 | { | ||
| 323 | int v, nv; | ||
| 324 | int res = 0; | ||
| 325 | |||
| 326 | if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) { | ||
| 327 | dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n"); | ||
| 328 | return -EIO; | ||
| 329 | } | ||
| 330 | |||
| 331 | if (enable) | ||
| 332 | nv = v | FSP_BIT_EN_OPC_TAG; | ||
| 333 | else | ||
| 334 | nv = v & ~FSP_BIT_EN_OPC_TAG; | ||
| 335 | |||
| 336 | /* only write if necessary */ | ||
| 337 | if (nv != v) { | ||
| 338 | fsp_reg_write_enable(psmouse, true); | ||
| 339 | res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv); | ||
| 340 | fsp_reg_write_enable(psmouse, false); | ||
| 341 | } | ||
| 342 | |||
| 343 | if (res != 0) { | ||
| 344 | dev_err(&psmouse->ps2dev.serio->dev, | ||
| 345 | "Unable to enable OPC tag.\n"); | ||
| 346 | res = -EIO; | ||
| 347 | } | ||
| 348 | |||
| 349 | return res; | ||
| 350 | } | ||
| 351 | |||
| 352 | static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable) | ||
| 353 | { | ||
| 354 | struct fsp_data *pad = psmouse->private; | ||
| 355 | int val; | ||
| 356 | |||
| 357 | if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) | ||
| 358 | return -EIO; | ||
| 359 | |||
| 360 | pad->vscroll = enable; | ||
| 361 | |||
| 362 | if (enable) | ||
| 363 | val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE); | ||
| 364 | else | ||
| 365 | val &= ~FSP_BIT_FIX_VSCR; | ||
| 366 | |||
| 367 | if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) | ||
| 368 | return -EIO; | ||
| 369 | |||
| 370 | return 0; | ||
| 371 | } | ||
| 372 | |||
| 373 | static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable) | ||
| 374 | { | ||
| 375 | struct fsp_data *pad = psmouse->private; | ||
| 376 | int val, v2; | ||
| 377 | |||
| 378 | if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val)) | ||
| 379 | return -EIO; | ||
| 380 | |||
| 381 | if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2)) | ||
| 382 | return -EIO; | ||
| 383 | |||
| 384 | pad->hscroll = enable; | ||
| 385 | |||
| 386 | if (enable) { | ||
| 387 | val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE); | ||
| 388 | v2 |= FSP_BIT_EN_MSID6; | ||
| 389 | } else { | ||
| 390 | val &= ~FSP_BIT_FIX_HSCR; | ||
| 391 | v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8); | ||
| 392 | } | ||
| 393 | |||
| 394 | if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val)) | ||
| 395 | return -EIO; | ||
| 396 | |||
| 397 | /* reconfigure horizontal scrolling packet output */ | ||
| 398 | if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2)) | ||
| 399 | return -EIO; | ||
| 400 | |||
| 401 | return 0; | ||
| 402 | } | ||
| 403 | |||
| 404 | /* | ||
| 405 | * Write device specific initial parameters. | ||
| 406 | * | ||
| 407 | * ex: 0xab 0xcd - write oxcd into register 0xab | ||
| 408 | */ | ||
| 409 | static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data, | ||
| 410 | const char *buf, size_t count) | ||
| 411 | { | ||
| 412 | unsigned long reg, val; | ||
| 413 | char *rest; | ||
| 414 | ssize_t retval; | ||
| 415 | |||
| 416 | reg = simple_strtoul(buf, &rest, 16); | ||
| 417 | if (rest == buf || *rest != ' ' || reg > 0xff) | ||
| 418 | return -EINVAL; | ||
| 419 | |||
| 420 | if (strict_strtoul(rest + 1, 16, &val) || val > 0xff) | ||
| 421 | return -EINVAL; | ||
| 422 | |||
| 423 | if (fsp_reg_write_enable(psmouse, true)) | ||
| 424 | return -EIO; | ||
| 425 | |||
| 426 | retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count; | ||
| 427 | |||
| 428 | fsp_reg_write_enable(psmouse, false); | ||
| 429 | |||
| 430 | return count; | ||
| 431 | } | ||
| 432 | |||
| 433 | PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg); | ||
| 434 | |||
| 435 | static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse, | ||
| 436 | void *data, char *buf) | ||
| 437 | { | ||
| 438 | struct fsp_data *pad = psmouse->private; | ||
| 439 | |||
| 440 | return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val); | ||
| 441 | } | ||
| 442 | |||
| 443 | /* | ||
| 444 | * Read a register from device. | ||
| 445 | * | ||
| 446 | * ex: 0xab -- read content from register 0xab | ||
| 447 | */ | ||
| 448 | static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data, | ||
| 449 | const char *buf, size_t count) | ||
| 450 | { | ||
| 451 | struct fsp_data *pad = psmouse->private; | ||
| 452 | unsigned long reg; | ||
| 453 | int val; | ||
| 454 | |||
| 455 | if (strict_strtoul(buf, 16, ®) || reg > 0xff) | ||
| 456 | return -EINVAL; | ||
| 457 | |||
| 458 | if (fsp_reg_read(psmouse, reg, &val)) | ||
| 459 | return -EIO; | ||
| 460 | |||
| 461 | pad->last_reg = reg; | ||
| 462 | pad->last_val = val; | ||
| 463 | |||
| 464 | return count; | ||
| 465 | } | ||
| 466 | |||
| 467 | PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL, | ||
| 468 | fsp_attr_show_getreg, fsp_attr_set_getreg); | ||
| 469 | |||
| 470 | static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse, | ||
| 471 | void *data, char *buf) | ||
| 472 | { | ||
| 473 | int val = 0; | ||
| 474 | |||
| 475 | if (fsp_page_reg_read(psmouse, &val)) | ||
| 476 | return -EIO; | ||
| 477 | |||
| 478 | return sprintf(buf, "%02x\n", val); | ||
| 479 | } | ||
| 480 | |||
| 481 | static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data, | ||
| 482 | const char *buf, size_t count) | ||
| 483 | { | ||
| 484 | unsigned long val; | ||
| 485 | |||
| 486 | if (strict_strtoul(buf, 16, &val) || val > 0xff) | ||
| 487 | return -EINVAL; | ||
| 488 | |||
| 489 | if (fsp_page_reg_write(psmouse, val)) | ||
| 490 | return -EIO; | ||
| 491 | |||
| 492 | return count; | ||
| 493 | } | ||
| 494 | |||
| 495 | PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL, | ||
| 496 | fsp_attr_show_pagereg, fsp_attr_set_pagereg); | ||
| 497 | |||
| 498 | static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse, | ||
| 499 | void *data, char *buf) | ||
| 500 | { | ||
| 501 | struct fsp_data *pad = psmouse->private; | ||
| 502 | |||
| 503 | return sprintf(buf, "%d\n", pad->vscroll); | ||
| 504 | } | ||
| 505 | |||
| 506 | static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data, | ||
| 507 | const char *buf, size_t count) | ||
| 508 | { | ||
| 509 | unsigned long val; | ||
| 510 | |||
| 511 | if (strict_strtoul(buf, 10, &val) || val > 1) | ||
| 512 | return -EINVAL; | ||
| 513 | |||
| 514 | fsp_onpad_vscr(psmouse, val); | ||
| 515 | |||
| 516 | return count; | ||
| 517 | } | ||
| 518 | |||
| 519 | PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL, | ||
| 520 | fsp_attr_show_vscroll, fsp_attr_set_vscroll); | ||
| 521 | |||
| 522 | static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse, | ||
| 523 | void *data, char *buf) | ||
| 524 | { | ||
| 525 | struct fsp_data *pad = psmouse->private; | ||
| 526 | |||
| 527 | return sprintf(buf, "%d\n", pad->hscroll); | ||
| 528 | } | ||
| 529 | |||
| 530 | static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data, | ||
| 531 | const char *buf, size_t count) | ||
| 532 | { | ||
| 533 | unsigned long val; | ||
| 534 | |||
| 535 | if (strict_strtoul(buf, 10, &val) || val > 1) | ||
| 536 | return -EINVAL; | ||
| 537 | |||
| 538 | fsp_onpad_hscr(psmouse, val); | ||
| 539 | |||
| 540 | return count; | ||
| 541 | } | ||
| 542 | |||
| 543 | PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL, | ||
| 544 | fsp_attr_show_hscroll, fsp_attr_set_hscroll); | ||
| 545 | |||
| 546 | static ssize_t fsp_attr_show_flags(struct psmouse *psmouse, | ||
| 547 | void *data, char *buf) | ||
| 548 | { | ||
| 549 | struct fsp_data *pad = psmouse->private; | ||
| 550 | |||
| 551 | return sprintf(buf, "%c\n", | ||
| 552 | pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c'); | ||
| 553 | } | ||
| 554 | |||
| 555 | static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data, | ||
| 556 | const char *buf, size_t count) | ||
| 557 | { | ||
| 558 | struct fsp_data *pad = psmouse->private; | ||
| 559 | size_t i; | ||
| 560 | |||
| 561 | for (i = 0; i < count; i++) { | ||
| 562 | switch (buf[i]) { | ||
| 563 | case 'C': | ||
| 564 | pad->flags |= FSPDRV_FLAG_EN_OPC; | ||
| 565 | break; | ||
| 566 | case 'c': | ||
| 567 | pad->flags &= ~FSPDRV_FLAG_EN_OPC; | ||
| 568 | break; | ||
| 569 | default: | ||
| 570 | return -EINVAL; | ||
| 571 | } | ||
| 572 | } | ||
| 573 | return count; | ||
| 574 | } | ||
| 575 | |||
| 576 | PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL, | ||
| 577 | fsp_attr_show_flags, fsp_attr_set_flags); | ||
| 578 | |||
| 579 | static ssize_t fsp_attr_show_ver(struct psmouse *psmouse, | ||
| 580 | void *data, char *buf) | ||
| 581 | { | ||
| 582 | return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver); | ||
| 583 | } | ||
| 584 | |||
| 585 | PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver); | ||
| 586 | |||
| 587 | static struct attribute *fsp_attributes[] = { | ||
| 588 | &psmouse_attr_setreg.dattr.attr, | ||
| 589 | &psmouse_attr_getreg.dattr.attr, | ||
| 590 | &psmouse_attr_page.dattr.attr, | ||
| 591 | &psmouse_attr_vscroll.dattr.attr, | ||
| 592 | &psmouse_attr_hscroll.dattr.attr, | ||
| 593 | &psmouse_attr_flags.dattr.attr, | ||
| 594 | &psmouse_attr_ver.dattr.attr, | ||
| 595 | NULL | ||
| 596 | }; | ||
| 597 | |||
| 598 | static struct attribute_group fsp_attribute_group = { | ||
| 599 | .attrs = fsp_attributes, | ||
| 600 | }; | ||
| 601 | |||
| 602 | #ifdef FSP_DEBUG | ||
| 603 | static void fsp_packet_debug(unsigned char packet[]) | ||
| 604 | { | ||
| 605 | static unsigned int ps2_packet_cnt; | ||
| 606 | static unsigned int ps2_last_second; | ||
| 607 | unsigned int jiffies_msec; | ||
| 608 | |||
| 609 | ps2_packet_cnt++; | ||
| 610 | jiffies_msec = jiffies_to_msecs(jiffies); | ||
| 611 | printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n", | ||
| 612 | jiffies_msec, packet[0], packet[1], packet[2], packet[3]); | ||
| 613 | |||
| 614 | if (jiffies_msec - ps2_last_second > 1000) { | ||
| 615 | printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt); | ||
| 616 | ps2_packet_cnt = 0; | ||
| 617 | ps2_last_second = jiffies_msec; | ||
| 618 | } | ||
| 619 | } | ||
| 620 | #else | ||
| 621 | static void fsp_packet_debug(unsigned char packet[]) | ||
| 622 | { | ||
| 623 | } | ||
| 624 | #endif | ||
| 625 | |||
| 626 | static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse) | ||
| 627 | { | ||
| 628 | struct input_dev *dev = psmouse->dev; | ||
| 629 | struct fsp_data *ad = psmouse->private; | ||
| 630 | unsigned char *packet = psmouse->packet; | ||
| 631 | unsigned char button_status = 0, lscroll = 0, rscroll = 0; | ||
| 632 | int rel_x, rel_y; | ||
| 633 | |||
| 634 | if (psmouse->pktcnt < 4) | ||
| 635 | return PSMOUSE_GOOD_DATA; | ||
| 636 | |||
| 637 | /* | ||
| 638 | * Full packet accumulated, process it | ||
| 639 | */ | ||
| 640 | |||
| 641 | switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) { | ||
| 642 | case FSP_PKT_TYPE_ABS: | ||
| 643 | dev_warn(&psmouse->ps2dev.serio->dev, | ||
| 644 | "Unexpected absolute mode packet, ignored.\n"); | ||
| 645 | break; | ||
| 646 | |||
| 647 | case FSP_PKT_TYPE_NORMAL_OPC: | ||
| 648 | /* on-pad click, filter it if necessary */ | ||
| 649 | if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC) | ||
| 650 | packet[0] &= ~BIT(0); | ||
| 651 | /* fall through */ | ||
| 652 | |||
| 653 | case FSP_PKT_TYPE_NORMAL: | ||
| 654 | /* normal packet */ | ||
| 655 | /* special packet data translation from on-pad packets */ | ||
| 656 | if (packet[3] != 0) { | ||
| 657 | if (packet[3] & BIT(0)) | ||
| 658 | button_status |= 0x01; /* wheel down */ | ||
| 659 | if (packet[3] & BIT(1)) | ||
| 660 | button_status |= 0x0f; /* wheel up */ | ||
| 661 | if (packet[3] & BIT(2)) | ||
| 662 | button_status |= BIT(4);/* horizontal left */ | ||
| 663 | if (packet[3] & BIT(3)) | ||
| 664 | button_status |= BIT(5);/* horizontal right */ | ||
| 665 | /* push back to packet queue */ | ||
| 666 | if (button_status != 0) | ||
| 667 | packet[3] = button_status; | ||
| 668 | rscroll = (packet[3] >> 4) & 1; | ||
| 669 | lscroll = (packet[3] >> 5) & 1; | ||
| 670 | } | ||
| 671 | /* | ||
| 672 | * Processing wheel up/down and extra button events | ||
| 673 | */ | ||
| 674 | input_report_rel(dev, REL_WHEEL, | ||
| 675 | (int)(packet[3] & 8) - (int)(packet[3] & 7)); | ||
| 676 | input_report_rel(dev, REL_HWHEEL, lscroll - rscroll); | ||
| 677 | input_report_key(dev, BTN_BACK, lscroll); | ||
| 678 | input_report_key(dev, BTN_FORWARD, rscroll); | ||
| 679 | |||
| 680 | /* | ||
| 681 | * Standard PS/2 Mouse | ||
| 682 | */ | ||
| 683 | input_report_key(dev, BTN_LEFT, packet[0] & 1); | ||
| 684 | input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1); | ||
| 685 | input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1); | ||
| 686 | |||
| 687 | rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0; | ||
| 688 | rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0; | ||
| 689 | |||
| 690 | input_report_rel(dev, REL_X, rel_x); | ||
| 691 | input_report_rel(dev, REL_Y, rel_y); | ||
| 692 | break; | ||
| 693 | } | ||
| 694 | |||
| 695 | input_sync(dev); | ||
| 696 | |||
| 697 | fsp_packet_debug(packet); | ||
| 698 | |||
| 699 | return PSMOUSE_FULL_PACKET; | ||
| 700 | } | ||
| 701 | |||
| 702 | static int fsp_activate_protocol(struct psmouse *psmouse) | ||
| 703 | { | ||
| 704 | struct fsp_data *pad = psmouse->private; | ||
| 705 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 706 | unsigned char param[2]; | ||
| 707 | int val; | ||
| 708 | |||
| 709 | /* | ||
| 710 | * Standard procedure to enter FSP Intellimouse mode | ||
| 711 | * (scrolling wheel, 4th and 5th buttons) | ||
| 712 | */ | ||
| 713 | param[0] = 200; | ||
| 714 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 715 | param[0] = 200; | ||
| 716 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 717 | param[0] = 80; | ||
| 718 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); | ||
| 719 | |||
| 720 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); | ||
| 721 | if (param[0] != 0x04) { | ||
| 722 | dev_err(&psmouse->ps2dev.serio->dev, | ||
| 723 | "Unable to enable 4 bytes packet format.\n"); | ||
| 724 | return -EIO; | ||
| 725 | } | ||
| 726 | |||
| 727 | if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) { | ||
| 728 | dev_err(&psmouse->ps2dev.serio->dev, | ||
| 729 | "Unable to read SYSCTL5 register.\n"); | ||
| 730 | return -EIO; | ||
| 731 | } | ||
| 732 | |||
| 733 | val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8); | ||
| 734 | /* Ensure we are not in absolute mode */ | ||
| 735 | val &= ~FSP_BIT_EN_PKT_G0; | ||
| 736 | if (pad->buttons == 0x06) { | ||
| 737 | /* Left/Middle/Right & Scroll Up/Down/Right/Left */ | ||
| 738 | val |= FSP_BIT_EN_MSID6; | ||
| 739 | } | ||
| 740 | |||
| 741 | if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) { | ||
| 742 | dev_err(&psmouse->ps2dev.serio->dev, | ||
| 743 | "Unable to set up required mode bits.\n"); | ||
| 744 | return -EIO; | ||
| 745 | } | ||
| 746 | |||
| 747 | /* | ||
| 748 | * Enable OPC tags such that driver can tell the difference between | ||
| 749 | * on-pad and real button click | ||
| 750 | */ | ||
| 751 | if (fsp_opc_tag_enable(psmouse, true)) | ||
| 752 | dev_warn(&psmouse->ps2dev.serio->dev, | ||
| 753 | "Failed to enable OPC tag mode.\n"); | ||
| 754 | |||
| 755 | /* Enable on-pad vertical and horizontal scrolling */ | ||
| 756 | fsp_onpad_vscr(psmouse, true); | ||
| 757 | fsp_onpad_hscr(psmouse, true); | ||
| 758 | |||
| 759 | return 0; | ||
| 760 | } | ||
| 761 | |||
| 762 | int fsp_detect(struct psmouse *psmouse, bool set_properties) | ||
| 763 | { | ||
| 764 | int id; | ||
| 765 | |||
| 766 | if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id)) | ||
| 767 | return -EIO; | ||
| 768 | |||
| 769 | if (id != 0x01) | ||
| 770 | return -ENODEV; | ||
| 771 | |||
| 772 | if (set_properties) { | ||
| 773 | psmouse->vendor = "Sentelic"; | ||
| 774 | psmouse->name = "FingerSensingPad"; | ||
| 775 | } | ||
| 776 | |||
| 777 | return 0; | ||
| 778 | } | ||
| 779 | |||
| 780 | static void fsp_reset(struct psmouse *psmouse) | ||
| 781 | { | ||
| 782 | fsp_opc_tag_enable(psmouse, false); | ||
| 783 | fsp_onpad_vscr(psmouse, false); | ||
| 784 | fsp_onpad_hscr(psmouse, false); | ||
| 785 | } | ||
| 786 | |||
| 787 | static void fsp_disconnect(struct psmouse *psmouse) | ||
| 788 | { | ||
| 789 | sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, | ||
| 790 | &fsp_attribute_group); | ||
| 791 | |||
| 792 | fsp_reset(psmouse); | ||
| 793 | kfree(psmouse->private); | ||
| 794 | } | ||
| 795 | |||
| 796 | static int fsp_reconnect(struct psmouse *psmouse) | ||
| 797 | { | ||
| 798 | int version; | ||
| 799 | |||
| 800 | if (fsp_detect(psmouse, 0)) | ||
| 801 | return -ENODEV; | ||
| 802 | |||
| 803 | if (fsp_get_version(psmouse, &version)) | ||
| 804 | return -ENODEV; | ||
| 805 | |||
| 806 | if (fsp_activate_protocol(psmouse)) | ||
| 807 | return -EIO; | ||
| 808 | |||
| 809 | return 0; | ||
| 810 | } | ||
| 811 | |||
| 812 | int fsp_init(struct psmouse *psmouse) | ||
| 813 | { | ||
| 814 | struct fsp_data *priv; | ||
| 815 | int ver, rev, buttons; | ||
| 816 | int error; | ||
| 817 | |||
| 818 | if (fsp_get_version(psmouse, &ver) || | ||
| 819 | fsp_get_revision(psmouse, &rev) || | ||
| 820 | fsp_get_buttons(psmouse, &buttons)) { | ||
| 821 | return -ENODEV; | ||
| 822 | } | ||
| 823 | |||
| 824 | printk(KERN_INFO | ||
| 825 | "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n", | ||
| 826 | ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7); | ||
| 827 | |||
| 828 | psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL); | ||
| 829 | if (!priv) | ||
| 830 | return -ENOMEM; | ||
| 831 | |||
| 832 | priv->ver = ver; | ||
| 833 | priv->rev = rev; | ||
| 834 | priv->buttons = buttons; | ||
| 835 | |||
| 836 | /* enable on-pad click by default */ | ||
| 837 | priv->flags |= FSPDRV_FLAG_EN_OPC; | ||
| 838 | |||
| 839 | /* Set up various supported input event bits */ | ||
| 840 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 841 | __set_bit(BTN_BACK, psmouse->dev->keybit); | ||
| 842 | __set_bit(BTN_FORWARD, psmouse->dev->keybit); | ||
| 843 | __set_bit(REL_WHEEL, psmouse->dev->relbit); | ||
| 844 | __set_bit(REL_HWHEEL, psmouse->dev->relbit); | ||
| 845 | |||
| 846 | psmouse->protocol_handler = fsp_process_byte; | ||
| 847 | psmouse->disconnect = fsp_disconnect; | ||
| 848 | psmouse->reconnect = fsp_reconnect; | ||
| 849 | psmouse->cleanup = fsp_reset; | ||
| 850 | psmouse->pktsize = 4; | ||
| 851 | |||
| 852 | /* set default packet output based on number of buttons we found */ | ||
| 853 | error = fsp_activate_protocol(psmouse); | ||
| 854 | if (error) | ||
| 855 | goto err_out; | ||
| 856 | |||
| 857 | error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj, | ||
| 858 | &fsp_attribute_group); | ||
| 859 | if (error) { | ||
| 860 | dev_err(&psmouse->ps2dev.serio->dev, | ||
| 861 | "Failed to create sysfs attributes (%d)", error); | ||
| 862 | goto err_out; | ||
| 863 | } | ||
| 864 | |||
| 865 | return 0; | ||
| 866 | |||
| 867 | err_out: | ||
| 868 | kfree(psmouse->private); | ||
| 869 | psmouse->private = NULL; | ||
| 870 | return error; | ||
| 871 | } | ||
diff --git a/usr/src/dkms_source_tree/sentelic.h b/usr/src/dkms_source_tree/sentelic.h new file mode 100644 index 0000000..ed1395a --- /dev/null +++ b/usr/src/dkms_source_tree/sentelic.h | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | /*- | ||
| 2 | * Finger Sensing Pad PS/2 mouse driver. | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd. | ||
| 5 | * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation. | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or | ||
| 8 | * modify it under the terms of the GNU General Public License | ||
| 9 | * as published by the Free Software Foundation; either version 2 | ||
| 10 | * of the License, or (at your option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #ifndef __SENTELIC_H | ||
| 23 | #define __SENTELIC_H | ||
| 24 | |||
| 25 | /* Finger-sensing Pad information registers */ | ||
| 26 | #define FSP_REG_DEVICE_ID 0x00 | ||
| 27 | #define FSP_REG_VERSION 0x01 | ||
| 28 | #define FSP_REG_REVISION 0x04 | ||
| 29 | #define FSP_REG_TMOD_STATUS1 0x0B | ||
| 30 | #define FSP_BIT_NO_ROTATION BIT(3) | ||
| 31 | #define FSP_REG_PAGE_CTRL 0x0F | ||
| 32 | |||
| 33 | /* Finger-sensing Pad control registers */ | ||
| 34 | #define FSP_REG_SYSCTL1 0x10 | ||
| 35 | #define FSP_BIT_EN_REG_CLK BIT(5) | ||
| 36 | #define FSP_REG_OPC_QDOWN 0x31 | ||
| 37 | #define FSP_BIT_EN_OPC_TAG BIT(7) | ||
| 38 | #define FSP_REG_OPTZ_XLO 0x34 | ||
| 39 | #define FSP_REG_OPTZ_XHI 0x35 | ||
| 40 | #define FSP_REG_OPTZ_YLO 0x36 | ||
| 41 | #define FSP_REG_OPTZ_YHI 0x37 | ||
| 42 | #define FSP_REG_SYSCTL5 0x40 | ||
| 43 | #define FSP_BIT_90_DEGREE BIT(0) | ||
| 44 | #define FSP_BIT_EN_MSID6 BIT(1) | ||
| 45 | #define FSP_BIT_EN_MSID7 BIT(2) | ||
| 46 | #define FSP_BIT_EN_MSID8 BIT(3) | ||
| 47 | #define FSP_BIT_EN_AUTO_MSID8 BIT(5) | ||
| 48 | #define FSP_BIT_EN_PKT_G0 BIT(6) | ||
| 49 | |||
| 50 | #define FSP_REG_ONPAD_CTL 0x43 | ||
| 51 | #define FSP_BIT_ONPAD_ENABLE BIT(0) | ||
| 52 | #define FSP_BIT_ONPAD_FBBB BIT(1) | ||
| 53 | #define FSP_BIT_FIX_VSCR BIT(3) | ||
| 54 | #define FSP_BIT_FIX_HSCR BIT(5) | ||
| 55 | #define FSP_BIT_DRAG_LOCK BIT(6) | ||
| 56 | |||
| 57 | /* Finger-sensing Pad packet formating related definitions */ | ||
| 58 | |||
| 59 | /* absolute packet type */ | ||
| 60 | #define FSP_PKT_TYPE_NORMAL (0x00) | ||
| 61 | #define FSP_PKT_TYPE_ABS (0x01) | ||
| 62 | #define FSP_PKT_TYPE_NOTIFY (0x02) | ||
| 63 | #define FSP_PKT_TYPE_NORMAL_OPC (0x03) | ||
| 64 | #define FSP_PKT_TYPE_SHIFT (6) | ||
| 65 | |||
| 66 | #ifdef __KERNEL__ | ||
| 67 | |||
| 68 | struct fsp_data { | ||
| 69 | unsigned char ver; /* hardware version */ | ||
| 70 | unsigned char rev; /* hardware revison */ | ||
| 71 | unsigned char buttons; /* Number of buttons */ | ||
| 72 | unsigned int flags; | ||
| 73 | #define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */ | ||
| 74 | |||
| 75 | bool vscroll; /* Vertical scroll zone enabled */ | ||
| 76 | bool hscroll; /* Horizontal scroll zone enabled */ | ||
| 77 | |||
| 78 | unsigned char last_reg; /* Last register we requested read from */ | ||
| 79 | unsigned char last_val; | ||
| 80 | }; | ||
| 81 | |||
| 82 | #ifdef CONFIG_MOUSE_PS2_SENTELIC | ||
| 83 | extern int fsp_detect(struct psmouse *psmouse, bool set_properties); | ||
| 84 | extern int fsp_init(struct psmouse *psmouse); | ||
| 85 | #else | ||
| 86 | inline int fsp_detect(struct psmouse *psmouse, bool set_properties) | ||
| 87 | { | ||
| 88 | return -ENOSYS; | ||
| 89 | } | ||
| 90 | inline int fsp_init(struct psmouse *psmouse) | ||
| 91 | { | ||
| 92 | return -ENOSYS; | ||
| 93 | } | ||
| 94 | #endif | ||
| 95 | |||
| 96 | #endif /* __KERNEL__ */ | ||
| 97 | |||
| 98 | #endif /* !__SENTELIC_H */ | ||
diff --git a/usr/src/dkms_source_tree/synaptics.c b/usr/src/dkms_source_tree/synaptics.c new file mode 100644 index 0000000..ae9891c --- /dev/null +++ b/usr/src/dkms_source_tree/synaptics.c | |||
| @@ -0,0 +1,863 @@ | |||
| 1 | /* | ||
| 2 | * Synaptics TouchPad PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * 2003 Dmitry Torokhov <dtor@mail.ru> | ||
| 5 | * Added support for pass-through port. Special thanks to Peter Berg Larsen | ||
| 6 | * for explaining various Synaptics quirks. | ||
| 7 | * | ||
| 8 | * 2003 Peter Osterlund <petero2@telia.com> | ||
| 9 | * Ported to 2.5 input device infrastructure. | ||
| 10 | * | ||
| 11 | * Copyright (C) 2001 Stefan Gmeiner <riddlebox@freesurf.ch> | ||
| 12 | * start merging tpconfig and gpm code to a xfree-input module | ||
| 13 | * adding some changes and extensions (ex. 3rd and 4th button) | ||
| 14 | * | ||
| 15 | * Copyright (c) 1997 C. Scott Ananian <cananian@alumni.priceton.edu> | ||
| 16 | * Copyright (c) 1998-2000 Bruce Kalk <kall@compass.com> | ||
| 17 | * code for the special synaptics commands (from the tpconfig-source) | ||
| 18 | * | ||
| 19 | * This program is free software; you can redistribute it and/or modify it | ||
| 20 | * under the terms of the GNU General Public License version 2 as published by | ||
| 21 | * the Free Software Foundation. | ||
| 22 | * | ||
| 23 | * Trademarks are the property of their respective owners. | ||
| 24 | */ | ||
| 25 | |||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/dmi.h> | ||
| 28 | #include <linux/input.h> | ||
| 29 | #include <linux/serio.h> | ||
| 30 | #include <linux/libps2.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include "psmouse.h" | ||
| 33 | #include "synaptics.h" | ||
| 34 | |||
| 35 | /* | ||
| 36 | * The x/y limits are taken from the Synaptics TouchPad interfacing Guide, | ||
| 37 | * section 2.3.2, which says that they should be valid regardless of the | ||
| 38 | * actual size of the sensor. | ||
| 39 | * Note that newer firmware allows querying device for maximum useable | ||
| 40 | * coordinates. | ||
| 41 | */ | ||
| 42 | #define XMIN_NOMINAL 1472 | ||
| 43 | #define XMAX_NOMINAL 5472 | ||
| 44 | #define YMIN_NOMINAL 1408 | ||
| 45 | #define YMAX_NOMINAL 4448 | ||
| 46 | |||
| 47 | |||
| 48 | /***************************************************************************** | ||
| 49 | * Stuff we need even when we do not want native Synaptics support | ||
| 50 | ****************************************************************************/ | ||
| 51 | |||
| 52 | /* | ||
| 53 | * Set the synaptics touchpad mode byte by special commands | ||
| 54 | */ | ||
| 55 | static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode) | ||
| 56 | { | ||
| 57 | unsigned char param[1]; | ||
| 58 | |||
| 59 | if (psmouse_sliced_command(psmouse, mode)) | ||
| 60 | return -1; | ||
| 61 | param[0] = SYN_PS_SET_MODE2; | ||
| 62 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_SETRATE)) | ||
| 63 | return -1; | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | int synaptics_detect(struct psmouse *psmouse, bool set_properties) | ||
| 68 | { | ||
| 69 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 70 | unsigned char param[4]; | ||
| 71 | |||
| 72 | param[0] = 0; | ||
| 73 | |||
| 74 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 75 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 76 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 77 | ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); | ||
| 78 | ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); | ||
| 79 | |||
| 80 | if (param[1] != 0x47) | ||
| 81 | return -ENODEV; | ||
| 82 | |||
| 83 | if (set_properties) { | ||
| 84 | psmouse->vendor = "Synaptics"; | ||
| 85 | psmouse->name = "TouchPad"; | ||
| 86 | } | ||
| 87 | |||
| 88 | return 0; | ||
| 89 | } | ||
| 90 | |||
| 91 | void synaptics_reset(struct psmouse *psmouse) | ||
| 92 | { | ||
| 93 | /* reset touchpad back to relative mode, gestures enabled */ | ||
| 94 | synaptics_mode_cmd(psmouse, 0); | ||
| 95 | } | ||
| 96 | |||
| 97 | #ifdef CONFIG_MOUSE_PS2_SYNAPTICS | ||
| 98 | |||
| 99 | /***************************************************************************** | ||
| 100 | * Synaptics communications functions | ||
| 101 | ****************************************************************************/ | ||
| 102 | |||
| 103 | /* | ||
| 104 | * Send a command to the synpatics touchpad by special commands | ||
| 105 | */ | ||
| 106 | static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param) | ||
| 107 | { | ||
| 108 | if (psmouse_sliced_command(psmouse, c)) | ||
| 109 | return -1; | ||
| 110 | if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO)) | ||
| 111 | return -1; | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | /* | ||
| 116 | * Read the model-id bytes from the touchpad | ||
| 117 | * see also SYN_MODEL_* macros | ||
| 118 | */ | ||
| 119 | static int synaptics_model_id(struct psmouse *psmouse) | ||
| 120 | { | ||
| 121 | struct synaptics_data *priv = psmouse->private; | ||
| 122 | unsigned char mi[3]; | ||
| 123 | |||
| 124 | if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi)) | ||
| 125 | return -1; | ||
| 126 | priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2]; | ||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* | ||
| 131 | * Read the capability-bits from the touchpad | ||
| 132 | * see also the SYN_CAP_* macros | ||
| 133 | */ | ||
| 134 | static int synaptics_capability(struct psmouse *psmouse) | ||
| 135 | { | ||
| 136 | struct synaptics_data *priv = psmouse->private; | ||
| 137 | unsigned char cap[3]; | ||
| 138 | |||
| 139 | if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap)) | ||
| 140 | return -1; | ||
| 141 | priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | ||
| 142 | priv->ext_cap = priv->ext_cap_0c = 0; | ||
| 143 | |||
| 144 | /* | ||
| 145 | * Older firmwares had submodel ID fixed to 0x47 | ||
| 146 | */ | ||
| 147 | if (SYN_ID_FULL(priv->identity) < 0x705 && | ||
| 148 | SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) { | ||
| 149 | return -1; | ||
| 150 | } | ||
| 151 | |||
| 152 | /* | ||
| 153 | * Unless capExtended is set the rest of the flags should be ignored | ||
| 154 | */ | ||
| 155 | if (!SYN_CAP_EXTENDED(priv->capabilities)) | ||
| 156 | priv->capabilities = 0; | ||
| 157 | |||
| 158 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) { | ||
| 159 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) { | ||
| 160 | printk(KERN_ERR "Synaptics claims to have extended capabilities," | ||
| 161 | " but I'm not able to read them.\n"); | ||
| 162 | } else { | ||
| 163 | priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | ||
| 164 | |||
| 165 | /* | ||
| 166 | * if nExtBtn is greater than 8 it should be considered | ||
| 167 | * invalid and treated as 0 | ||
| 168 | */ | ||
| 169 | if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8) | ||
| 170 | priv->ext_cap &= 0xff0fff; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) { | ||
| 175 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) { | ||
| 176 | printk(KERN_ERR "Synaptics claims to have extended capability 0x0c," | ||
| 177 | " but I'm not able to read it.\n"); | ||
| 178 | } else { | ||
| 179 | priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2]; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | return 0; | ||
| 184 | } | ||
| 185 | |||
| 186 | /* | ||
| 187 | * Identify Touchpad | ||
| 188 | * See also the SYN_ID_* macros | ||
| 189 | */ | ||
| 190 | static int synaptics_identify(struct psmouse *psmouse) | ||
| 191 | { | ||
| 192 | struct synaptics_data *priv = psmouse->private; | ||
| 193 | unsigned char id[3]; | ||
| 194 | |||
| 195 | if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id)) | ||
| 196 | return -1; | ||
| 197 | priv->identity = (id[0]<<16) | (id[1]<<8) | id[2]; | ||
| 198 | if (SYN_ID_IS_SYNAPTICS(priv->identity)) | ||
| 199 | return 0; | ||
| 200 | return -1; | ||
| 201 | } | ||
| 202 | |||
| 203 | /* | ||
| 204 | * Read touchpad resolution and maximum reported coordinates | ||
| 205 | * Resolution is left zero if touchpad does not support the query | ||
| 206 | */ | ||
| 207 | static int synaptics_resolution(struct psmouse *psmouse) | ||
| 208 | { | ||
| 209 | struct synaptics_data *priv = psmouse->private; | ||
| 210 | unsigned char res[3]; | ||
| 211 | unsigned char max[3]; | ||
| 212 | |||
| 213 | if (SYN_ID_MAJOR(priv->identity) < 4) | ||
| 214 | return 0; | ||
| 215 | |||
| 216 | if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res) == 0) { | ||
| 217 | if (res[0] != 0 && (res[1] & 0x80) && res[2] != 0) { | ||
| 218 | priv->x_res = res[0]; /* x resolution in units/mm */ | ||
| 219 | priv->y_res = res[2]; /* y resolution in units/mm */ | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 && | ||
| 224 | SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) { | ||
| 225 | if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_DIMENSIONS, max)) { | ||
| 226 | printk(KERN_ERR "Synaptics claims to have dimensions query," | ||
| 227 | " but I'm not able to read it.\n"); | ||
| 228 | } else { | ||
| 229 | priv->x_max = (max[0] << 5) | ((max[1] & 0x0f) << 1); | ||
| 230 | priv->y_max = (max[2] << 5) | ((max[1] & 0xf0) >> 3); | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | return 0; | ||
| 235 | } | ||
| 236 | |||
| 237 | static int synaptics_query_hardware(struct psmouse *psmouse) | ||
| 238 | { | ||
| 239 | if (synaptics_identify(psmouse)) | ||
| 240 | return -1; | ||
| 241 | if (synaptics_model_id(psmouse)) | ||
| 242 | return -1; | ||
| 243 | if (synaptics_capability(psmouse)) | ||
| 244 | return -1; | ||
| 245 | if (synaptics_resolution(psmouse)) | ||
| 246 | return -1; | ||
| 247 | |||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static int synaptics_set_absolute_mode(struct psmouse *psmouse) | ||
| 252 | { | ||
| 253 | struct synaptics_data *priv = psmouse->private; | ||
| 254 | |||
| 255 | priv->mode = SYN_BIT_ABSOLUTE_MODE; | ||
| 256 | if (SYN_ID_MAJOR(priv->identity) >= 4) | ||
| 257 | priv->mode |= SYN_BIT_DISABLE_GESTURE; | ||
| 258 | if (SYN_CAP_EXTENDED(priv->capabilities)) | ||
| 259 | priv->mode |= SYN_BIT_W_MODE; | ||
| 260 | |||
| 261 | if (synaptics_mode_cmd(psmouse, priv->mode)) | ||
| 262 | return -1; | ||
| 263 | |||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | static void synaptics_set_rate(struct psmouse *psmouse, unsigned int rate) | ||
| 268 | { | ||
| 269 | struct synaptics_data *priv = psmouse->private; | ||
| 270 | |||
| 271 | if (rate >= 80) { | ||
| 272 | priv->mode |= SYN_BIT_HIGH_RATE; | ||
| 273 | psmouse->rate = 80; | ||
| 274 | } else { | ||
| 275 | priv->mode &= ~SYN_BIT_HIGH_RATE; | ||
| 276 | psmouse->rate = 40; | ||
| 277 | } | ||
| 278 | |||
| 279 | synaptics_mode_cmd(psmouse, priv->mode); | ||
| 280 | } | ||
| 281 | |||
| 282 | /***************************************************************************** | ||
| 283 | * Synaptics pass-through PS/2 port support | ||
| 284 | ****************************************************************************/ | ||
| 285 | static int synaptics_pt_write(struct serio *serio, unsigned char c) | ||
| 286 | { | ||
| 287 | struct psmouse *parent = serio_get_drvdata(serio->parent); | ||
| 288 | char rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */ | ||
| 289 | |||
| 290 | if (psmouse_sliced_command(parent, c)) | ||
| 291 | return -1; | ||
| 292 | if (ps2_command(&parent->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE)) | ||
| 293 | return -1; | ||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | static inline int synaptics_is_pt_packet(unsigned char *buf) | ||
| 298 | { | ||
| 299 | return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4; | ||
| 300 | } | ||
| 301 | |||
| 302 | static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet) | ||
| 303 | { | ||
| 304 | struct psmouse *child = serio_get_drvdata(ptport); | ||
| 305 | |||
| 306 | if (child && child->state == PSMOUSE_ACTIVATED) { | ||
| 307 | serio_interrupt(ptport, packet[1], 0); | ||
| 308 | serio_interrupt(ptport, packet[4], 0); | ||
| 309 | serio_interrupt(ptport, packet[5], 0); | ||
| 310 | if (child->pktsize == 4) | ||
| 311 | serio_interrupt(ptport, packet[2], 0); | ||
| 312 | } else | ||
| 313 | serio_interrupt(ptport, packet[1], 0); | ||
| 314 | } | ||
| 315 | |||
| 316 | static void synaptics_pt_activate(struct psmouse *psmouse) | ||
| 317 | { | ||
| 318 | struct serio *ptport = psmouse->ps2dev.serio->child; | ||
| 319 | struct psmouse *child = serio_get_drvdata(ptport); | ||
| 320 | struct synaptics_data *priv = psmouse->private; | ||
| 321 | |||
| 322 | /* adjust the touchpad to child's choice of protocol */ | ||
| 323 | if (child) { | ||
| 324 | if (child->pktsize == 4) | ||
| 325 | priv->mode |= SYN_BIT_FOUR_BYTE_CLIENT; | ||
| 326 | else | ||
| 327 | priv->mode &= ~SYN_BIT_FOUR_BYTE_CLIENT; | ||
| 328 | |||
| 329 | if (synaptics_mode_cmd(psmouse, priv->mode)) | ||
| 330 | printk(KERN_INFO "synaptics: failed to switch guest protocol\n"); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | static void synaptics_pt_create(struct psmouse *psmouse) | ||
| 335 | { | ||
| 336 | struct serio *serio; | ||
| 337 | |||
| 338 | serio = kzalloc(sizeof(struct serio), GFP_KERNEL); | ||
| 339 | if (!serio) { | ||
| 340 | printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); | ||
| 341 | return; | ||
| 342 | } | ||
| 343 | |||
| 344 | serio->id.type = SERIO_PS_PSTHRU; | ||
| 345 | strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); | ||
| 346 | strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); | ||
| 347 | serio->write = synaptics_pt_write; | ||
| 348 | serio->parent = psmouse->ps2dev.serio; | ||
| 349 | |||
| 350 | psmouse->pt_activate = synaptics_pt_activate; | ||
| 351 | |||
| 352 | printk(KERN_INFO "serio: %s port at %s\n", serio->name, psmouse->phys); | ||
| 353 | serio_register_port(serio); | ||
| 354 | } | ||
| 355 | |||
| 356 | /***************************************************************************** | ||
| 357 | * Functions to interpret the absolute mode packets | ||
| 358 | ****************************************************************************/ | ||
| 359 | |||
| 360 | /* left and right clickpad button ranges; | ||
| 361 | * the gap between them is interpreted as a middle-button click | ||
| 362 | */ | ||
| 363 | #define CLICKPAD_LEFT_BTN_X \ | ||
| 364 | ((XMAX_NOMINAL - XMIN_NOMINAL) * 2 / 5 + XMIN_NOMINAL) | ||
| 365 | #define CLICKPAD_RIGHT_BTN_X \ | ||
| 366 | ((XMAX_NOMINAL - XMIN_NOMINAL) * 3 / 5 + XMIN_NOMINAL) | ||
| 367 | |||
| 368 | /* handle clickpad events */ | ||
| 369 | static void clickpad_process_packet(struct synaptics_data *priv, | ||
| 370 | struct synaptics_hw_state *hw) | ||
| 371 | { | ||
| 372 | /* clickpad mode reports Y range from 0 to YMAX_NOMINAL, | ||
| 373 | * where the area Y < YMIN_NOMINAL is used as click buttons | ||
| 374 | */ | ||
| 375 | if (hw->y < YMIN_NOMINAL) { | ||
| 376 | /* button area */ | ||
| 377 | hw->z = 0; /* don't move pointer */ | ||
| 378 | /* clickpad reports only the middle button, and we need | ||
| 379 | * to fake left/right buttons depending on the touch position | ||
| 380 | */ | ||
| 381 | if (hw->middle) { /* clicked? */ | ||
| 382 | hw->middle = 0; | ||
| 383 | if (hw->x < CLICKPAD_LEFT_BTN_X) | ||
| 384 | hw->left = 1; | ||
| 385 | else if (hw->x > CLICKPAD_RIGHT_BTN_X) | ||
| 386 | hw->right = 1; | ||
| 387 | else | ||
| 388 | hw->middle = 1; | ||
| 389 | } | ||
| 390 | } else if (hw->middle) { | ||
| 391 | /* dragging */ | ||
| 392 | hw->left = priv->prev_hw.left; | ||
| 393 | hw->right = priv->prev_hw.right; | ||
| 394 | hw->middle = priv->prev_hw.middle; | ||
| 395 | } | ||
| 396 | priv->prev_hw = *hw; | ||
| 397 | } | ||
| 398 | |||
| 399 | static void synaptics_parse_hw_state(unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) | ||
| 400 | { | ||
| 401 | memset(hw, 0, sizeof(struct synaptics_hw_state)); | ||
| 402 | |||
| 403 | if (SYN_MODEL_NEWABS(priv->model_id)) { | ||
| 404 | hw->x = (((buf[3] & 0x10) << 8) | | ||
| 405 | ((buf[1] & 0x0f) << 8) | | ||
| 406 | buf[4]); | ||
| 407 | hw->y = (((buf[3] & 0x20) << 7) | | ||
| 408 | ((buf[1] & 0xf0) << 4) | | ||
| 409 | buf[5]); | ||
| 410 | |||
| 411 | hw->z = buf[2]; | ||
| 412 | hw->w = (((buf[0] & 0x30) >> 2) | | ||
| 413 | ((buf[0] & 0x04) >> 1) | | ||
| 414 | ((buf[3] & 0x04) >> 2)); | ||
| 415 | |||
| 416 | hw->left = (buf[0] & 0x01) ? 1 : 0; | ||
| 417 | hw->right = (buf[0] & 0x02) ? 1 : 0; | ||
| 418 | |||
| 419 | if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { | ||
| 420 | /* | ||
| 421 | * Clickpad's button is transmitted as middle button, | ||
| 422 | * however, since it is primary button, we will report | ||
| 423 | * it as BTN_LEFT. | ||
| 424 | */ | ||
| 425 | hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | ||
| 426 | |||
| 427 | } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | ||
| 428 | hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | ||
| 429 | if (hw->w == 2) | ||
| 430 | hw->scroll = (signed char)(buf[1]); | ||
| 431 | } | ||
| 432 | |||
| 433 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | ||
| 434 | hw->up = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0; | ||
| 435 | hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; | ||
| 436 | } | ||
| 437 | |||
| 438 | if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) && | ||
| 439 | ((buf[0] ^ buf[3]) & 0x02)) { | ||
| 440 | switch (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) & ~0x01) { | ||
| 441 | default: | ||
| 442 | /* | ||
| 443 | * if nExtBtn is greater than 8 it should be | ||
| 444 | * considered invalid and treated as 0 | ||
| 445 | */ | ||
| 446 | break; | ||
| 447 | case 8: | ||
| 448 | hw->ext_buttons |= ((buf[5] & 0x08)) ? 0x80 : 0; | ||
| 449 | hw->ext_buttons |= ((buf[4] & 0x08)) ? 0x40 : 0; | ||
| 450 | case 6: | ||
| 451 | hw->ext_buttons |= ((buf[5] & 0x04)) ? 0x20 : 0; | ||
| 452 | hw->ext_buttons |= ((buf[4] & 0x04)) ? 0x10 : 0; | ||
| 453 | case 4: | ||
| 454 | hw->ext_buttons |= ((buf[5] & 0x02)) ? 0x08 : 0; | ||
| 455 | hw->ext_buttons |= ((buf[4] & 0x02)) ? 0x04 : 0; | ||
| 456 | case 2: | ||
| 457 | hw->ext_buttons |= ((buf[5] & 0x01)) ? 0x02 : 0; | ||
| 458 | hw->ext_buttons |= ((buf[4] & 0x01)) ? 0x01 : 0; | ||
| 459 | } | ||
| 460 | } | ||
| 461 | } else { | ||
| 462 | hw->x = (((buf[1] & 0x1f) << 8) | buf[2]); | ||
| 463 | hw->y = (((buf[4] & 0x1f) << 8) | buf[5]); | ||
| 464 | |||
| 465 | hw->z = (((buf[0] & 0x30) << 2) | (buf[3] & 0x3F)); | ||
| 466 | hw->w = (((buf[1] & 0x80) >> 4) | ((buf[0] & 0x04) >> 1)); | ||
| 467 | |||
| 468 | hw->left = (buf[0] & 0x01) ? 1 : 0; | ||
| 469 | hw->right = (buf[0] & 0x02) ? 1 : 0; | ||
| 470 | } | ||
| 471 | } | ||
| 472 | |||
| 473 | /* | ||
| 474 | * called for each full received packet from the touchpad | ||
| 475 | */ | ||
| 476 | static void synaptics_process_packet(struct psmouse *psmouse) | ||
| 477 | { | ||
| 478 | struct input_dev *dev = psmouse->dev; | ||
| 479 | struct synaptics_data *priv = psmouse->private; | ||
| 480 | struct synaptics_hw_state hw; | ||
| 481 | int num_fingers; | ||
| 482 | int finger_width; | ||
| 483 | int i; | ||
| 484 | |||
| 485 | synaptics_parse_hw_state(psmouse->packet, priv, &hw); | ||
| 486 | |||
| 487 | if (SYN_CAP_CLICKPAD(priv->ext_cap)) | ||
| 488 | clickpad_process_packet(priv, &hw); | ||
| 489 | |||
| 490 | if (hw.scroll) { | ||
| 491 | priv->scroll += hw.scroll; | ||
| 492 | |||
| 493 | while (priv->scroll >= 4) { | ||
| 494 | input_report_key(dev, BTN_BACK, !hw.down); | ||
| 495 | input_sync(dev); | ||
| 496 | input_report_key(dev, BTN_BACK, hw.down); | ||
| 497 | input_sync(dev); | ||
| 498 | priv->scroll -= 4; | ||
| 499 | } | ||
| 500 | while (priv->scroll <= -4) { | ||
| 501 | input_report_key(dev, BTN_FORWARD, !hw.up); | ||
| 502 | input_sync(dev); | ||
| 503 | input_report_key(dev, BTN_FORWARD, hw.up); | ||
| 504 | input_sync(dev); | ||
| 505 | priv->scroll += 4; | ||
| 506 | } | ||
| 507 | return; | ||
| 508 | } | ||
| 509 | |||
| 510 | if (hw.z > 0) { | ||
| 511 | num_fingers = 1; | ||
| 512 | finger_width = 5; | ||
| 513 | if (SYN_CAP_EXTENDED(priv->capabilities)) { | ||
| 514 | switch (hw.w) { | ||
| 515 | case 0 ... 1: | ||
| 516 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) | ||
| 517 | num_fingers = hw.w + 2; | ||
| 518 | break; | ||
| 519 | case 2: | ||
| 520 | if (SYN_MODEL_PEN(priv->model_id)) | ||
| 521 | ; /* Nothing, treat a pen as a single finger */ | ||
| 522 | break; | ||
| 523 | case 4 ... 15: | ||
| 524 | if (SYN_CAP_PALMDETECT(priv->capabilities)) | ||
| 525 | finger_width = hw.w; | ||
| 526 | break; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | } else { | ||
| 530 | num_fingers = 0; | ||
| 531 | finger_width = 0; | ||
| 532 | } | ||
| 533 | |||
| 534 | /* Post events | ||
| 535 | * BTN_TOUCH has to be first as mousedev relies on it when doing | ||
| 536 | * absolute -> relative conversion | ||
| 537 | */ | ||
| 538 | if (hw.z > 30) input_report_key(dev, BTN_TOUCH, 1); | ||
| 539 | if (hw.z < 25) input_report_key(dev, BTN_TOUCH, 0); | ||
| 540 | |||
| 541 | if (hw.z > 0) { | ||
| 542 | input_report_abs(dev, ABS_X, hw.x); | ||
| 543 | input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y); | ||
| 544 | } | ||
| 545 | input_report_abs(dev, ABS_PRESSURE, hw.z); | ||
| 546 | |||
| 547 | input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); | ||
| 548 | input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); | ||
| 549 | input_report_key(dev, BTN_LEFT, hw.left); | ||
| 550 | input_report_key(dev, BTN_RIGHT, hw.right); | ||
| 551 | |||
| 552 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) { | ||
| 553 | input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); | ||
| 554 | input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); | ||
| 555 | } | ||
| 556 | |||
| 557 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | ||
| 558 | input_report_key(dev, BTN_MIDDLE, hw.middle); | ||
| 559 | |||
| 560 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | ||
| 561 | input_report_key(dev, BTN_FORWARD, hw.up); | ||
| 562 | input_report_key(dev, BTN_BACK, hw.down); | ||
| 563 | } | ||
| 564 | |||
| 565 | for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | ||
| 566 | input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i)); | ||
| 567 | |||
| 568 | input_sync(dev); | ||
| 569 | } | ||
| 570 | |||
| 571 | static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type) | ||
| 572 | { | ||
| 573 | static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 }; | ||
| 574 | static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 }; | ||
| 575 | static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 }; | ||
| 576 | static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 }; | ||
| 577 | static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 }; | ||
| 578 | |||
| 579 | if (idx < 0 || idx > 4) | ||
| 580 | return 0; | ||
| 581 | |||
| 582 | switch (pkt_type) { | ||
| 583 | |||
| 584 | case SYN_NEWABS: | ||
| 585 | case SYN_NEWABS_RELAXED: | ||
| 586 | return (packet[idx] & newabs_rel_mask[idx]) == newabs_rslt[idx]; | ||
| 587 | |||
| 588 | case SYN_NEWABS_STRICT: | ||
| 589 | return (packet[idx] & newabs_mask[idx]) == newabs_rslt[idx]; | ||
| 590 | |||
| 591 | case SYN_OLDABS: | ||
| 592 | return (packet[idx] & oldabs_mask[idx]) == oldabs_rslt[idx]; | ||
| 593 | |||
| 594 | default: | ||
| 595 | printk(KERN_ERR "synaptics: unknown packet type %d\n", pkt_type); | ||
| 596 | return 0; | ||
| 597 | } | ||
| 598 | } | ||
| 599 | |||
| 600 | static unsigned char synaptics_detect_pkt_type(struct psmouse *psmouse) | ||
| 601 | { | ||
| 602 | int i; | ||
| 603 | |||
| 604 | for (i = 0; i < 5; i++) | ||
| 605 | if (!synaptics_validate_byte(psmouse->packet, i, SYN_NEWABS_STRICT)) { | ||
| 606 | printk(KERN_INFO "synaptics: using relaxed packet validation\n"); | ||
| 607 | return SYN_NEWABS_RELAXED; | ||
| 608 | } | ||
| 609 | |||
| 610 | return SYN_NEWABS_STRICT; | ||
| 611 | } | ||
| 612 | |||
| 613 | static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse) | ||
| 614 | { | ||
| 615 | struct synaptics_data *priv = psmouse->private; | ||
| 616 | |||
| 617 | if (psmouse->pktcnt >= 6) { /* Full packet received */ | ||
| 618 | if (unlikely(priv->pkt_type == SYN_NEWABS)) | ||
| 619 | priv->pkt_type = synaptics_detect_pkt_type(psmouse); | ||
| 620 | |||
| 621 | if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) { | ||
| 622 | if (psmouse->ps2dev.serio->child) | ||
| 623 | synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet); | ||
| 624 | } else | ||
| 625 | synaptics_process_packet(psmouse); | ||
| 626 | |||
| 627 | return PSMOUSE_FULL_PACKET; | ||
| 628 | } | ||
| 629 | |||
| 630 | return synaptics_validate_byte(psmouse->packet, psmouse->pktcnt - 1, priv->pkt_type) ? | ||
| 631 | PSMOUSE_GOOD_DATA : PSMOUSE_BAD_DATA; | ||
| 632 | } | ||
| 633 | |||
| 634 | /***************************************************************************** | ||
| 635 | * Driver initialization/cleanup functions | ||
| 636 | ****************************************************************************/ | ||
| 637 | static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | ||
| 638 | { | ||
| 639 | int i; | ||
| 640 | |||
| 641 | __set_bit(EV_ABS, dev->evbit); | ||
| 642 | input_set_abs_params(dev, ABS_X, | ||
| 643 | XMIN_NOMINAL, priv->x_max ?: XMAX_NOMINAL, 0, 0); | ||
| 644 | input_set_abs_params(dev, ABS_Y, | ||
| 645 | YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0); | ||
| 646 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); | ||
| 647 | __set_bit(ABS_TOOL_WIDTH, dev->absbit); | ||
| 648 | |||
| 649 | __set_bit(EV_KEY, dev->evbit); | ||
| 650 | __set_bit(BTN_TOUCH, dev->keybit); | ||
| 651 | __set_bit(BTN_TOOL_FINGER, dev->keybit); | ||
| 652 | __set_bit(BTN_LEFT, dev->keybit); | ||
| 653 | __set_bit(BTN_RIGHT, dev->keybit); | ||
| 654 | |||
| 655 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) { | ||
| 656 | __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); | ||
| 657 | __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit); | ||
| 658 | } | ||
| 659 | |||
| 660 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | ||
| 661 | __set_bit(BTN_MIDDLE, dev->keybit); | ||
| 662 | |||
| 663 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities) || | ||
| 664 | SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) { | ||
| 665 | __set_bit(BTN_FORWARD, dev->keybit); | ||
| 666 | __set_bit(BTN_BACK, dev->keybit); | ||
| 667 | } | ||
| 668 | |||
| 669 | for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | ||
| 670 | __set_bit(BTN_0 + i, dev->keybit); | ||
| 671 | |||
| 672 | __clear_bit(EV_REL, dev->evbit); | ||
| 673 | __clear_bit(REL_X, dev->relbit); | ||
| 674 | __clear_bit(REL_Y, dev->relbit); | ||
| 675 | |||
| 676 | dev->absres[ABS_X] = priv->x_res; | ||
| 677 | dev->absres[ABS_Y] = priv->y_res; | ||
| 678 | |||
| 679 | if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) { | ||
| 680 | /* Clickpads report only left button */ | ||
| 681 | __clear_bit(BTN_RIGHT, dev->keybit); | ||
| 682 | __clear_bit(BTN_MIDDLE, dev->keybit); | ||
| 683 | } | ||
| 684 | } | ||
| 685 | |||
| 686 | static void synaptics_disconnect(struct psmouse *psmouse) | ||
| 687 | { | ||
| 688 | synaptics_reset(psmouse); | ||
| 689 | kfree(psmouse->private); | ||
| 690 | psmouse->private = NULL; | ||
| 691 | } | ||
| 692 | |||
| 693 | static int synaptics_reconnect(struct psmouse *psmouse) | ||
| 694 | { | ||
| 695 | struct synaptics_data *priv = psmouse->private; | ||
| 696 | struct synaptics_data old_priv = *priv; | ||
| 697 | |||
| 698 | psmouse_reset(psmouse); | ||
| 699 | |||
| 700 | if (synaptics_detect(psmouse, 0)) | ||
| 701 | return -1; | ||
| 702 | |||
| 703 | if (synaptics_query_hardware(psmouse)) { | ||
| 704 | printk(KERN_ERR "Unable to query Synaptics hardware.\n"); | ||
| 705 | return -1; | ||
| 706 | } | ||
| 707 | |||
| 708 | if (old_priv.identity != priv->identity || | ||
| 709 | old_priv.model_id != priv->model_id || | ||
| 710 | old_priv.capabilities != priv->capabilities || | ||
| 711 | old_priv.ext_cap != priv->ext_cap) | ||
| 712 | return -1; | ||
| 713 | |||
| 714 | if (synaptics_set_absolute_mode(psmouse)) { | ||
| 715 | printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); | ||
| 716 | return -1; | ||
| 717 | } | ||
| 718 | |||
| 719 | return 0; | ||
| 720 | } | ||
| 721 | |||
| 722 | static bool impaired_toshiba_kbc; | ||
| 723 | |||
| 724 | static const struct dmi_system_id __initconst toshiba_dmi_table[] = { | ||
| 725 | #if defined(CONFIG_DMI) && defined(CONFIG_X86) | ||
| 726 | { | ||
| 727 | /* Toshiba Satellite */ | ||
| 728 | .matches = { | ||
| 729 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | ||
| 730 | DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), | ||
| 731 | }, | ||
| 732 | }, | ||
| 733 | { | ||
| 734 | /* Toshiba Dynabook */ | ||
| 735 | .matches = { | ||
| 736 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | ||
| 737 | DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), | ||
| 738 | }, | ||
| 739 | }, | ||
| 740 | { | ||
| 741 | /* Toshiba Portege M300 */ | ||
| 742 | .matches = { | ||
| 743 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | ||
| 744 | DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), | ||
| 745 | }, | ||
| 746 | |||
| 747 | }, | ||
| 748 | { | ||
| 749 | /* Toshiba Portege M300 */ | ||
| 750 | .matches = { | ||
| 751 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | ||
| 752 | DMI_MATCH(DMI_PRODUCT_NAME, "Portable PC"), | ||
| 753 | DMI_MATCH(DMI_PRODUCT_VERSION, "Version 1.0"), | ||
| 754 | }, | ||
| 755 | |||
| 756 | }, | ||
| 757 | { } | ||
| 758 | #endif | ||
| 759 | }; | ||
| 760 | |||
| 761 | void __init synaptics_module_init(void) | ||
| 762 | { | ||
| 763 | impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); | ||
| 764 | } | ||
| 765 | |||
| 766 | int synaptics_init(struct psmouse *psmouse) | ||
| 767 | { | ||
| 768 | struct synaptics_data *priv; | ||
| 769 | |||
| 770 | psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); | ||
| 771 | if (!priv) | ||
| 772 | return -1; | ||
| 773 | |||
| 774 | psmouse_reset(psmouse); | ||
| 775 | |||
| 776 | if (synaptics_query_hardware(psmouse)) { | ||
| 777 | printk(KERN_ERR "Unable to query Synaptics hardware.\n"); | ||
| 778 | goto init_fail; | ||
| 779 | } | ||
| 780 | |||
| 781 | if (synaptics_set_absolute_mode(psmouse)) { | ||
| 782 | printk(KERN_ERR "Unable to initialize Synaptics hardware.\n"); | ||
| 783 | goto init_fail; | ||
| 784 | } | ||
| 785 | |||
| 786 | priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS; | ||
| 787 | |||
| 788 | printk(KERN_INFO "Synaptics Touchpad, model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n", | ||
| 789 | SYN_ID_MODEL(priv->identity), | ||
| 790 | SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity), | ||
| 791 | priv->model_id, priv->capabilities, priv->ext_cap, priv->ext_cap_0c); | ||
| 792 | |||
| 793 | if (SYN_CAP_CLICKPAD(priv->ext_cap)) { | ||
| 794 | printk(KERN_INFO "Synaptics: Clickpad mode enabled\n"); | ||
| 795 | /* force to enable the middle button */ | ||
| 796 | priv->capabilities |= (1 << 18); | ||
| 797 | } | ||
| 798 | |||
| 799 | set_input_params(psmouse->dev, priv); | ||
| 800 | |||
| 801 | /* | ||
| 802 | * Encode touchpad model so that it can be used to set | ||
| 803 | * input device->id.version and be visible to userspace. | ||
| 804 | * Because version is __u16 we have to drop something. | ||
| 805 | * Hardware info bits seem to be good candidates as they | ||
| 806 | * are documented to be for Synaptics corp. internal use. | ||
| 807 | */ | ||
| 808 | psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) | | ||
| 809 | (priv->model_id & 0x000000ff); | ||
| 810 | |||
| 811 | psmouse->protocol_handler = synaptics_process_byte; | ||
| 812 | psmouse->set_rate = synaptics_set_rate; | ||
| 813 | psmouse->disconnect = synaptics_disconnect; | ||
| 814 | psmouse->reconnect = synaptics_reconnect; | ||
| 815 | psmouse->cleanup = synaptics_reset; | ||
| 816 | psmouse->pktsize = 6; | ||
| 817 | /* Synaptics can usually stay in sync without extra help */ | ||
| 818 | psmouse->resync_time = 0; | ||
| 819 | |||
| 820 | if (SYN_CAP_PASS_THROUGH(priv->capabilities)) | ||
| 821 | synaptics_pt_create(psmouse); | ||
| 822 | |||
| 823 | /* | ||
| 824 | * Toshiba's KBC seems to have trouble handling data from | ||
| 825 | * Synaptics as full rate, switch to lower rate which is roughly | ||
| 826 | * thye same as rate of standard PS/2 mouse. | ||
| 827 | */ | ||
| 828 | if (psmouse->rate >= 80 && impaired_toshiba_kbc) { | ||
| 829 | printk(KERN_INFO "synaptics: Toshiba %s detected, limiting rate to 40pps.\n", | ||
| 830 | dmi_get_system_info(DMI_PRODUCT_NAME)); | ||
| 831 | psmouse->rate = 40; | ||
| 832 | } | ||
| 833 | |||
| 834 | return 0; | ||
| 835 | |||
| 836 | init_fail: | ||
| 837 | kfree(priv); | ||
| 838 | return -1; | ||
| 839 | } | ||
| 840 | |||
| 841 | bool synaptics_supported(void) | ||
| 842 | { | ||
| 843 | return true; | ||
| 844 | } | ||
| 845 | |||
| 846 | #else /* CONFIG_MOUSE_PS2_SYNAPTICS */ | ||
| 847 | |||
| 848 | void __init synaptics_module_init(void) | ||
| 849 | { | ||
| 850 | } | ||
| 851 | |||
| 852 | int synaptics_init(struct psmouse *psmouse) | ||
| 853 | { | ||
| 854 | return -ENOSYS; | ||
| 855 | } | ||
| 856 | |||
| 857 | bool synaptics_supported(void) | ||
| 858 | { | ||
| 859 | return false; | ||
| 860 | } | ||
| 861 | |||
| 862 | #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */ | ||
| 863 | |||
diff --git a/usr/src/dkms_source_tree/synaptics.h b/usr/src/dkms_source_tree/synaptics.h new file mode 100644 index 0000000..80907d0 --- /dev/null +++ b/usr/src/dkms_source_tree/synaptics.h | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | /* | ||
| 2 | * Synaptics TouchPad PS/2 mouse driver | ||
| 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 | |||
| 9 | #ifndef _SYNAPTICS_H | ||
| 10 | #define _SYNAPTICS_H | ||
| 11 | |||
| 12 | /* synaptics queries */ | ||
| 13 | #define SYN_QUE_IDENTIFY 0x00 | ||
| 14 | #define SYN_QUE_MODES 0x01 | ||
| 15 | #define SYN_QUE_CAPABILITIES 0x02 | ||
| 16 | #define SYN_QUE_MODEL 0x03 | ||
| 17 | #define SYN_QUE_SERIAL_NUMBER_PREFIX 0x06 | ||
| 18 | #define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07 | ||
| 19 | #define SYN_QUE_RESOLUTION 0x08 | ||
| 20 | #define SYN_QUE_EXT_CAPAB 0x09 | ||
| 21 | #define SYN_QUE_EXT_CAPAB_0C 0x0c | ||
| 22 | #define SYN_QUE_EXT_DIMENSIONS 0x0d | ||
| 23 | |||
| 24 | /* synatics modes */ | ||
| 25 | #define SYN_BIT_ABSOLUTE_MODE (1 << 7) | ||
| 26 | #define SYN_BIT_HIGH_RATE (1 << 6) | ||
| 27 | #define SYN_BIT_SLEEP_MODE (1 << 3) | ||
| 28 | #define SYN_BIT_DISABLE_GESTURE (1 << 2) | ||
| 29 | #define SYN_BIT_FOUR_BYTE_CLIENT (1 << 1) | ||
| 30 | #define SYN_BIT_W_MODE (1 << 0) | ||
| 31 | |||
| 32 | /* synaptics model ID bits */ | ||
| 33 | #define SYN_MODEL_ROT180(m) ((m) & (1 << 23)) | ||
| 34 | #define SYN_MODEL_PORTRAIT(m) ((m) & (1 << 22)) | ||
| 35 | #define SYN_MODEL_SENSOR(m) (((m) >> 16) & 0x3f) | ||
| 36 | #define SYN_MODEL_HARDWARE(m) (((m) >> 9) & 0x7f) | ||
| 37 | #define SYN_MODEL_NEWABS(m) ((m) & (1 << 7)) | ||
| 38 | #define SYN_MODEL_PEN(m) ((m) & (1 << 6)) | ||
| 39 | #define SYN_MODEL_SIMPLIC(m) ((m) & (1 << 5)) | ||
| 40 | #define SYN_MODEL_GEOMETRY(m) ((m) & 0x0f) | ||
| 41 | |||
| 42 | /* synaptics capability bits */ | ||
| 43 | #define SYN_CAP_EXTENDED(c) ((c) & (1 << 23)) | ||
| 44 | #define SYN_CAP_MIDDLE_BUTTON(c) ((c) & (1 << 18)) | ||
| 45 | #define SYN_CAP_PASS_THROUGH(c) ((c) & (1 << 7)) | ||
| 46 | #define SYN_CAP_SLEEP(c) ((c) & (1 << 4)) | ||
| 47 | #define SYN_CAP_FOUR_BUTTON(c) ((c) & (1 << 3)) | ||
| 48 | #define SYN_CAP_MULTIFINGER(c) ((c) & (1 << 1)) | ||
| 49 | #define SYN_CAP_PALMDETECT(c) ((c) & (1 << 0)) | ||
| 50 | #define SYN_CAP_SUBMODEL_ID(c) (((c) & 0x00ff00) >> 8) | ||
| 51 | #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) | ||
| 52 | #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) | ||
| 53 | #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) | ||
| 54 | #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100100) | ||
| 55 | #define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000) | ||
| 56 | |||
| 57 | /* synaptics modes query bits */ | ||
| 58 | #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) | ||
| 59 | #define SYN_MODE_RATE(m) ((m) & (1 << 6)) | ||
| 60 | #define SYN_MODE_BAUD_SLEEP(m) ((m) & (1 << 3)) | ||
| 61 | #define SYN_MODE_DISABLE_GESTURE(m) ((m) & (1 << 2)) | ||
| 62 | #define SYN_MODE_PACKSIZE(m) ((m) & (1 << 1)) | ||
| 63 | #define SYN_MODE_WMODE(m) ((m) & (1 << 0)) | ||
| 64 | |||
| 65 | /* synaptics identify query bits */ | ||
| 66 | #define SYN_ID_MODEL(i) (((i) >> 4) & 0x0f) | ||
| 67 | #define SYN_ID_MAJOR(i) ((i) & 0x0f) | ||
| 68 | #define SYN_ID_MINOR(i) (((i) >> 16) & 0xff) | ||
| 69 | #define SYN_ID_FULL(i) ((SYN_ID_MAJOR(i) << 8) | SYN_ID_MINOR(i)) | ||
| 70 | #define SYN_ID_IS_SYNAPTICS(i) ((((i) >> 8) & 0xff) == 0x47) | ||
| 71 | |||
| 72 | /* synaptics special commands */ | ||
| 73 | #define SYN_PS_SET_MODE2 0x14 | ||
| 74 | #define SYN_PS_CLIENT_CMD 0x28 | ||
| 75 | |||
| 76 | /* synaptics packet types */ | ||
| 77 | #define SYN_NEWABS 0 | ||
| 78 | #define SYN_NEWABS_STRICT 1 | ||
| 79 | #define SYN_NEWABS_RELAXED 2 | ||
| 80 | #define SYN_OLDABS 3 | ||
| 81 | |||
| 82 | /* | ||
| 83 | * A structure to describe the state of the touchpad hardware (buttons and pad) | ||
| 84 | */ | ||
| 85 | |||
| 86 | struct synaptics_hw_state { | ||
| 87 | int x; | ||
| 88 | int y; | ||
| 89 | int z; | ||
| 90 | int w; | ||
| 91 | unsigned int left:1; | ||
| 92 | unsigned int right:1; | ||
| 93 | unsigned int middle:1; | ||
| 94 | unsigned int up:1; | ||
| 95 | unsigned int down:1; | ||
| 96 | unsigned char ext_buttons; | ||
| 97 | signed char scroll; | ||
| 98 | }; | ||
| 99 | |||
| 100 | struct synaptics_data { | ||
| 101 | /* Data read from the touchpad */ | ||
| 102 | unsigned long int model_id; /* Model-ID */ | ||
| 103 | unsigned long int capabilities; /* Capabilities */ | ||
| 104 | unsigned long int ext_cap; /* Extended Capabilities */ | ||
| 105 | unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */ | ||
| 106 | unsigned long int identity; /* Identification */ | ||
| 107 | unsigned int x_res, y_res; /* X/Y resolution in units/mm */ | ||
| 108 | unsigned int x_max, y_max; /* Max dimensions (from FW) */ | ||
| 109 | |||
| 110 | unsigned char pkt_type; /* packet type - old, new, etc */ | ||
| 111 | unsigned char mode; /* current mode byte */ | ||
| 112 | int scroll; | ||
| 113 | struct synaptics_hw_state prev_hw; | ||
| 114 | }; | ||
| 115 | |||
| 116 | void synaptics_module_init(void); | ||
| 117 | int synaptics_detect(struct psmouse *psmouse, bool set_properties); | ||
| 118 | int synaptics_init(struct psmouse *psmouse); | ||
| 119 | void synaptics_reset(struct psmouse *psmouse); | ||
| 120 | bool synaptics_supported(void); | ||
| 121 | |||
| 122 | #endif /* _SYNAPTICS_H */ | ||
diff --git a/usr/src/dkms_source_tree/touchkit_ps2.c b/usr/src/dkms_source_tree/touchkit_ps2.c new file mode 100644 index 0000000..88121c5 --- /dev/null +++ b/usr/src/dkms_source_tree/touchkit_ps2.c | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | /* ---------------------------------------------------------------------------- | ||
| 2 | * touchkit_ps2.c -- Driver for eGalax TouchKit PS/2 Touchscreens | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 by Stefan Lucke | ||
| 5 | * Copyright (C) 2004 by Daniel Ritz | ||
| 6 | * Copyright (C) by Todd E. Johnson (mtouchusb.c) | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License as | ||
| 10 | * published by the Free Software Foundation; either version 2 of the | ||
| 11 | * License, or (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, but | ||
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 16 | * General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 21 | * | ||
| 22 | * Based upon touchkitusb.c | ||
| 23 | * | ||
| 24 | * Vendor documentation is available in support section of: | ||
| 25 | * http://www.egalax.com.tw/ | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include <linux/kernel.h> | ||
| 29 | |||
| 30 | #include <linux/input.h> | ||
| 31 | #include <linux/serio.h> | ||
| 32 | #include <linux/libps2.h> | ||
| 33 | |||
| 34 | #include "psmouse.h" | ||
| 35 | #include "touchkit_ps2.h" | ||
| 36 | |||
| 37 | #define TOUCHKIT_MAX_XC 0x07ff | ||
| 38 | #define TOUCHKIT_MAX_YC 0x07ff | ||
| 39 | |||
| 40 | #define TOUCHKIT_CMD 0x0a | ||
| 41 | #define TOUCHKIT_CMD_LENGTH 1 | ||
| 42 | |||
| 43 | #define TOUCHKIT_CMD_ACTIVE 'A' | ||
| 44 | #define TOUCHKIT_CMD_FIRMWARE_VERSION 'D' | ||
| 45 | #define TOUCHKIT_CMD_CONTROLLER_TYPE 'E' | ||
| 46 | |||
| 47 | #define TOUCHKIT_SEND_PARMS(s, r, c) ((s) << 12 | (r) << 8 | (c)) | ||
| 48 | |||
| 49 | #define TOUCHKIT_GET_TOUCHED(packet) (((packet)[0]) & 0x01) | ||
| 50 | #define TOUCHKIT_GET_X(packet) (((packet)[1] << 7) | (packet)[2]) | ||
| 51 | #define TOUCHKIT_GET_Y(packet) (((packet)[3] << 7) | (packet)[4]) | ||
| 52 | |||
| 53 | static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse) | ||
| 54 | { | ||
| 55 | unsigned char *packet = psmouse->packet; | ||
| 56 | struct input_dev *dev = psmouse->dev; | ||
| 57 | |||
| 58 | if (psmouse->pktcnt != 5) | ||
| 59 | return PSMOUSE_GOOD_DATA; | ||
| 60 | |||
| 61 | input_report_abs(dev, ABS_X, TOUCHKIT_GET_X(packet)); | ||
| 62 | input_report_abs(dev, ABS_Y, TOUCHKIT_GET_Y(packet)); | ||
| 63 | input_report_key(dev, BTN_TOUCH, TOUCHKIT_GET_TOUCHED(packet)); | ||
| 64 | input_sync(dev); | ||
| 65 | |||
| 66 | return PSMOUSE_FULL_PACKET; | ||
| 67 | } | ||
| 68 | |||
| 69 | int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties) | ||
| 70 | { | ||
| 71 | struct input_dev *dev = psmouse->dev; | ||
| 72 | unsigned char param[3]; | ||
| 73 | int command; | ||
| 74 | |||
| 75 | param[0] = TOUCHKIT_CMD_LENGTH; | ||
| 76 | param[1] = TOUCHKIT_CMD_ACTIVE; | ||
| 77 | command = TOUCHKIT_SEND_PARMS(2, 3, TOUCHKIT_CMD); | ||
| 78 | |||
| 79 | if (ps2_command(&psmouse->ps2dev, param, command)) | ||
| 80 | return -ENODEV; | ||
| 81 | |||
| 82 | if (param[0] != TOUCHKIT_CMD || param[1] != 0x01 || | ||
| 83 | param[2] != TOUCHKIT_CMD_ACTIVE) | ||
| 84 | return -ENODEV; | ||
| 85 | |||
| 86 | if (set_properties) { | ||
| 87 | dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | ||
| 88 | dev->keybit[BIT_WORD(BTN_MOUSE)] = 0; | ||
| 89 | dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | ||
| 90 | input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0); | ||
| 91 | input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0); | ||
| 92 | |||
| 93 | psmouse->vendor = "eGalax"; | ||
| 94 | psmouse->name = "Touchscreen"; | ||
| 95 | psmouse->protocol_handler = touchkit_ps2_process_byte; | ||
| 96 | psmouse->pktsize = 5; | ||
| 97 | } | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
diff --git a/usr/src/dkms_source_tree/touchkit_ps2.h b/usr/src/dkms_source_tree/touchkit_ps2.h new file mode 100644 index 0000000..2efe9ea --- /dev/null +++ b/usr/src/dkms_source_tree/touchkit_ps2.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* ---------------------------------------------------------------------------- | ||
| 2 | * touchkit_ps2.h -- Driver for eGalax TouchKit PS/2 Touchscreens | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 by Stefan Lucke | ||
| 5 | * Copyright (c) 2005 Vojtech Pavlik | ||
| 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 | #ifndef _TOUCHKIT_PS2_H | ||
| 13 | #define _TOUCHKIT_PS2_H | ||
| 14 | |||
| 15 | #ifdef CONFIG_MOUSE_PS2_TOUCHKIT | ||
| 16 | int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties); | ||
| 17 | #else | ||
| 18 | static inline int touchkit_ps2_detect(struct psmouse *psmouse, | ||
| 19 | bool set_properties) | ||
| 20 | { | ||
| 21 | return -ENOSYS; | ||
| 22 | } | ||
| 23 | #endif /* CONFIG_MOUSE_PS2_TOUCHKIT */ | ||
| 24 | |||
| 25 | #endif | ||
diff --git a/usr/src/dkms_source_tree/trackpoint.c b/usr/src/dkms_source_tree/trackpoint.c new file mode 100644 index 0000000..0643e49 --- /dev/null +++ b/usr/src/dkms_source_tree/trackpoint.c | |||
| @@ -0,0 +1,335 @@ | |||
| 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 | * Device IO: read, write and toggle bit | ||
| 24 | */ | ||
| 25 | static int trackpoint_read(struct ps2dev *ps2dev, unsigned char loc, unsigned char *results) | ||
| 26 | { | ||
| 27 | if (ps2_command(ps2dev, NULL, MAKE_PS2_CMD(0, 0, TP_COMMAND)) || | ||
| 28 | ps2_command(ps2dev, results, MAKE_PS2_CMD(0, 1, loc))) { | ||
| 29 | return -1; | ||
| 30 | } | ||
| 31 | |||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | static int trackpoint_write(struct ps2dev *ps2dev, unsigned char loc, unsigned char val) | ||
| 36 | { | ||
| 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))) { | ||
| 41 | return -1; | ||
| 42 | } | ||
| 43 | |||
| 44 | return 0; | ||
| 45 | } | ||
| 46 | |||
| 47 | static int trackpoint_toggle_bit(struct ps2dev *ps2dev, unsigned char loc, unsigned char mask) | ||
| 48 | { | ||
| 49 | /* Bad things will happen if the loc param isn't in this range */ | ||
| 50 | if (loc < 0x20 || loc >= 0x2F) | ||
| 51 | return -1; | ||
| 52 | |||
| 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))) { | ||
| 57 | return -1; | ||
| 58 | } | ||
| 59 | |||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | |||
| 64 | /* | ||
| 65 | * Trackpoint-specific attributes | ||
| 66 | */ | ||
| 67 | struct trackpoint_attr_data { | ||
| 68 | size_t field_offset; | ||
| 69 | unsigned char command; | ||
| 70 | unsigned char mask; | ||
| 71 | unsigned char inverted; | ||
| 72 | }; | ||
| 73 | |||
| 74 | static ssize_t trackpoint_show_int_attr(struct psmouse *psmouse, void *data, char *buf) | ||
| 75 | { | ||
| 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); | ||
| 79 | |||
| 80 | if (attr->inverted) | ||
| 81 | value = !value; | ||
| 82 | |||
| 83 | return sprintf(buf, "%u\n", value); | ||
| 84 | } | ||
| 85 | |||
| 86 | static ssize_t trackpoint_set_int_attr(struct psmouse *psmouse, void *data, | ||
| 87 | const char *buf, size_t count) | ||
| 88 | { | ||
| 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); | ||
| 92 | unsigned long value; | ||
| 93 | |||
| 94 | if (strict_strtoul(buf, 10, &value) || value > 255) | ||
| 95 | return -EINVAL; | ||
| 96 | |||
| 97 | *field = value; | ||
| 98 | trackpoint_write(&psmouse->ps2dev, attr->command, value); | ||
| 99 | |||
| 100 | return count; | ||
| 101 | } | ||
| 102 | |||
| 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, \ | ||
| 107 | }; \ | ||
| 108 | PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ | ||
| 109 | &trackpoint_attr_##_name, \ | ||
| 110 | trackpoint_show_int_attr, trackpoint_set_int_attr) | ||
| 111 | |||
| 112 | static ssize_t trackpoint_set_bit_attr(struct psmouse *psmouse, void *data, | ||
| 113 | const char *buf, size_t count) | ||
| 114 | { | ||
| 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); | ||
| 118 | unsigned long value; | ||
| 119 | |||
| 120 | if (strict_strtoul(buf, 10, &value) || value > 1) | ||
| 121 | return -EINVAL; | ||
| 122 | |||
| 123 | if (attr->inverted) | ||
| 124 | value = !value; | ||
| 125 | |||
| 126 | if (*field != value) { | ||
| 127 | *field = value; | ||
| 128 | trackpoint_toggle_bit(&psmouse->ps2dev, attr->command, attr->mask); | ||
| 129 | } | ||
| 130 | |||
| 131 | return count; | ||
| 132 | } | ||
| 133 | |||
| 134 | |||
| 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, \ | ||
| 139 | .mask = _mask, \ | ||
| 140 | .inverted = _inv, \ | ||
| 141 | }; \ | ||
| 142 | PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \ | ||
| 143 | &trackpoint_attr_##_name, \ | ||
| 144 | trackpoint_show_int_attr, trackpoint_set_bit_attr) | ||
| 145 | |||
| 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); | ||
| 156 | |||
| 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); | ||
| 160 | |||
| 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, | ||
| 175 | NULL | ||
| 176 | }; | ||
| 177 | |||
| 178 | static struct attribute_group trackpoint_attr_group = { | ||
| 179 | .attrs = trackpoint_attrs, | ||
| 180 | }; | ||
| 181 | |||
| 182 | static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *firmware_id) | ||
| 183 | { | ||
| 184 | unsigned char param[2] = { 0 }; | ||
| 185 | |||
| 186 | if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID))) | ||
| 187 | return -1; | ||
| 188 | |||
| 189 | if (param[0] != TP_MAGIC_IDENT) | ||
| 190 | return -1; | ||
| 191 | |||
| 192 | if (firmware_id) | ||
| 193 | *firmware_id = param[1]; | ||
| 194 | |||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | static int trackpoint_sync(struct psmouse *psmouse) | ||
| 199 | { | ||
| 200 | struct trackpoint_data *tp = psmouse->private; | ||
| 201 | unsigned char toggle; | ||
| 202 | |||
| 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); | ||
| 207 | |||
| 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); | ||
| 211 | |||
| 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); | ||
| 215 | |||
| 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); | ||
| 220 | |||
| 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); | ||
| 224 | |||
| 225 | trackpoint_write(&psmouse->ps2dev, TP_THRESH, tp->thresh); | ||
| 226 | trackpoint_write(&psmouse->ps2dev, TP_UP_THRESH, tp->upthresh); | ||
| 227 | |||
| 228 | trackpoint_write(&psmouse->ps2dev, TP_Z_TIME, tp->ztime); | ||
| 229 | trackpoint_write(&psmouse->ps2dev, TP_JENKS_CURV, tp->jenks); | ||
| 230 | |||
| 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); | ||
| 234 | |||
| 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); | ||
| 238 | |||
| 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); | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static void trackpoint_defaults(struct trackpoint_data *tp) | ||
| 247 | { | ||
| 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; | ||
| 252 | |||
| 253 | tp->draghys = TP_DEF_DRAGHYS; | ||
| 254 | tp->mindrag = TP_DEF_MINDRAG; | ||
| 255 | |||
| 256 | tp->thresh = TP_DEF_THRESH; | ||
| 257 | tp->upthresh = TP_DEF_UP_THRESH; | ||
| 258 | |||
| 259 | tp->ztime = TP_DEF_Z_TIME; | ||
| 260 | tp->jenks = TP_DEF_JENKS_CURV; | ||
| 261 | |||
| 262 | tp->inertia = TP_DEF_INERTIA; | ||
| 263 | tp->skipback = TP_DEF_SKIPBACK; | ||
| 264 | tp->ext_dev = TP_DEF_EXT_DEV; | ||
| 265 | } | ||
| 266 | |||
| 267 | static void trackpoint_disconnect(struct psmouse *psmouse) | ||
| 268 | { | ||
| 269 | sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj, &trackpoint_attr_group); | ||
| 270 | |||
| 271 | kfree(psmouse->private); | ||
| 272 | psmouse->private = NULL; | ||
| 273 | } | ||
| 274 | |||
| 275 | static int trackpoint_reconnect(struct psmouse *psmouse) | ||
| 276 | { | ||
| 277 | if (trackpoint_start_protocol(psmouse, NULL)) | ||
| 278 | return -1; | ||
| 279 | |||
| 280 | if (trackpoint_sync(psmouse)) | ||
| 281 | return -1; | ||
| 282 | |||
| 283 | return 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | int trackpoint_detect(struct psmouse *psmouse, bool set_properties) | ||
| 287 | { | ||
| 288 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 289 | unsigned char firmware_id; | ||
| 290 | unsigned char button_info; | ||
| 291 | int error; | ||
| 292 | |||
| 293 | if (trackpoint_start_protocol(psmouse, &firmware_id)) | ||
| 294 | return -1; | ||
| 295 | |||
| 296 | if (!set_properties) | ||
| 297 | return 0; | ||
| 298 | |||
| 299 | if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) { | ||
| 300 | printk(KERN_WARNING "trackpoint.c: failed to get extended button data\n"); | ||
| 301 | button_info = 0; | ||
| 302 | } | ||
| 303 | |||
| 304 | psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); | ||
| 305 | if (!psmouse->private) | ||
| 306 | return -1; | ||
| 307 | |||
| 308 | psmouse->vendor = "IBM"; | ||
| 309 | psmouse->name = "TrackPoint"; | ||
| 310 | |||
| 311 | psmouse->reconnect = trackpoint_reconnect; | ||
| 312 | psmouse->disconnect = trackpoint_disconnect; | ||
| 313 | |||
| 314 | if ((button_info & 0x0f) >= 3) | ||
| 315 | __set_bit(BTN_MIDDLE, psmouse->dev->keybit); | ||
| 316 | |||
| 317 | trackpoint_defaults(psmouse->private); | ||
| 318 | trackpoint_sync(psmouse); | ||
| 319 | |||
| 320 | error = sysfs_create_group(&ps2dev->serio->dev.kobj, &trackpoint_attr_group); | ||
| 321 | if (error) { | ||
| 322 | printk(KERN_ERR | ||
| 323 | "trackpoint.c: failed to create sysfs attributes, error: %d\n", | ||
| 324 | error); | ||
| 325 | kfree(psmouse->private); | ||
| 326 | psmouse->private = NULL; | ||
| 327 | return -1; | ||
| 328 | } | ||
| 329 | |||
| 330 | printk(KERN_INFO "IBM TrackPoint firmware: 0x%02x, buttons: %d/%d\n", | ||
| 331 | firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f); | ||
| 332 | |||
| 333 | return 0; | ||
| 334 | } | ||
| 335 | |||
diff --git a/usr/src/dkms_source_tree/trackpoint.h b/usr/src/dkms_source_tree/trackpoint.h new file mode 100644 index 0000000..e558a70 --- /dev/null +++ b/usr/src/dkms_source_tree/trackpoint.h | |||
| @@ -0,0 +1,154 @@ | |||
| 1 | /* | ||
| 2 | * IBM TrackPoint PS/2 mouse driver | ||
| 3 | * | ||
| 4 | * Stephen Evanchik <evanchsa@gmail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License version 2 as published by | ||
| 8 | * the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef _TRACKPOINT_H | ||
| 12 | #define _TRACKPOINT_H | ||
| 13 | |||
| 14 | /* | ||
| 15 | * These constants are from the TrackPoint System | ||
| 16 | * Engineering documentation Version 4 from IBM Watson | ||
| 17 | * research: | ||
| 18 | * http://wwwcssrv.almaden.ibm.com/trackpoint/download.html | ||
| 19 | */ | ||
| 20 | |||
| 21 | #define TP_COMMAND 0xE2 /* Commands start with this */ | ||
| 22 | |||
| 23 | #define TP_READ_ID 0xE1 /* Sent for device identification */ | ||
| 24 | #define TP_MAGIC_IDENT 0x01 /* Sent after a TP_READ_ID followed */ | ||
| 25 | /* by the firmware ID */ | ||
| 26 | |||
| 27 | |||
| 28 | /* | ||
| 29 | * Commands | ||
| 30 | */ | ||
| 31 | #define TP_RECALIB 0x51 /* Recalibrate */ | ||
| 32 | #define TP_POWER_DOWN 0x44 /* Can only be undone through HW reset */ | ||
| 33 | #define TP_EXT_DEV 0x21 /* Determines if external device is connected (RO) */ | ||
| 34 | #define TP_EXT_BTN 0x4B /* Read extended button status */ | ||
| 35 | #define TP_POR 0x7F /* Execute Power on Reset */ | ||
| 36 | #define TP_POR_RESULTS 0x25 /* Read Power on Self test results */ | ||
| 37 | #define TP_DISABLE_EXT 0x40 /* Disable external pointing device */ | ||
| 38 | #define TP_ENABLE_EXT 0x41 /* Enable external pointing device */ | ||
| 39 | |||
| 40 | /* | ||
| 41 | * Mode manipulation | ||
| 42 | */ | ||
| 43 | #define TP_SET_SOFT_TRANS 0x4E /* Set mode */ | ||
| 44 | #define TP_CANCEL_SOFT_TRANS 0xB9 /* Cancel mode */ | ||
| 45 | #define TP_SET_HARD_TRANS 0x45 /* Mode can only be set */ | ||
| 46 | |||
| 47 | |||
| 48 | /* | ||
| 49 | * Register oriented commands/properties | ||
| 50 | */ | ||
| 51 | #define TP_WRITE_MEM 0x81 | ||
| 52 | #define TP_READ_MEM 0x80 /* Not used in this implementation */ | ||
| 53 | |||
| 54 | /* | ||
| 55 | * RAM Locations for properties | ||
| 56 | */ | ||
| 57 | #define TP_SENS 0x4A /* Sensitivity */ | ||
| 58 | #define TP_MB 0x4C /* Read Middle Button Status (RO) */ | ||
| 59 | #define TP_INERTIA 0x4D /* Negative Inertia */ | ||
| 60 | #define TP_SPEED 0x60 /* Speed of TP Cursor */ | ||
| 61 | #define TP_REACH 0x57 /* Backup for Z-axis press */ | ||
| 62 | #define TP_DRAGHYS 0x58 /* Drag Hysteresis */ | ||
| 63 | /* (how hard it is to drag */ | ||
| 64 | /* with Z-axis pressed) */ | ||
| 65 | |||
| 66 | #define TP_MINDRAG 0x59 /* Minimum amount of force needed */ | ||
| 67 | /* to trigger dragging */ | ||
| 68 | |||
| 69 | #define TP_THRESH 0x5C /* Minimum value for a Z-axis press */ | ||
| 70 | #define TP_UP_THRESH 0x5A /* Used to generate a 'click' on Z-axis */ | ||
| 71 | #define TP_Z_TIME 0x5E /* How sharp of a press */ | ||
| 72 | #define TP_JENKS_CURV 0x5D /* Minimum curvature for double click */ | ||
| 73 | |||
| 74 | /* | ||
| 75 | * Toggling Flag bits | ||
| 76 | */ | ||
| 77 | #define TP_TOGGLE 0x47 /* Toggle command */ | ||
| 78 | |||
| 79 | #define TP_TOGGLE_MB 0x23 /* Disable/Enable Middle Button */ | ||
| 80 | #define TP_MASK_MB 0x01 | ||
| 81 | #define TP_TOGGLE_EXT_DEV 0x23 /* Disable external device */ | ||
| 82 | #define TP_MASK_EXT_DEV 0x02 | ||
| 83 | #define TP_TOGGLE_DRIFT 0x23 /* Drift Correction */ | ||
| 84 | #define TP_MASK_DRIFT 0x80 | ||
| 85 | #define TP_TOGGLE_BURST 0x28 /* Burst Mode */ | ||
| 86 | #define TP_MASK_BURST 0x80 | ||
| 87 | #define TP_TOGGLE_PTSON 0x2C /* Press to Select */ | ||
| 88 | #define TP_MASK_PTSON 0x01 | ||
| 89 | #define TP_TOGGLE_HARD_TRANS 0x2C /* Alternate method to set Hard Transparency */ | ||
| 90 | #define TP_MASK_HARD_TRANS 0x80 | ||
| 91 | #define TP_TOGGLE_TWOHAND 0x2D /* Two handed */ | ||
| 92 | #define TP_MASK_TWOHAND 0x01 | ||
| 93 | #define TP_TOGGLE_STICKY_TWO 0x2D /* Sticky two handed */ | ||
| 94 | #define TP_MASK_STICKY_TWO 0x04 | ||
| 95 | #define TP_TOGGLE_SKIPBACK 0x2D /* Suppress movement after drag release */ | ||
| 96 | #define TP_MASK_SKIPBACK 0x08 | ||
| 97 | #define TP_TOGGLE_SOURCE_TAG 0x20 /* Bit 3 of the first packet will be set to | ||
| 98 | to the origin of the packet (external or TP) */ | ||
| 99 | #define TP_MASK_SOURCE_TAG 0x80 | ||
| 100 | #define TP_TOGGLE_EXT_TAG 0x22 /* Bit 3 of the first packet coming from the | ||
| 101 | external device will be forced to 1 */ | ||
| 102 | #define TP_MASK_EXT_TAG 0x04 | ||
| 103 | |||
| 104 | |||
| 105 | /* Power on Self Test Results */ | ||
| 106 | #define TP_POR_SUCCESS 0x3B | ||
| 107 | |||
| 108 | /* | ||
| 109 | * Default power on values | ||
| 110 | */ | ||
| 111 | #define TP_DEF_SENS 0x80 | ||
| 112 | #define TP_DEF_INERTIA 0x06 | ||
| 113 | #define TP_DEF_SPEED 0x61 | ||
| 114 | #define TP_DEF_REACH 0x0A | ||
| 115 | |||
| 116 | #define TP_DEF_DRAGHYS 0xFF | ||
| 117 | #define TP_DEF_MINDRAG 0x14 | ||
| 118 | |||
| 119 | #define TP_DEF_THRESH 0x08 | ||
| 120 | #define TP_DEF_UP_THRESH 0xFF | ||
| 121 | #define TP_DEF_Z_TIME 0x26 | ||
| 122 | #define TP_DEF_JENKS_CURV 0x87 | ||
| 123 | |||
| 124 | /* Toggles */ | ||
| 125 | #define TP_DEF_MB 0x00 | ||
| 126 | #define TP_DEF_PTSON 0x00 | ||
| 127 | #define TP_DEF_SKIPBACK 0x00 | ||
| 128 | #define TP_DEF_EXT_DEV 0x00 /* 0 means enabled */ | ||
| 129 | |||
| 130 | #define MAKE_PS2_CMD(params, results, cmd) ((params<<12) | (results<<8) | (cmd)) | ||
| 131 | |||
| 132 | struct trackpoint_data | ||
| 133 | { | ||
| 134 | unsigned char sensitivity, speed, inertia, reach; | ||
| 135 | unsigned char draghys, mindrag; | ||
| 136 | unsigned char thresh, upthresh; | ||
| 137 | unsigned char ztime, jenks; | ||
| 138 | |||
| 139 | unsigned char press_to_select; | ||
| 140 | unsigned char skipback; | ||
| 141 | |||
| 142 | unsigned char ext_dev; | ||
| 143 | }; | ||
| 144 | |||
| 145 | #ifdef CONFIG_MOUSE_PS2_TRACKPOINT | ||
| 146 | int trackpoint_detect(struct psmouse *psmouse, bool set_properties); | ||
| 147 | #else | ||
| 148 | inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties) | ||
| 149 | { | ||
| 150 | return -ENOSYS; | ||
| 151 | } | ||
| 152 | #endif /* CONFIG_MOUSE_PS2_TRACKPOINT */ | ||
| 153 | |||
| 154 | #endif /* _TRACKPOINT_H */ | ||
