1df08ef27SAndres Salomon /* 2df08ef27SAndres Salomon * OLPC HGPK (XO-1) touchpad PS/2 mouse driver 3df08ef27SAndres Salomon * 4df08ef27SAndres Salomon * Copyright (c) 2006-2008 One Laptop Per Child 5df08ef27SAndres Salomon * Authors: 6df08ef27SAndres Salomon * Zephaniah E. Hull 7df08ef27SAndres Salomon * Andres Salomon <dilinger@debian.org> 8df08ef27SAndres Salomon * 9df08ef27SAndres Salomon * This driver is partly based on the ALPS driver, which is: 10df08ef27SAndres Salomon * 11df08ef27SAndres Salomon * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au> 12df08ef27SAndres Salomon * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com> 13df08ef27SAndres Salomon * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru> 14df08ef27SAndres Salomon * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> 15df08ef27SAndres Salomon * 16df08ef27SAndres Salomon * This program is free software; you can redistribute it and/or modify 17df08ef27SAndres Salomon * it under the terms of the GNU General Public License version 2 as 18df08ef27SAndres Salomon * published by the Free Software Foundation. 19df08ef27SAndres Salomon */ 20df08ef27SAndres Salomon 21df08ef27SAndres Salomon /* 22df08ef27SAndres Salomon * The spec from ALPS is available from 23df08ef27SAndres Salomon * <http://wiki.laptop.org/go/Touch_Pad/Tablet>. It refers to this 24df08ef27SAndres Salomon * device as HGPK (Hybrid GS, PT, and Keymatrix). 25df08ef27SAndres Salomon * 26df08ef27SAndres Salomon * The earliest versions of the device had simultaneous reporting; that 27df08ef27SAndres Salomon * was removed. After that, the device used the Advanced Mode GS/PT streaming 28df08ef27SAndres Salomon * stuff. That turned out to be too buggy to support, so we've finally 29df08ef27SAndres Salomon * switched to Mouse Mode (which utilizes only the center 1/3 of the touchpad). 30df08ef27SAndres Salomon */ 31df08ef27SAndres Salomon 32df08ef27SAndres Salomon #define DEBUG 335a0e3ad6STejun Heo #include <linux/slab.h> 34df08ef27SAndres Salomon #include <linux/input.h> 35df08ef27SAndres Salomon #include <linux/serio.h> 36df08ef27SAndres Salomon #include <linux/libps2.h> 37df08ef27SAndres Salomon #include <linux/delay.h> 38df08ef27SAndres Salomon #include <asm/olpc.h> 39df08ef27SAndres Salomon 40df08ef27SAndres Salomon #include "psmouse.h" 41df08ef27SAndres Salomon #include "hgpk.h" 42df08ef27SAndres Salomon 43a62f0d27SDmitry Torokhov static bool tpdebug; 44a62f0d27SDmitry Torokhov module_param(tpdebug, bool, 0644); 45df08ef27SAndres Salomon MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); 46df08ef27SAndres Salomon 47df08ef27SAndres Salomon static int recalib_delta = 100; 48df08ef27SAndres Salomon module_param(recalib_delta, int, 0644); 49df08ef27SAndres Salomon MODULE_PARM_DESC(recalib_delta, 50df08ef27SAndres Salomon "packets containing a delta this large will cause a recalibration."); 51df08ef27SAndres Salomon 528bbf2703SPaul Fox static int jumpy_delay = 1000; 538bbf2703SPaul Fox module_param(jumpy_delay, int, 0644); 548bbf2703SPaul Fox MODULE_PARM_DESC(jumpy_delay, 558bbf2703SPaul Fox "delay (ms) before recal after jumpiness detected"); 568bbf2703SPaul Fox 57*c0dc8342SDaniel Drake static int spew_delay = 1; 588bbf2703SPaul Fox module_param(spew_delay, int, 0644); 598bbf2703SPaul Fox MODULE_PARM_DESC(spew_delay, 608bbf2703SPaul Fox "delay (ms) before recal after packet spew detected"); 618bbf2703SPaul Fox 628bbf2703SPaul Fox static int recal_guard_time = 2000; 638bbf2703SPaul Fox module_param(recal_guard_time, int, 0644); 648bbf2703SPaul Fox MODULE_PARM_DESC(recal_guard_time, 658bbf2703SPaul Fox "interval (ms) during which recal will be restarted if packet received"); 668bbf2703SPaul Fox 678bbf2703SPaul Fox static int post_interrupt_delay = 1000; 688bbf2703SPaul Fox module_param(post_interrupt_delay, int, 0644); 698bbf2703SPaul Fox MODULE_PARM_DESC(post_interrupt_delay, 708bbf2703SPaul Fox "delay (ms) before recal after recal interrupt detected"); 718bbf2703SPaul Fox 72ca94ec43SDaniel Drake static char hgpk_mode_name[16]; 73ca94ec43SDaniel Drake module_param_string(hgpk_mode, hgpk_mode_name, sizeof(hgpk_mode_name), 0644); 74ca94ec43SDaniel Drake MODULE_PARM_DESC(hgpk_mode, 75ca94ec43SDaniel Drake "default hgpk mode: mouse, glidesensor or pentablet"); 76ca94ec43SDaniel Drake 77ca94ec43SDaniel Drake static int hgpk_default_mode = HGPK_MODE_MOUSE; 78ca94ec43SDaniel Drake 79ca94ec43SDaniel Drake static const char * const hgpk_mode_names[] = { 80ca94ec43SDaniel Drake [HGPK_MODE_MOUSE] = "Mouse", 81ca94ec43SDaniel Drake [HGPK_MODE_GLIDESENSOR] = "GlideSensor", 82ca94ec43SDaniel Drake [HGPK_MODE_PENTABLET] = "PenTablet", 83ca94ec43SDaniel Drake }; 84ca94ec43SDaniel Drake 85ca94ec43SDaniel Drake static int hgpk_mode_from_name(const char *buf, int len) 86ca94ec43SDaniel Drake { 87ca94ec43SDaniel Drake int i; 88ca94ec43SDaniel Drake 89ca94ec43SDaniel Drake for (i = 0; i < ARRAY_SIZE(hgpk_mode_names); i++) { 90ca94ec43SDaniel Drake const char *name = hgpk_mode_names[i]; 91ca94ec43SDaniel Drake if (strlen(name) == len && !strncasecmp(name, buf, len)) 92ca94ec43SDaniel Drake return i; 93ca94ec43SDaniel Drake } 94ca94ec43SDaniel Drake 95ca94ec43SDaniel Drake return HGPK_MODE_INVALID; 96ca94ec43SDaniel Drake } 97ca94ec43SDaniel Drake 98df08ef27SAndres Salomon /* 99df08ef27SAndres Salomon * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" 100df08ef27SAndres Salomon * above the pad and still have it send packets. This causes a jump cursor 101df08ef27SAndres Salomon * when one places their finger on the pad. We can probably detect the 102df08ef27SAndres Salomon * jump as we see a large deltas (>= 100px). In mouse mode, I've been 103df08ef27SAndres Salomon * unable to even come close to 100px deltas during normal usage, so I think 104df08ef27SAndres Salomon * this threshold is safe. If a large delta occurs, trigger a recalibration. 105df08ef27SAndres Salomon */ 106df08ef27SAndres Salomon static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) 107df08ef27SAndres Salomon { 108df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 109df08ef27SAndres Salomon 110df08ef27SAndres Salomon if (abs(x) > recalib_delta || abs(y) > recalib_delta) { 111df08ef27SAndres Salomon hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", 112df08ef27SAndres Salomon recalib_delta, x, y); 113df08ef27SAndres Salomon /* My car gets forty rods to the hogshead and that's the 114df08ef27SAndres Salomon * way I likes it! */ 115df08ef27SAndres Salomon psmouse_queue_work(psmouse, &priv->recalib_wq, 1168bbf2703SPaul Fox msecs_to_jiffies(jumpy_delay)); 117df08ef27SAndres Salomon } 118df08ef27SAndres Salomon } 119df08ef27SAndres Salomon 120*c0dc8342SDaniel Drake static void hgpk_reset_spew_detection(struct hgpk_data *priv) 121*c0dc8342SDaniel Drake { 122*c0dc8342SDaniel Drake priv->spew_count = 0; 123*c0dc8342SDaniel Drake priv->dupe_count = 0; 124*c0dc8342SDaniel Drake priv->x_tally = 0; 125*c0dc8342SDaniel Drake priv->y_tally = 0; 126*c0dc8342SDaniel Drake priv->spew_flag = NO_SPEW; 127*c0dc8342SDaniel Drake } 128*c0dc8342SDaniel Drake 129*c0dc8342SDaniel Drake static void hgpk_reset_hack_state(struct psmouse *psmouse) 130*c0dc8342SDaniel Drake { 131*c0dc8342SDaniel Drake struct hgpk_data *priv = psmouse->private; 132*c0dc8342SDaniel Drake 133*c0dc8342SDaniel Drake priv->abs_x = priv->abs_y = -1; 134*c0dc8342SDaniel Drake hgpk_reset_spew_detection(priv); 135*c0dc8342SDaniel Drake } 136*c0dc8342SDaniel Drake 137df08ef27SAndres Salomon /* 138df08ef27SAndres Salomon * We have no idea why this particular hardware bug occurs. The touchpad 139df08ef27SAndres Salomon * will randomly start spewing packets without anything touching the 140df08ef27SAndres Salomon * pad. This wouldn't necessarily be bad, but it's indicative of a 141df08ef27SAndres Salomon * severely miscalibrated pad; attempting to use the touchpad while it's 142df08ef27SAndres Salomon * spewing means the cursor will jump all over the place, and act "drunk". 143df08ef27SAndres Salomon * 144df08ef27SAndres Salomon * The packets that are spewed tend to all have deltas between -2 and 2, and 145df08ef27SAndres Salomon * the cursor will move around without really going very far. It will 146df08ef27SAndres Salomon * tend to end up in the same location; if we tally up the changes over 147df08ef27SAndres Salomon * 100 packets, we end up w/ a final delta of close to 0. This happens 148df08ef27SAndres Salomon * pretty regularly when the touchpad is spewing, and is pretty hard to 149df08ef27SAndres Salomon * manually trigger (at least for *my* fingers). So, it makes a perfect 150df08ef27SAndres Salomon * scheme for detecting spews. 151df08ef27SAndres Salomon */ 152df08ef27SAndres Salomon static void hgpk_spewing_hack(struct psmouse *psmouse, 153df08ef27SAndres Salomon int l, int r, int x, int y) 154df08ef27SAndres Salomon { 155df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 156df08ef27SAndres Salomon 157df08ef27SAndres Salomon /* ignore button press packets; many in a row could trigger 158df08ef27SAndres Salomon * a false-positive! */ 159df08ef27SAndres Salomon if (l || r) 160df08ef27SAndres Salomon return; 161df08ef27SAndres Salomon 162*c0dc8342SDaniel Drake /* don't track spew if the workaround feature has been turned off */ 163*c0dc8342SDaniel Drake if (!spew_delay) 164*c0dc8342SDaniel Drake return; 165*c0dc8342SDaniel Drake 166*c0dc8342SDaniel Drake if (abs(x) > 3 || abs(y) > 3) { 167*c0dc8342SDaniel Drake /* no spew, or spew ended */ 168*c0dc8342SDaniel Drake hgpk_reset_spew_detection(priv); 169*c0dc8342SDaniel Drake return; 170*c0dc8342SDaniel Drake } 171*c0dc8342SDaniel Drake 172*c0dc8342SDaniel Drake /* Keep a tally of the overall delta to the cursor position caused by 173*c0dc8342SDaniel Drake * the spew */ 174df08ef27SAndres Salomon priv->x_tally += x; 175df08ef27SAndres Salomon priv->y_tally += y; 176df08ef27SAndres Salomon 177*c0dc8342SDaniel Drake switch (priv->spew_flag) { 178*c0dc8342SDaniel Drake case NO_SPEW: 179*c0dc8342SDaniel Drake /* we're not spewing, but this packet might be the start */ 180*c0dc8342SDaniel Drake priv->spew_flag = MAYBE_SPEWING; 181*c0dc8342SDaniel Drake 182*c0dc8342SDaniel Drake /* fall-through */ 183*c0dc8342SDaniel Drake 184*c0dc8342SDaniel Drake case MAYBE_SPEWING: 185*c0dc8342SDaniel Drake priv->spew_count++; 186*c0dc8342SDaniel Drake 187*c0dc8342SDaniel Drake if (priv->spew_count < SPEW_WATCH_COUNT) 188*c0dc8342SDaniel Drake break; 189*c0dc8342SDaniel Drake 190*c0dc8342SDaniel Drake /* excessive spew detected, request recalibration */ 191*c0dc8342SDaniel Drake priv->spew_flag = SPEW_DETECTED; 192*c0dc8342SDaniel Drake 193*c0dc8342SDaniel Drake /* fall-through */ 194*c0dc8342SDaniel Drake 195*c0dc8342SDaniel Drake case SPEW_DETECTED: 196*c0dc8342SDaniel Drake /* only recalibrate when the overall delta to the cursor 197*c0dc8342SDaniel Drake * is really small. if the spew is causing significant cursor 198*c0dc8342SDaniel Drake * movement, it is probably a case of the user moving the 199*c0dc8342SDaniel Drake * cursor very slowly across the screen. */ 200df08ef27SAndres Salomon if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { 201*c0dc8342SDaniel Drake hgpk_err(psmouse, "packet spew detected (%d,%d)\n", 202df08ef27SAndres Salomon priv->x_tally, priv->y_tally); 203*c0dc8342SDaniel Drake priv->spew_flag = RECALIBRATING; 204df08ef27SAndres Salomon psmouse_queue_work(psmouse, &priv->recalib_wq, 2058bbf2703SPaul Fox msecs_to_jiffies(spew_delay)); 206df08ef27SAndres Salomon } 207*c0dc8342SDaniel Drake 208*c0dc8342SDaniel Drake break; 209*c0dc8342SDaniel Drake case RECALIBRATING: 210*c0dc8342SDaniel Drake /* we already detected a spew and requested a recalibration, 211*c0dc8342SDaniel Drake * just wait for the queue to kick into action. */ 212*c0dc8342SDaniel Drake break; 213df08ef27SAndres Salomon } 214df08ef27SAndres Salomon } 215df08ef27SAndres Salomon 216df08ef27SAndres Salomon /* 217df08ef27SAndres Salomon * HGPK Mouse Mode format (standard mouse format, sans middle button) 218df08ef27SAndres Salomon * 219df08ef27SAndres Salomon * byte 0: y-over x-over y-neg x-neg 1 0 swr swl 220df08ef27SAndres Salomon * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 221df08ef27SAndres Salomon * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 222df08ef27SAndres Salomon * 223df08ef27SAndres Salomon * swr/swl are the left/right buttons. 224df08ef27SAndres Salomon * x-neg/y-neg are the x and y delta negative bits 225df08ef27SAndres Salomon * x-over/y-over are the x and y overflow bits 226ca94ec43SDaniel Drake * 227ca94ec43SDaniel Drake * --- 228ca94ec43SDaniel Drake * 229ca94ec43SDaniel Drake * HGPK Advanced Mode - single-mode format 230ca94ec43SDaniel Drake * 231ca94ec43SDaniel Drake * byte 0(PT): 1 1 0 0 1 1 1 1 232ca94ec43SDaniel Drake * byte 0(GS): 1 1 1 1 1 1 1 1 233ca94ec43SDaniel Drake * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 234ca94ec43SDaniel Drake * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0 235ca94ec43SDaniel Drake * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw 236ca94ec43SDaniel Drake * byte 3: 0 y9 y8 y7 1 0 swr swl 237ca94ec43SDaniel Drake * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 238ca94ec43SDaniel Drake * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 239ca94ec43SDaniel Drake * 240ca94ec43SDaniel Drake * ?'s are not defined in the protocol spec, may vary between models. 241ca94ec43SDaniel Drake * 242ca94ec43SDaniel Drake * swr/swl are the left/right buttons. 243ca94ec43SDaniel Drake * 244ca94ec43SDaniel Drake * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a 245ca94ec43SDaniel Drake * pen/finger 246df08ef27SAndres Salomon */ 247ca94ec43SDaniel Drake static bool hgpk_is_byte_valid(struct psmouse *psmouse, unsigned char *packet) 248df08ef27SAndres Salomon { 249ca94ec43SDaniel Drake struct hgpk_data *priv = psmouse->private; 250ca94ec43SDaniel Drake int pktcnt = psmouse->pktcnt; 251ca94ec43SDaniel Drake bool valid; 252ca94ec43SDaniel Drake 253ca94ec43SDaniel Drake switch (priv->mode) { 254ca94ec43SDaniel Drake case HGPK_MODE_MOUSE: 255ca94ec43SDaniel Drake valid = (packet[0] & 0x0C) == 0x08; 256ca94ec43SDaniel Drake break; 257ca94ec43SDaniel Drake 258ca94ec43SDaniel Drake case HGPK_MODE_GLIDESENSOR: 259ca94ec43SDaniel Drake valid = pktcnt == 1 ? 260ca94ec43SDaniel Drake packet[0] == HGPK_GS : !(packet[pktcnt - 1] & 0x80); 261ca94ec43SDaniel Drake break; 262ca94ec43SDaniel Drake 263ca94ec43SDaniel Drake case HGPK_MODE_PENTABLET: 264ca94ec43SDaniel Drake valid = pktcnt == 1 ? 265ca94ec43SDaniel Drake packet[0] == HGPK_PT : !(packet[pktcnt - 1] & 0x80); 266ca94ec43SDaniel Drake break; 267ca94ec43SDaniel Drake 268ca94ec43SDaniel Drake default: 269ca94ec43SDaniel Drake valid = false; 270ca94ec43SDaniel Drake break; 271df08ef27SAndres Salomon } 272df08ef27SAndres Salomon 273ca94ec43SDaniel Drake if (!valid) 274ca94ec43SDaniel Drake hgpk_dbg(psmouse, 275ca94ec43SDaniel Drake "bad data, mode %d (%d) %02x %02x %02x %02x %02x %02x\n", 276ca94ec43SDaniel Drake priv->mode, pktcnt, 277ca94ec43SDaniel Drake psmouse->packet[0], psmouse->packet[1], 278ca94ec43SDaniel Drake psmouse->packet[2], psmouse->packet[3], 279ca94ec43SDaniel Drake psmouse->packet[4], psmouse->packet[5]); 280ca94ec43SDaniel Drake 281ca94ec43SDaniel Drake return valid; 282ca94ec43SDaniel Drake } 283ca94ec43SDaniel Drake 284ca94ec43SDaniel Drake static void hgpk_process_advanced_packet(struct psmouse *psmouse) 285ca94ec43SDaniel Drake { 286ca94ec43SDaniel Drake struct hgpk_data *priv = psmouse->private; 287ca94ec43SDaniel Drake struct input_dev *idev = psmouse->dev; 288ca94ec43SDaniel Drake unsigned char *packet = psmouse->packet; 289ca94ec43SDaniel Drake int down = !!(packet[2] & 2); 290ca94ec43SDaniel Drake int left = !!(packet[3] & 1); 291ca94ec43SDaniel Drake int right = !!(packet[3] & 2); 292ca94ec43SDaniel Drake int x = packet[1] | ((packet[2] & 0x78) << 4); 293ca94ec43SDaniel Drake int y = packet[4] | ((packet[3] & 0x70) << 3); 294ca94ec43SDaniel Drake 295ca94ec43SDaniel Drake if (priv->mode == HGPK_MODE_GLIDESENSOR) { 296ca94ec43SDaniel Drake int pt_down = !!(packet[2] & 1); 297ca94ec43SDaniel Drake int finger_down = !!(packet[2] & 2); 298ca94ec43SDaniel Drake int z = packet[5]; 299ca94ec43SDaniel Drake 300ca94ec43SDaniel Drake input_report_abs(idev, ABS_PRESSURE, z); 301ca94ec43SDaniel Drake if (tpdebug) 302ca94ec43SDaniel Drake hgpk_dbg(psmouse, "pd=%d fd=%d z=%d", 303ca94ec43SDaniel Drake pt_down, finger_down, z); 304ca94ec43SDaniel Drake } else { 305ca94ec43SDaniel Drake /* 306ca94ec43SDaniel Drake * PenTablet mode does not report pressure, so we don't 307ca94ec43SDaniel Drake * report it here 308ca94ec43SDaniel Drake */ 309ca94ec43SDaniel Drake if (tpdebug) 310ca94ec43SDaniel Drake hgpk_dbg(psmouse, "pd=%d ", down); 311ca94ec43SDaniel Drake } 312ca94ec43SDaniel Drake 313ca94ec43SDaniel Drake if (tpdebug) 314ca94ec43SDaniel Drake hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); 315ca94ec43SDaniel Drake 316ca94ec43SDaniel Drake input_report_key(idev, BTN_TOUCH, down); 317ca94ec43SDaniel Drake input_report_key(idev, BTN_LEFT, left); 318ca94ec43SDaniel Drake input_report_key(idev, BTN_RIGHT, right); 319ca94ec43SDaniel Drake 320ca94ec43SDaniel Drake /* 321ca94ec43SDaniel Drake * If this packet says that the finger was removed, reset our position 322ca94ec43SDaniel Drake * tracking so that we don't erroneously detect a jump on next press. 323ca94ec43SDaniel Drake */ 324*c0dc8342SDaniel Drake if (!down) { 325*c0dc8342SDaniel Drake hgpk_reset_hack_state(priv); 326*c0dc8342SDaniel Drake goto done; 327*c0dc8342SDaniel Drake } 328ca94ec43SDaniel Drake 329ca94ec43SDaniel Drake /* 330*c0dc8342SDaniel Drake * Weed out duplicate packets (we get quite a few, and they mess up 331*c0dc8342SDaniel Drake * our jump detection) 332ca94ec43SDaniel Drake */ 333*c0dc8342SDaniel Drake if (x == priv->abs_x && y == priv->abs_y) { 334*c0dc8342SDaniel Drake if (++priv->dupe_count > SPEW_WATCH_COUNT) { 335*c0dc8342SDaniel Drake if (tpdebug) 336*c0dc8342SDaniel Drake hgpk_dbg(psmouse, "hard spew detected\n"); 337*c0dc8342SDaniel Drake priv->spew_flag = RECALIBRATING; 338*c0dc8342SDaniel Drake psmouse_queue_work(psmouse, &priv->recalib_wq, 339*c0dc8342SDaniel Drake msecs_to_jiffies(spew_delay)); 340*c0dc8342SDaniel Drake } 341*c0dc8342SDaniel Drake goto done; 342*c0dc8342SDaniel Drake } 343*c0dc8342SDaniel Drake 344*c0dc8342SDaniel Drake /* not a duplicate, continue with position reporting */ 345*c0dc8342SDaniel Drake priv->dupe_count = 0; 346ca94ec43SDaniel Drake 347ca94ec43SDaniel Drake /* Don't apply hacks in PT mode, it seems reliable */ 348ca94ec43SDaniel Drake if (priv->mode != HGPK_MODE_PENTABLET && priv->abs_x != -1) { 349ca94ec43SDaniel Drake hgpk_jumpy_hack(psmouse, 350ca94ec43SDaniel Drake priv->abs_x - x, priv->abs_y - y); 351ca94ec43SDaniel Drake hgpk_spewing_hack(psmouse, left, right, 352ca94ec43SDaniel Drake priv->abs_x - x, priv->abs_y - y); 353ca94ec43SDaniel Drake } 354ca94ec43SDaniel Drake 355ca94ec43SDaniel Drake input_report_abs(idev, ABS_X, x); 356ca94ec43SDaniel Drake input_report_abs(idev, ABS_Y, y); 357ca94ec43SDaniel Drake priv->abs_x = x; 358ca94ec43SDaniel Drake priv->abs_y = y; 359ca94ec43SDaniel Drake 360*c0dc8342SDaniel Drake done: 361ca94ec43SDaniel Drake input_sync(idev); 362ca94ec43SDaniel Drake } 363ca94ec43SDaniel Drake 364ca94ec43SDaniel Drake static void hgpk_process_simple_packet(struct psmouse *psmouse) 365df08ef27SAndres Salomon { 366df08ef27SAndres Salomon struct input_dev *dev = psmouse->dev; 367df08ef27SAndres Salomon unsigned char *packet = psmouse->packet; 368ca94ec43SDaniel Drake int left = packet[0] & 1; 369ca94ec43SDaniel Drake int right = (packet[0] >> 1) & 1; 370ca94ec43SDaniel Drake int x = packet[1] - ((packet[0] << 4) & 0x100); 371ca94ec43SDaniel Drake int y = ((packet[0] << 3) & 0x100) - packet[2]; 372df08ef27SAndres Salomon 373df08ef27SAndres Salomon hgpk_jumpy_hack(psmouse, x, y); 374df08ef27SAndres Salomon hgpk_spewing_hack(psmouse, left, right, x, y); 375df08ef27SAndres Salomon 376df08ef27SAndres Salomon if (tpdebug) 377df08ef27SAndres Salomon hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); 378df08ef27SAndres Salomon 379df08ef27SAndres Salomon input_report_key(dev, BTN_LEFT, left); 380df08ef27SAndres Salomon input_report_key(dev, BTN_RIGHT, right); 381df08ef27SAndres Salomon 382df08ef27SAndres Salomon input_report_rel(dev, REL_X, x); 383df08ef27SAndres Salomon input_report_rel(dev, REL_Y, y); 384df08ef27SAndres Salomon 385df08ef27SAndres Salomon input_sync(dev); 386df08ef27SAndres Salomon } 387df08ef27SAndres Salomon 388df08ef27SAndres Salomon static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) 389df08ef27SAndres Salomon { 390df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 391df08ef27SAndres Salomon 392ca94ec43SDaniel Drake if (!hgpk_is_byte_valid(psmouse, psmouse->packet)) 393df08ef27SAndres Salomon return PSMOUSE_BAD_DATA; 394df08ef27SAndres Salomon 395df08ef27SAndres Salomon if (psmouse->pktcnt >= psmouse->pktsize) { 396ca94ec43SDaniel Drake if (priv->mode == HGPK_MODE_MOUSE) 397ca94ec43SDaniel Drake hgpk_process_simple_packet(psmouse); 398ca94ec43SDaniel Drake else 399ca94ec43SDaniel Drake hgpk_process_advanced_packet(psmouse); 400df08ef27SAndres Salomon return PSMOUSE_FULL_PACKET; 401df08ef27SAndres Salomon } 402df08ef27SAndres Salomon 403df08ef27SAndres Salomon if (priv->recalib_window) { 404df08ef27SAndres Salomon if (time_before(jiffies, priv->recalib_window)) { 405df08ef27SAndres Salomon /* 406df08ef27SAndres Salomon * ugh, got a packet inside our recalibration 407df08ef27SAndres Salomon * window, schedule another recalibration. 408df08ef27SAndres Salomon */ 409df08ef27SAndres Salomon hgpk_dbg(psmouse, 410df08ef27SAndres Salomon "packet inside calibration window, " 411df08ef27SAndres Salomon "queueing another recalibration\n"); 412df08ef27SAndres Salomon psmouse_queue_work(psmouse, &priv->recalib_wq, 4138bbf2703SPaul Fox msecs_to_jiffies(post_interrupt_delay)); 414df08ef27SAndres Salomon } 415df08ef27SAndres Salomon priv->recalib_window = 0; 416df08ef27SAndres Salomon } 417df08ef27SAndres Salomon 418df08ef27SAndres Salomon return PSMOUSE_GOOD_DATA; 419df08ef27SAndres Salomon } 420df08ef27SAndres Salomon 421ca94ec43SDaniel Drake static int hgpk_select_mode(struct psmouse *psmouse) 422df08ef27SAndres Salomon { 423df08ef27SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev; 424df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 425ca94ec43SDaniel Drake int i; 426ca94ec43SDaniel Drake int cmd; 427df08ef27SAndres Salomon 428ca94ec43SDaniel Drake /* 429ca94ec43SDaniel Drake * 4 disables to enable advanced mode 430ca94ec43SDaniel Drake * then 3 0xf2 bytes as the preamble for GS/PT selection 431ca94ec43SDaniel Drake */ 432ca94ec43SDaniel Drake const int advanced_init[] = { 433ca94ec43SDaniel Drake PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, 434ca94ec43SDaniel Drake PSMOUSE_CMD_DISABLE, PSMOUSE_CMD_DISABLE, 435ca94ec43SDaniel Drake 0xf2, 0xf2, 0xf2, 436ca94ec43SDaniel Drake }; 437ca94ec43SDaniel Drake 438ca94ec43SDaniel Drake switch (priv->mode) { 439ca94ec43SDaniel Drake case HGPK_MODE_MOUSE: 440ca94ec43SDaniel Drake psmouse->pktsize = 3; 441ca94ec43SDaniel Drake break; 442ca94ec43SDaniel Drake 443ca94ec43SDaniel Drake case HGPK_MODE_GLIDESENSOR: 444ca94ec43SDaniel Drake case HGPK_MODE_PENTABLET: 445ca94ec43SDaniel Drake psmouse->pktsize = 6; 446ca94ec43SDaniel Drake 447ca94ec43SDaniel Drake /* Switch to 'Advanced mode.', four disables in a row. */ 448ca94ec43SDaniel Drake for (i = 0; i < ARRAY_SIZE(advanced_init); i++) 449ca94ec43SDaniel Drake if (ps2_command(ps2dev, NULL, advanced_init[i])) 450ca94ec43SDaniel Drake return -EIO; 451ca94ec43SDaniel Drake 452ca94ec43SDaniel Drake /* select between GlideSensor (mouse) or PenTablet */ 453ca94ec43SDaniel Drake cmd = priv->mode == HGPK_MODE_GLIDESENSOR ? 454ca94ec43SDaniel Drake PSMOUSE_CMD_SETSCALE11 : PSMOUSE_CMD_SETSCALE21; 455ca94ec43SDaniel Drake 456ca94ec43SDaniel Drake if (ps2_command(ps2dev, NULL, cmd)) 457ca94ec43SDaniel Drake return -EIO; 458ca94ec43SDaniel Drake break; 459ca94ec43SDaniel Drake 460ca94ec43SDaniel Drake default: 461ca94ec43SDaniel Drake return -EINVAL; 462ca94ec43SDaniel Drake } 463ca94ec43SDaniel Drake 464df08ef27SAndres Salomon return 0; 465ca94ec43SDaniel Drake } 466df08ef27SAndres Salomon 467ca94ec43SDaniel Drake static void hgpk_setup_input_device(struct input_dev *input, 468ca94ec43SDaniel Drake struct input_dev *old_input, 469ca94ec43SDaniel Drake enum hgpk_mode mode) 470ca94ec43SDaniel Drake { 471ca94ec43SDaniel Drake if (old_input) { 472ca94ec43SDaniel Drake input->name = old_input->name; 473ca94ec43SDaniel Drake input->phys = old_input->phys; 474ca94ec43SDaniel Drake input->id = old_input->id; 475ca94ec43SDaniel Drake input->dev.parent = old_input->dev.parent; 476ca94ec43SDaniel Drake } 477df08ef27SAndres Salomon 478ca94ec43SDaniel Drake memset(input->evbit, 0, sizeof(input->evbit)); 479ca94ec43SDaniel Drake memset(input->relbit, 0, sizeof(input->relbit)); 480ca94ec43SDaniel Drake memset(input->keybit, 0, sizeof(input->keybit)); 481ca94ec43SDaniel Drake 482ca94ec43SDaniel Drake /* All modes report left and right buttons */ 483ca94ec43SDaniel Drake __set_bit(EV_KEY, input->evbit); 484ca94ec43SDaniel Drake __set_bit(BTN_LEFT, input->keybit); 485ca94ec43SDaniel Drake __set_bit(BTN_RIGHT, input->keybit); 486ca94ec43SDaniel Drake 487ca94ec43SDaniel Drake switch (mode) { 488ca94ec43SDaniel Drake case HGPK_MODE_MOUSE: 489ca94ec43SDaniel Drake __set_bit(EV_REL, input->evbit); 490ca94ec43SDaniel Drake __set_bit(REL_X, input->relbit); 491ca94ec43SDaniel Drake __set_bit(REL_Y, input->relbit); 492ca94ec43SDaniel Drake break; 493ca94ec43SDaniel Drake 494ca94ec43SDaniel Drake case HGPK_MODE_GLIDESENSOR: 495ca94ec43SDaniel Drake __set_bit(BTN_TOUCH, input->keybit); 496ca94ec43SDaniel Drake __set_bit(BTN_TOOL_FINGER, input->keybit); 497ca94ec43SDaniel Drake 498ca94ec43SDaniel Drake __set_bit(EV_ABS, input->evbit); 499ca94ec43SDaniel Drake 500ca94ec43SDaniel Drake /* GlideSensor has pressure sensor, PenTablet does not */ 501ca94ec43SDaniel Drake input_set_abs_params(input, ABS_PRESSURE, 0, 15, 0, 0); 502ca94ec43SDaniel Drake 503ca94ec43SDaniel Drake /* From device specs */ 504ca94ec43SDaniel Drake input_set_abs_params(input, ABS_X, 0, 399, 0, 0); 505ca94ec43SDaniel Drake input_set_abs_params(input, ABS_Y, 0, 290, 0, 0); 506ca94ec43SDaniel Drake 507ca94ec43SDaniel Drake /* Calculated by hand based on usable size (52mm x 38mm) */ 508ca94ec43SDaniel Drake input_abs_set_res(input, ABS_X, 8); 509ca94ec43SDaniel Drake input_abs_set_res(input, ABS_Y, 8); 510ca94ec43SDaniel Drake break; 511ca94ec43SDaniel Drake 512ca94ec43SDaniel Drake case HGPK_MODE_PENTABLET: 513ca94ec43SDaniel Drake __set_bit(BTN_TOUCH, input->keybit); 514ca94ec43SDaniel Drake __set_bit(BTN_TOOL_FINGER, input->keybit); 515ca94ec43SDaniel Drake 516ca94ec43SDaniel Drake __set_bit(EV_ABS, input->evbit); 517ca94ec43SDaniel Drake 518ca94ec43SDaniel Drake /* From device specs */ 519ca94ec43SDaniel Drake input_set_abs_params(input, ABS_X, 0, 999, 0, 0); 520ca94ec43SDaniel Drake input_set_abs_params(input, ABS_Y, 5, 239, 0, 0); 521ca94ec43SDaniel Drake 522ca94ec43SDaniel Drake /* Calculated by hand based on usable size (156mm x 38mm) */ 523ca94ec43SDaniel Drake input_abs_set_res(input, ABS_X, 6); 524ca94ec43SDaniel Drake input_abs_set_res(input, ABS_Y, 8); 525ca94ec43SDaniel Drake break; 526ca94ec43SDaniel Drake 527ca94ec43SDaniel Drake default: 528ca94ec43SDaniel Drake BUG(); 529ca94ec43SDaniel Drake } 530ca94ec43SDaniel Drake } 531ca94ec43SDaniel Drake 532ca94ec43SDaniel Drake static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate) 533ca94ec43SDaniel Drake { 534ca94ec43SDaniel Drake int err; 535ca94ec43SDaniel Drake 536df08ef27SAndres Salomon psmouse_reset(psmouse); 537df08ef27SAndres Salomon 538ca94ec43SDaniel Drake if (recalibrate) { 539ca94ec43SDaniel Drake struct ps2dev *ps2dev = &psmouse->ps2dev; 540ca94ec43SDaniel Drake 541df08ef27SAndres Salomon /* send the recalibrate request */ 542df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, 0xf5) || 543df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xf5) || 544df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xe6) || 545df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xf5)) { 546df08ef27SAndres Salomon return -1; 547df08ef27SAndres Salomon } 548df08ef27SAndres Salomon 549df08ef27SAndres Salomon /* according to ALPS, 150mS is required for recalibration */ 550df08ef27SAndres Salomon msleep(150); 551ca94ec43SDaniel Drake } 552df08ef27SAndres Salomon 553ca94ec43SDaniel Drake err = hgpk_select_mode(psmouse); 554ca94ec43SDaniel Drake if (err) { 555ca94ec43SDaniel Drake hgpk_err(psmouse, "failed to select mode\n"); 556ca94ec43SDaniel Drake return err; 557ca94ec43SDaniel Drake } 558ca94ec43SDaniel Drake 559ca94ec43SDaniel Drake hgpk_reset_hack_state(psmouse); 560ca94ec43SDaniel Drake 561ca94ec43SDaniel Drake return 0; 562ca94ec43SDaniel Drake } 563ca94ec43SDaniel Drake 564ca94ec43SDaniel Drake static int hgpk_force_recalibrate(struct psmouse *psmouse) 565ca94ec43SDaniel Drake { 566ca94ec43SDaniel Drake struct ps2dev *ps2dev = &psmouse->ps2dev; 567ca94ec43SDaniel Drake struct hgpk_data *priv = psmouse->private; 568ca94ec43SDaniel Drake int err; 569ca94ec43SDaniel Drake 570ca94ec43SDaniel Drake /* C-series touchpads added the recalibrate command */ 571ca94ec43SDaniel Drake if (psmouse->model < HGPK_MODEL_C) 572ca94ec43SDaniel Drake return 0; 573ca94ec43SDaniel Drake 574ca94ec43SDaniel Drake /* we don't want to race with the irq handler, nor with resyncs */ 575ca94ec43SDaniel Drake psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); 576ca94ec43SDaniel Drake 577ca94ec43SDaniel Drake /* start by resetting the device */ 578ca94ec43SDaniel Drake err = hgpk_reset_device(psmouse, true); 579ca94ec43SDaniel Drake if (err) 580ca94ec43SDaniel Drake return err; 581ca94ec43SDaniel Drake 582ca94ec43SDaniel Drake /* 583ca94ec43SDaniel Drake * XXX: If a finger is down during this delay, recalibration will 584df08ef27SAndres Salomon * detect capacitance incorrectly. This is a hardware bug, and 585df08ef27SAndres Salomon * we don't have a good way to deal with it. The 2s window stuff 586df08ef27SAndres Salomon * (below) is our best option for now. 587df08ef27SAndres Salomon */ 588df08ef27SAndres Salomon 589df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) 590df08ef27SAndres Salomon return -1; 591df08ef27SAndres Salomon 592df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 593df08ef27SAndres Salomon 594ca94ec43SDaniel Drake /* 595ca94ec43SDaniel Drake * After we recalibrate, we shouldn't get any packets for 2s. If 596df08ef27SAndres Salomon * we do, it's likely that someone's finger was on the touchpad. 597df08ef27SAndres Salomon * If someone's finger *was* on the touchpad, it's probably 598df08ef27SAndres Salomon * miscalibrated. So, we should schedule another recalibration 599df08ef27SAndres Salomon */ 6008bbf2703SPaul Fox priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time); 601df08ef27SAndres Salomon 602df08ef27SAndres Salomon return 0; 603df08ef27SAndres Salomon } 604df08ef27SAndres Salomon 605df08ef27SAndres Salomon /* 606df08ef27SAndres Salomon * This kills power to the touchpad; according to ALPS, current consumption 607df08ef27SAndres Salomon * goes down to 50uA after running this. To turn power back on, we drive 608df08ef27SAndres Salomon * MS-DAT low. 609df08ef27SAndres Salomon */ 610df08ef27SAndres Salomon static int hgpk_toggle_power(struct psmouse *psmouse, int enable) 611df08ef27SAndres Salomon { 612df08ef27SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev; 613df08ef27SAndres Salomon int timeo; 614ca94ec43SDaniel Drake int err; 615df08ef27SAndres Salomon 616df08ef27SAndres Salomon /* Added on D-series touchpads */ 617df08ef27SAndres Salomon if (psmouse->model < HGPK_MODEL_D) 618df08ef27SAndres Salomon return 0; 619df08ef27SAndres Salomon 620df08ef27SAndres Salomon if (enable) { 621df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); 622df08ef27SAndres Salomon 623df08ef27SAndres Salomon /* 624df08ef27SAndres Salomon * Sending a byte will drive MS-DAT low; this will wake up 625df08ef27SAndres Salomon * the controller. Once we get an ACK back from it, it 626df08ef27SAndres Salomon * means we can continue with the touchpad re-init. ALPS 627df08ef27SAndres Salomon * tells us that 1s should be long enough, so set that as 628df08ef27SAndres Salomon * the upper bound. 629df08ef27SAndres Salomon */ 630df08ef27SAndres Salomon for (timeo = 20; timeo > 0; timeo--) { 631df08ef27SAndres Salomon if (!ps2_sendbyte(&psmouse->ps2dev, 632df08ef27SAndres Salomon PSMOUSE_CMD_DISABLE, 20)) 633df08ef27SAndres Salomon break; 634df08ef27SAndres Salomon msleep(50); 635df08ef27SAndres Salomon } 636df08ef27SAndres Salomon 637ca94ec43SDaniel Drake err = hgpk_reset_device(psmouse, false); 638ca94ec43SDaniel Drake if (err) { 639ca94ec43SDaniel Drake hgpk_err(psmouse, "Failed to reset device!\n"); 640ca94ec43SDaniel Drake return err; 641ca94ec43SDaniel Drake } 642df08ef27SAndres Salomon 643df08ef27SAndres Salomon /* should be all set, enable the touchpad */ 644df08ef27SAndres Salomon ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); 645df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 646df08ef27SAndres Salomon 647df08ef27SAndres Salomon } else { 648df08ef27SAndres Salomon hgpk_dbg(psmouse, "Powering off touchpad.\n"); 649df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_IGNORE); 650df08ef27SAndres Salomon 651df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, 0xec) || 652df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xec) || 653df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xea)) { 654df08ef27SAndres Salomon return -1; 655df08ef27SAndres Salomon } 656df08ef27SAndres Salomon 657df08ef27SAndres Salomon /* probably won't see an ACK, the touchpad will be off */ 658df08ef27SAndres Salomon ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); 659df08ef27SAndres Salomon } 660df08ef27SAndres Salomon 661df08ef27SAndres Salomon return 0; 662df08ef27SAndres Salomon } 663df08ef27SAndres Salomon 664df08ef27SAndres Salomon static int hgpk_poll(struct psmouse *psmouse) 665df08ef27SAndres Salomon { 666df08ef27SAndres Salomon /* We can't poll, so always return failure. */ 667df08ef27SAndres Salomon return -1; 668df08ef27SAndres Salomon } 669df08ef27SAndres Salomon 670df08ef27SAndres Salomon static int hgpk_reconnect(struct psmouse *psmouse) 671df08ef27SAndres Salomon { 672ca94ec43SDaniel Drake /* 673ca94ec43SDaniel Drake * During suspend/resume the ps2 rails remain powered. We don't want 674df08ef27SAndres Salomon * to do a reset because it's flush data out of buffers; however, 675ca94ec43SDaniel Drake * earlier prototypes (B1) had some brokenness that required a reset. 676ca94ec43SDaniel Drake */ 677df08ef27SAndres Salomon if (olpc_board_at_least(olpc_board(0xb2))) 678df08ef27SAndres Salomon if (psmouse->ps2dev.serio->dev.power.power_state.event != 679df08ef27SAndres Salomon PM_EVENT_ON) 680df08ef27SAndres Salomon return 0; 681df08ef27SAndres Salomon 682ca94ec43SDaniel Drake return hgpk_reset_device(psmouse, false); 683df08ef27SAndres Salomon } 684df08ef27SAndres Salomon 685df08ef27SAndres Salomon static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) 686df08ef27SAndres Salomon { 687df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 688df08ef27SAndres Salomon 689df08ef27SAndres Salomon return sprintf(buf, "%d\n", priv->powered); 690df08ef27SAndres Salomon } 691df08ef27SAndres Salomon 692df08ef27SAndres Salomon static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, 693df08ef27SAndres Salomon const char *buf, size_t count) 694df08ef27SAndres Salomon { 695df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 696df08ef27SAndres Salomon unsigned long value; 697df08ef27SAndres Salomon int err; 698df08ef27SAndres Salomon 699df08ef27SAndres Salomon err = strict_strtoul(buf, 10, &value); 700df08ef27SAndres Salomon if (err || value > 1) 701df08ef27SAndres Salomon return -EINVAL; 702df08ef27SAndres Salomon 703df08ef27SAndres Salomon if (value != priv->powered) { 704df08ef27SAndres Salomon /* 705df08ef27SAndres Salomon * hgpk_toggle_power will deal w/ state so 706df08ef27SAndres Salomon * we're not racing w/ irq 707df08ef27SAndres Salomon */ 708df08ef27SAndres Salomon err = hgpk_toggle_power(psmouse, value); 709df08ef27SAndres Salomon if (!err) 710df08ef27SAndres Salomon priv->powered = value; 711df08ef27SAndres Salomon } 712df08ef27SAndres Salomon 713df08ef27SAndres Salomon return err ? err : count; 714df08ef27SAndres Salomon } 715df08ef27SAndres Salomon 716df08ef27SAndres Salomon __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, 717b7802c5cSDmitry Torokhov hgpk_show_powered, hgpk_set_powered, false); 718df08ef27SAndres Salomon 719ca94ec43SDaniel Drake static ssize_t attr_show_mode(struct psmouse *psmouse, void *data, char *buf) 720ca94ec43SDaniel Drake { 721ca94ec43SDaniel Drake struct hgpk_data *priv = psmouse->private; 722ca94ec43SDaniel Drake 723ca94ec43SDaniel Drake return sprintf(buf, "%s\n", hgpk_mode_names[priv->mode]); 724ca94ec43SDaniel Drake } 725ca94ec43SDaniel Drake 726ca94ec43SDaniel Drake static ssize_t attr_set_mode(struct psmouse *psmouse, void *data, 727ca94ec43SDaniel Drake const char *buf, size_t len) 728ca94ec43SDaniel Drake { 729ca94ec43SDaniel Drake struct hgpk_data *priv = psmouse->private; 730ca94ec43SDaniel Drake enum hgpk_mode old_mode = priv->mode; 731ca94ec43SDaniel Drake enum hgpk_mode new_mode = hgpk_mode_from_name(buf, len); 732ca94ec43SDaniel Drake struct input_dev *old_dev = psmouse->dev; 733ca94ec43SDaniel Drake struct input_dev *new_dev; 734ca94ec43SDaniel Drake int err; 735ca94ec43SDaniel Drake 736ca94ec43SDaniel Drake if (new_mode == HGPK_MODE_INVALID) 737ca94ec43SDaniel Drake return -EINVAL; 738ca94ec43SDaniel Drake 739ca94ec43SDaniel Drake if (old_mode == new_mode) 740ca94ec43SDaniel Drake return len; 741ca94ec43SDaniel Drake 742ca94ec43SDaniel Drake new_dev = input_allocate_device(); 743ca94ec43SDaniel Drake if (!new_dev) 744ca94ec43SDaniel Drake return -ENOMEM; 745ca94ec43SDaniel Drake 746ca94ec43SDaniel Drake psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); 747ca94ec43SDaniel Drake 748ca94ec43SDaniel Drake /* Switch device into the new mode */ 749ca94ec43SDaniel Drake priv->mode = new_mode; 750ca94ec43SDaniel Drake err = hgpk_reset_device(psmouse, false); 751ca94ec43SDaniel Drake if (err) 752ca94ec43SDaniel Drake goto err_try_restore; 753ca94ec43SDaniel Drake 754ca94ec43SDaniel Drake hgpk_setup_input_device(new_dev, old_dev, new_mode); 755ca94ec43SDaniel Drake 756ca94ec43SDaniel Drake psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); 757ca94ec43SDaniel Drake 758ca94ec43SDaniel Drake err = input_register_device(new_dev); 759ca94ec43SDaniel Drake if (err) 760ca94ec43SDaniel Drake goto err_try_restore; 761ca94ec43SDaniel Drake 762ca94ec43SDaniel Drake psmouse->dev = new_dev; 763ca94ec43SDaniel Drake input_unregister_device(old_dev); 764ca94ec43SDaniel Drake 765ca94ec43SDaniel Drake return len; 766ca94ec43SDaniel Drake 767ca94ec43SDaniel Drake err_try_restore: 768ca94ec43SDaniel Drake input_free_device(new_dev); 769ca94ec43SDaniel Drake priv->mode = old_mode; 770ca94ec43SDaniel Drake hgpk_reset_device(psmouse, false); 771ca94ec43SDaniel Drake 772ca94ec43SDaniel Drake return err; 773ca94ec43SDaniel Drake } 774ca94ec43SDaniel Drake 775ca94ec43SDaniel Drake PSMOUSE_DEFINE_ATTR(hgpk_mode, S_IWUSR | S_IRUGO, NULL, 776ca94ec43SDaniel Drake attr_show_mode, attr_set_mode); 777ca94ec43SDaniel Drake 778c46dd1ebSPaul Fox static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse, 779c46dd1ebSPaul Fox void *data, char *buf) 780c46dd1ebSPaul Fox { 781c46dd1ebSPaul Fox return -EINVAL; 782c46dd1ebSPaul Fox } 783c46dd1ebSPaul Fox 784c46dd1ebSPaul Fox static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data, 785c46dd1ebSPaul Fox const char *buf, size_t count) 786c46dd1ebSPaul Fox { 787c46dd1ebSPaul Fox struct hgpk_data *priv = psmouse->private; 788c46dd1ebSPaul Fox unsigned long value; 789c46dd1ebSPaul Fox int err; 790c46dd1ebSPaul Fox 791c46dd1ebSPaul Fox err = strict_strtoul(buf, 10, &value); 792c46dd1ebSPaul Fox if (err || value != 1) 793c46dd1ebSPaul Fox return -EINVAL; 794c46dd1ebSPaul Fox 795c46dd1ebSPaul Fox /* 796c46dd1ebSPaul Fox * We queue work instead of doing recalibration right here 797c46dd1ebSPaul Fox * to avoid adding locking to to hgpk_force_recalibrate() 798c46dd1ebSPaul Fox * since workqueue provides serialization. 799c46dd1ebSPaul Fox */ 800c46dd1ebSPaul Fox psmouse_queue_work(psmouse, &priv->recalib_wq, 0); 801c46dd1ebSPaul Fox return count; 802c46dd1ebSPaul Fox } 803c46dd1ebSPaul Fox 804c46dd1ebSPaul Fox __PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL, 805b7802c5cSDmitry Torokhov hgpk_trigger_recal_show, hgpk_trigger_recal, false); 806c46dd1ebSPaul Fox 807df08ef27SAndres Salomon static void hgpk_disconnect(struct psmouse *psmouse) 808df08ef27SAndres Salomon { 809df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 810df08ef27SAndres Salomon 811df08ef27SAndres Salomon device_remove_file(&psmouse->ps2dev.serio->dev, 812df08ef27SAndres Salomon &psmouse_attr_powered.dattr); 813ca94ec43SDaniel Drake device_remove_file(&psmouse->ps2dev.serio->dev, 814ca94ec43SDaniel Drake &psmouse_attr_hgpk_mode.dattr); 815c46dd1ebSPaul Fox 816c46dd1ebSPaul Fox if (psmouse->model >= HGPK_MODEL_C) 817c46dd1ebSPaul Fox device_remove_file(&psmouse->ps2dev.serio->dev, 818c46dd1ebSPaul Fox &psmouse_attr_recalibrate.dattr); 819c46dd1ebSPaul Fox 820df08ef27SAndres Salomon psmouse_reset(psmouse); 821df08ef27SAndres Salomon kfree(priv); 822df08ef27SAndres Salomon } 823df08ef27SAndres Salomon 824df08ef27SAndres Salomon static void hgpk_recalib_work(struct work_struct *work) 825df08ef27SAndres Salomon { 826bf6aede7SJean Delvare struct delayed_work *w = to_delayed_work(work); 827df08ef27SAndres Salomon struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); 828df08ef27SAndres Salomon struct psmouse *psmouse = priv->psmouse; 829df08ef27SAndres Salomon 830df08ef27SAndres Salomon hgpk_dbg(psmouse, "recalibrating touchpad..\n"); 831df08ef27SAndres Salomon 832df08ef27SAndres Salomon if (hgpk_force_recalibrate(psmouse)) 833df08ef27SAndres Salomon hgpk_err(psmouse, "recalibration failed!\n"); 834df08ef27SAndres Salomon } 835df08ef27SAndres Salomon 836df08ef27SAndres Salomon static int hgpk_register(struct psmouse *psmouse) 837df08ef27SAndres Salomon { 838ca94ec43SDaniel Drake struct hgpk_data *priv = psmouse->private; 839df08ef27SAndres Salomon int err; 840df08ef27SAndres Salomon 841df08ef27SAndres Salomon /* register handlers */ 842df08ef27SAndres Salomon psmouse->protocol_handler = hgpk_process_byte; 843df08ef27SAndres Salomon psmouse->poll = hgpk_poll; 844df08ef27SAndres Salomon psmouse->disconnect = hgpk_disconnect; 845df08ef27SAndres Salomon psmouse->reconnect = hgpk_reconnect; 846df08ef27SAndres Salomon 847df08ef27SAndres Salomon /* Disable the idle resync. */ 848df08ef27SAndres Salomon psmouse->resync_time = 0; 849df08ef27SAndres Salomon /* Reset after a lot of bad bytes. */ 850df08ef27SAndres Salomon psmouse->resetafter = 1024; 851df08ef27SAndres Salomon 852ca94ec43SDaniel Drake hgpk_setup_input_device(psmouse->dev, NULL, priv->mode); 853ca94ec43SDaniel Drake 854df08ef27SAndres Salomon err = device_create_file(&psmouse->ps2dev.serio->dev, 855df08ef27SAndres Salomon &psmouse_attr_powered.dattr); 856c46dd1ebSPaul Fox if (err) { 857c46dd1ebSPaul Fox hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n"); 858df08ef27SAndres Salomon return err; 859df08ef27SAndres Salomon } 860df08ef27SAndres Salomon 861ca94ec43SDaniel Drake err = device_create_file(&psmouse->ps2dev.serio->dev, 862ca94ec43SDaniel Drake &psmouse_attr_hgpk_mode.dattr); 863ca94ec43SDaniel Drake if (err) { 864ca94ec43SDaniel Drake hgpk_err(psmouse, "Failed creating 'hgpk_mode' sysfs node\n"); 865ca94ec43SDaniel Drake goto err_remove_powered; 866ca94ec43SDaniel Drake } 867ca94ec43SDaniel Drake 868c46dd1ebSPaul Fox /* C-series touchpads added the recalibrate command */ 869c46dd1ebSPaul Fox if (psmouse->model >= HGPK_MODEL_C) { 870c46dd1ebSPaul Fox err = device_create_file(&psmouse->ps2dev.serio->dev, 871c46dd1ebSPaul Fox &psmouse_attr_recalibrate.dattr); 872c46dd1ebSPaul Fox if (err) { 873c46dd1ebSPaul Fox hgpk_err(psmouse, 874c46dd1ebSPaul Fox "Failed creating 'recalibrate' sysfs node\n"); 875ca94ec43SDaniel Drake goto err_remove_mode; 876c46dd1ebSPaul Fox } 877c46dd1ebSPaul Fox } 878c46dd1ebSPaul Fox 879c46dd1ebSPaul Fox return 0; 880ca94ec43SDaniel Drake 881ca94ec43SDaniel Drake err_remove_mode: 882ca94ec43SDaniel Drake device_remove_file(&psmouse->ps2dev.serio->dev, 883ca94ec43SDaniel Drake &psmouse_attr_hgpk_mode.dattr); 884ca94ec43SDaniel Drake err_remove_powered: 885ca94ec43SDaniel Drake device_remove_file(&psmouse->ps2dev.serio->dev, 886ca94ec43SDaniel Drake &psmouse_attr_powered.dattr); 887ca94ec43SDaniel Drake return err; 888c46dd1ebSPaul Fox } 889c46dd1ebSPaul Fox 890df08ef27SAndres Salomon int hgpk_init(struct psmouse *psmouse) 891df08ef27SAndres Salomon { 892df08ef27SAndres Salomon struct hgpk_data *priv; 893ca94ec43SDaniel Drake int err; 894df08ef27SAndres Salomon 895df08ef27SAndres Salomon priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); 896ca94ec43SDaniel Drake if (!priv) { 897ca94ec43SDaniel Drake err = -ENOMEM; 898df08ef27SAndres Salomon goto alloc_fail; 899ca94ec43SDaniel Drake } 900df08ef27SAndres Salomon 901df08ef27SAndres Salomon psmouse->private = priv; 902ca94ec43SDaniel Drake 903df08ef27SAndres Salomon priv->psmouse = psmouse; 904b7802c5cSDmitry Torokhov priv->powered = true; 905ca94ec43SDaniel Drake priv->mode = hgpk_default_mode; 906df08ef27SAndres Salomon INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); 907df08ef27SAndres Salomon 908ca94ec43SDaniel Drake err = hgpk_reset_device(psmouse, false); 909df08ef27SAndres Salomon if (err) 910df08ef27SAndres Salomon goto init_fail; 911df08ef27SAndres Salomon 912df08ef27SAndres Salomon err = hgpk_register(psmouse); 913df08ef27SAndres Salomon if (err) 914df08ef27SAndres Salomon goto init_fail; 915df08ef27SAndres Salomon 916df08ef27SAndres Salomon return 0; 917df08ef27SAndres Salomon 918df08ef27SAndres Salomon init_fail: 919df08ef27SAndres Salomon kfree(priv); 920df08ef27SAndres Salomon alloc_fail: 921df08ef27SAndres Salomon return err; 922df08ef27SAndres Salomon } 923df08ef27SAndres Salomon 924df08ef27SAndres Salomon static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) 925df08ef27SAndres Salomon { 926df08ef27SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev; 927df08ef27SAndres Salomon unsigned char param[3]; 928df08ef27SAndres Salomon 929df08ef27SAndres Salomon /* E7, E7, E7, E9 gets us a 3 byte identifier */ 930df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 931df08ef27SAndres Salomon ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 932df08ef27SAndres Salomon ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 933df08ef27SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 934df08ef27SAndres Salomon return -EIO; 935df08ef27SAndres Salomon } 936df08ef27SAndres Salomon 9370f495481SAndy Whitcroft hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]); 938df08ef27SAndres Salomon 939df08ef27SAndres Salomon /* HGPK signature: 0x67, 0x00, 0x<model> */ 940df08ef27SAndres Salomon if (param[0] != 0x67 || param[1] != 0x00) 941df08ef27SAndres Salomon return -ENODEV; 942df08ef27SAndres Salomon 943df08ef27SAndres Salomon hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); 944df08ef27SAndres Salomon 945df08ef27SAndres Salomon return param[2]; 946df08ef27SAndres Salomon } 947df08ef27SAndres Salomon 948b7802c5cSDmitry Torokhov int hgpk_detect(struct psmouse *psmouse, bool set_properties) 949df08ef27SAndres Salomon { 950df08ef27SAndres Salomon int version; 951df08ef27SAndres Salomon 952df08ef27SAndres Salomon version = hgpk_get_model(psmouse); 953df08ef27SAndres Salomon if (version < 0) 954df08ef27SAndres Salomon return version; 955df08ef27SAndres Salomon 956df08ef27SAndres Salomon if (set_properties) { 957df08ef27SAndres Salomon psmouse->vendor = "ALPS"; 958df08ef27SAndres Salomon psmouse->name = "HGPK"; 959df08ef27SAndres Salomon psmouse->model = version; 960df08ef27SAndres Salomon } 961df08ef27SAndres Salomon 962df08ef27SAndres Salomon return 0; 963df08ef27SAndres Salomon } 964ca94ec43SDaniel Drake 965ca94ec43SDaniel Drake void hgpk_module_init(void) 966ca94ec43SDaniel Drake { 967ca94ec43SDaniel Drake hgpk_default_mode = hgpk_mode_from_name(hgpk_mode_name, 968ca94ec43SDaniel Drake strlen(hgpk_mode_name)); 969ca94ec43SDaniel Drake if (hgpk_default_mode == HGPK_MODE_INVALID) { 970ca94ec43SDaniel Drake hgpk_default_mode = HGPK_MODE_MOUSE; 971ca94ec43SDaniel Drake strlcpy(hgpk_mode_name, hgpk_mode_names[HGPK_MODE_MOUSE], 972ca94ec43SDaniel Drake sizeof(hgpk_mode_name)); 973ca94ec43SDaniel Drake } 974ca94ec43SDaniel Drake } 975