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