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 33df08ef27SAndres Salomon #include <linux/input.h> 34df08ef27SAndres Salomon #include <linux/serio.h> 35df08ef27SAndres Salomon #include <linux/libps2.h> 36df08ef27SAndres Salomon #include <linux/delay.h> 37df08ef27SAndres Salomon #include <asm/olpc.h> 38df08ef27SAndres Salomon 39df08ef27SAndres Salomon #include "psmouse.h" 40df08ef27SAndres Salomon #include "hgpk.h" 41df08ef27SAndres Salomon 42df08ef27SAndres Salomon static int tpdebug; 43df08ef27SAndres Salomon module_param(tpdebug, int, 0644); 44df08ef27SAndres Salomon MODULE_PARM_DESC(tpdebug, "enable debugging, dumping packets to KERN_DEBUG."); 45df08ef27SAndres Salomon 46df08ef27SAndres Salomon static int recalib_delta = 100; 47df08ef27SAndres Salomon module_param(recalib_delta, int, 0644); 48df08ef27SAndres Salomon MODULE_PARM_DESC(recalib_delta, 49df08ef27SAndres Salomon "packets containing a delta this large will cause a recalibration."); 50df08ef27SAndres Salomon 518bbf2703SPaul Fox static int jumpy_delay = 1000; 528bbf2703SPaul Fox module_param(jumpy_delay, int, 0644); 538bbf2703SPaul Fox MODULE_PARM_DESC(jumpy_delay, 548bbf2703SPaul Fox "delay (ms) before recal after jumpiness detected"); 558bbf2703SPaul Fox 568bbf2703SPaul Fox static int spew_delay = 1000; 578bbf2703SPaul Fox module_param(spew_delay, int, 0644); 588bbf2703SPaul Fox MODULE_PARM_DESC(spew_delay, 598bbf2703SPaul Fox "delay (ms) before recal after packet spew detected"); 608bbf2703SPaul Fox 618bbf2703SPaul Fox static int recal_guard_time = 2000; 628bbf2703SPaul Fox module_param(recal_guard_time, int, 0644); 638bbf2703SPaul Fox MODULE_PARM_DESC(recal_guard_time, 648bbf2703SPaul Fox "interval (ms) during which recal will be restarted if packet received"); 658bbf2703SPaul Fox 668bbf2703SPaul Fox static int post_interrupt_delay = 1000; 678bbf2703SPaul Fox module_param(post_interrupt_delay, int, 0644); 688bbf2703SPaul Fox MODULE_PARM_DESC(post_interrupt_delay, 698bbf2703SPaul Fox "delay (ms) before recal after recal interrupt detected"); 708bbf2703SPaul Fox 718bbf2703SPaul Fox static int autorecal = 1; 728bbf2703SPaul Fox module_param(autorecal, int, 0644); 738bbf2703SPaul Fox MODULE_PARM_DESC(autorecal, "enable recalibration in the driver"); 748bbf2703SPaul Fox 75df08ef27SAndres Salomon /* 76df08ef27SAndres Salomon * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" 77df08ef27SAndres Salomon * above the pad and still have it send packets. This causes a jump cursor 78df08ef27SAndres Salomon * when one places their finger on the pad. We can probably detect the 79df08ef27SAndres Salomon * jump as we see a large deltas (>= 100px). In mouse mode, I've been 80df08ef27SAndres Salomon * unable to even come close to 100px deltas during normal usage, so I think 81df08ef27SAndres Salomon * this threshold is safe. If a large delta occurs, trigger a recalibration. 82df08ef27SAndres Salomon */ 83df08ef27SAndres Salomon static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) 84df08ef27SAndres Salomon { 85df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 86df08ef27SAndres Salomon 87df08ef27SAndres Salomon if (abs(x) > recalib_delta || abs(y) > recalib_delta) { 88df08ef27SAndres Salomon hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", 89df08ef27SAndres Salomon recalib_delta, x, y); 90df08ef27SAndres Salomon /* My car gets forty rods to the hogshead and that's the 91df08ef27SAndres Salomon * way I likes it! */ 92df08ef27SAndres Salomon psmouse_queue_work(psmouse, &priv->recalib_wq, 938bbf2703SPaul Fox msecs_to_jiffies(jumpy_delay)); 94df08ef27SAndres Salomon } 95df08ef27SAndres Salomon } 96df08ef27SAndres Salomon 97df08ef27SAndres Salomon /* 98df08ef27SAndres Salomon * We have no idea why this particular hardware bug occurs. The touchpad 99df08ef27SAndres Salomon * will randomly start spewing packets without anything touching the 100df08ef27SAndres Salomon * pad. This wouldn't necessarily be bad, but it's indicative of a 101df08ef27SAndres Salomon * severely miscalibrated pad; attempting to use the touchpad while it's 102df08ef27SAndres Salomon * spewing means the cursor will jump all over the place, and act "drunk". 103df08ef27SAndres Salomon * 104df08ef27SAndres Salomon * The packets that are spewed tend to all have deltas between -2 and 2, and 105df08ef27SAndres Salomon * the cursor will move around without really going very far. It will 106df08ef27SAndres Salomon * tend to end up in the same location; if we tally up the changes over 107df08ef27SAndres Salomon * 100 packets, we end up w/ a final delta of close to 0. This happens 108df08ef27SAndres Salomon * pretty regularly when the touchpad is spewing, and is pretty hard to 109df08ef27SAndres Salomon * manually trigger (at least for *my* fingers). So, it makes a perfect 110df08ef27SAndres Salomon * scheme for detecting spews. 111df08ef27SAndres Salomon */ 112df08ef27SAndres Salomon static void hgpk_spewing_hack(struct psmouse *psmouse, 113df08ef27SAndres Salomon int l, int r, int x, int y) 114df08ef27SAndres Salomon { 115df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 116df08ef27SAndres Salomon 117df08ef27SAndres Salomon /* ignore button press packets; many in a row could trigger 118df08ef27SAndres Salomon * a false-positive! */ 119df08ef27SAndres Salomon if (l || r) 120df08ef27SAndres Salomon return; 121df08ef27SAndres Salomon 122df08ef27SAndres Salomon priv->x_tally += x; 123df08ef27SAndres Salomon priv->y_tally += y; 124df08ef27SAndres Salomon 125df08ef27SAndres Salomon if (++priv->count > 100) { 126df08ef27SAndres Salomon if (abs(priv->x_tally) < 3 && abs(priv->y_tally) < 3) { 127df08ef27SAndres Salomon hgpk_dbg(psmouse, "packet spew detected (%d,%d)\n", 128df08ef27SAndres Salomon priv->x_tally, priv->y_tally); 129df08ef27SAndres Salomon psmouse_queue_work(psmouse, &priv->recalib_wq, 1308bbf2703SPaul Fox msecs_to_jiffies(spew_delay)); 131df08ef27SAndres Salomon } 132df08ef27SAndres Salomon /* reset every 100 packets */ 133df08ef27SAndres Salomon priv->count = 0; 134df08ef27SAndres Salomon priv->x_tally = 0; 135df08ef27SAndres Salomon priv->y_tally = 0; 136df08ef27SAndres Salomon } 137df08ef27SAndres Salomon } 138df08ef27SAndres Salomon 139df08ef27SAndres Salomon /* 140df08ef27SAndres Salomon * HGPK Mouse Mode format (standard mouse format, sans middle button) 141df08ef27SAndres Salomon * 142df08ef27SAndres Salomon * byte 0: y-over x-over y-neg x-neg 1 0 swr swl 143df08ef27SAndres Salomon * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 144df08ef27SAndres Salomon * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 145df08ef27SAndres Salomon * 146df08ef27SAndres Salomon * swr/swl are the left/right buttons. 147df08ef27SAndres Salomon * x-neg/y-neg are the x and y delta negative bits 148df08ef27SAndres Salomon * x-over/y-over are the x and y overflow bits 149df08ef27SAndres Salomon */ 150df08ef27SAndres Salomon static int hgpk_validate_byte(unsigned char *packet) 151df08ef27SAndres Salomon { 1525fb17fd9SAndres Salomon return (packet[0] & 0x0C) != 0x08; 153df08ef27SAndres Salomon } 154df08ef27SAndres Salomon 155df08ef27SAndres Salomon static void hgpk_process_packet(struct psmouse *psmouse) 156df08ef27SAndres Salomon { 157df08ef27SAndres Salomon struct input_dev *dev = psmouse->dev; 158df08ef27SAndres Salomon unsigned char *packet = psmouse->packet; 159df08ef27SAndres Salomon int x, y, left, right; 160df08ef27SAndres Salomon 161df08ef27SAndres Salomon left = packet[0] & 1; 162df08ef27SAndres Salomon right = (packet[0] >> 1) & 1; 163df08ef27SAndres Salomon 164df08ef27SAndres Salomon x = packet[1] - ((packet[0] << 4) & 0x100); 165df08ef27SAndres Salomon y = ((packet[0] << 3) & 0x100) - packet[2]; 166df08ef27SAndres Salomon 167df08ef27SAndres Salomon hgpk_jumpy_hack(psmouse, x, y); 168df08ef27SAndres Salomon hgpk_spewing_hack(psmouse, left, right, x, y); 169df08ef27SAndres Salomon 170df08ef27SAndres Salomon if (tpdebug) 171df08ef27SAndres Salomon hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); 172df08ef27SAndres Salomon 173df08ef27SAndres Salomon input_report_key(dev, BTN_LEFT, left); 174df08ef27SAndres Salomon input_report_key(dev, BTN_RIGHT, right); 175df08ef27SAndres Salomon 176df08ef27SAndres Salomon input_report_rel(dev, REL_X, x); 177df08ef27SAndres Salomon input_report_rel(dev, REL_Y, y); 178df08ef27SAndres Salomon 179df08ef27SAndres Salomon input_sync(dev); 180df08ef27SAndres Salomon } 181df08ef27SAndres Salomon 182df08ef27SAndres Salomon static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) 183df08ef27SAndres Salomon { 184df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 185df08ef27SAndres Salomon 186df08ef27SAndres Salomon if (hgpk_validate_byte(psmouse->packet)) { 187df08ef27SAndres Salomon hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n", 188df08ef27SAndres Salomon __func__, psmouse->pktcnt, psmouse->packet[0], 189df08ef27SAndres Salomon psmouse->packet[1], psmouse->packet[2]); 190df08ef27SAndres Salomon return PSMOUSE_BAD_DATA; 191df08ef27SAndres Salomon } 192df08ef27SAndres Salomon 193df08ef27SAndres Salomon if (psmouse->pktcnt >= psmouse->pktsize) { 194df08ef27SAndres Salomon hgpk_process_packet(psmouse); 195df08ef27SAndres Salomon return PSMOUSE_FULL_PACKET; 196df08ef27SAndres Salomon } 197df08ef27SAndres Salomon 198df08ef27SAndres Salomon if (priv->recalib_window) { 199df08ef27SAndres Salomon if (time_before(jiffies, priv->recalib_window)) { 200df08ef27SAndres Salomon /* 201df08ef27SAndres Salomon * ugh, got a packet inside our recalibration 202df08ef27SAndres Salomon * window, schedule another recalibration. 203df08ef27SAndres Salomon */ 204df08ef27SAndres Salomon hgpk_dbg(psmouse, 205df08ef27SAndres Salomon "packet inside calibration window, " 206df08ef27SAndres Salomon "queueing another recalibration\n"); 207df08ef27SAndres Salomon psmouse_queue_work(psmouse, &priv->recalib_wq, 2088bbf2703SPaul Fox msecs_to_jiffies(post_interrupt_delay)); 209df08ef27SAndres Salomon } 210df08ef27SAndres Salomon priv->recalib_window = 0; 211df08ef27SAndres Salomon } 212df08ef27SAndres Salomon 213df08ef27SAndres Salomon return PSMOUSE_GOOD_DATA; 214df08ef27SAndres Salomon } 215df08ef27SAndres Salomon 216df08ef27SAndres Salomon static int hgpk_force_recalibrate(struct psmouse *psmouse) 217df08ef27SAndres Salomon { 218df08ef27SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev; 219df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 220df08ef27SAndres Salomon 221df08ef27SAndres Salomon /* C-series touchpads added the recalibrate command */ 222df08ef27SAndres Salomon if (psmouse->model < HGPK_MODEL_C) 223df08ef27SAndres Salomon return 0; 224df08ef27SAndres Salomon 225df08ef27SAndres Salomon /* we don't want to race with the irq handler, nor with resyncs */ 226df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); 227df08ef27SAndres Salomon 228df08ef27SAndres Salomon /* start by resetting the device */ 229df08ef27SAndres Salomon psmouse_reset(psmouse); 230df08ef27SAndres Salomon 231df08ef27SAndres Salomon /* send the recalibrate request */ 232df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, 0xf5) || 233df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xf5) || 234df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xe6) || 235df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xf5)) { 236df08ef27SAndres Salomon return -1; 237df08ef27SAndres Salomon } 238df08ef27SAndres Salomon 239df08ef27SAndres Salomon /* according to ALPS, 150mS is required for recalibration */ 240df08ef27SAndres Salomon msleep(150); 241df08ef27SAndres Salomon 242df08ef27SAndres Salomon /* XXX: If a finger is down during this delay, recalibration will 243df08ef27SAndres Salomon * detect capacitance incorrectly. This is a hardware bug, and 244df08ef27SAndres Salomon * we don't have a good way to deal with it. The 2s window stuff 245df08ef27SAndres Salomon * (below) is our best option for now. 246df08ef27SAndres Salomon */ 247df08ef27SAndres Salomon 248df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) 249df08ef27SAndres Salomon return -1; 250df08ef27SAndres Salomon 251df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 252df08ef27SAndres Salomon 253df08ef27SAndres Salomon /* After we recalibrate, we shouldn't get any packets for 2s. If 254df08ef27SAndres Salomon * we do, it's likely that someone's finger was on the touchpad. 255df08ef27SAndres Salomon * If someone's finger *was* on the touchpad, it's probably 256df08ef27SAndres Salomon * miscalibrated. So, we should schedule another recalibration 257df08ef27SAndres Salomon */ 2588bbf2703SPaul Fox priv->recalib_window = jiffies + msecs_to_jiffies(recal_guard_time); 259df08ef27SAndres Salomon 260df08ef27SAndres Salomon return 0; 261df08ef27SAndres Salomon } 262df08ef27SAndres Salomon 263df08ef27SAndres Salomon /* 264df08ef27SAndres Salomon * This kills power to the touchpad; according to ALPS, current consumption 265df08ef27SAndres Salomon * goes down to 50uA after running this. To turn power back on, we drive 266df08ef27SAndres Salomon * MS-DAT low. 267df08ef27SAndres Salomon */ 268df08ef27SAndres Salomon static int hgpk_toggle_power(struct psmouse *psmouse, int enable) 269df08ef27SAndres Salomon { 270df08ef27SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev; 271df08ef27SAndres Salomon int timeo; 272df08ef27SAndres Salomon 273df08ef27SAndres Salomon /* Added on D-series touchpads */ 274df08ef27SAndres Salomon if (psmouse->model < HGPK_MODEL_D) 275df08ef27SAndres Salomon return 0; 276df08ef27SAndres Salomon 277df08ef27SAndres Salomon if (enable) { 278df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); 279df08ef27SAndres Salomon 280df08ef27SAndres Salomon /* 281df08ef27SAndres Salomon * Sending a byte will drive MS-DAT low; this will wake up 282df08ef27SAndres Salomon * the controller. Once we get an ACK back from it, it 283df08ef27SAndres Salomon * means we can continue with the touchpad re-init. ALPS 284df08ef27SAndres Salomon * tells us that 1s should be long enough, so set that as 285df08ef27SAndres Salomon * the upper bound. 286df08ef27SAndres Salomon */ 287df08ef27SAndres Salomon for (timeo = 20; timeo > 0; timeo--) { 288df08ef27SAndres Salomon if (!ps2_sendbyte(&psmouse->ps2dev, 289df08ef27SAndres Salomon PSMOUSE_CMD_DISABLE, 20)) 290df08ef27SAndres Salomon break; 291df08ef27SAndres Salomon msleep(50); 292df08ef27SAndres Salomon } 293df08ef27SAndres Salomon 294df08ef27SAndres Salomon psmouse_reset(psmouse); 295df08ef27SAndres Salomon 296df08ef27SAndres Salomon /* should be all set, enable the touchpad */ 297df08ef27SAndres Salomon ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); 298df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); 299df08ef27SAndres Salomon 300df08ef27SAndres Salomon } else { 301df08ef27SAndres Salomon hgpk_dbg(psmouse, "Powering off touchpad.\n"); 302df08ef27SAndres Salomon psmouse_set_state(psmouse, PSMOUSE_IGNORE); 303df08ef27SAndres Salomon 304df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, 0xec) || 305df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xec) || 306df08ef27SAndres Salomon ps2_command(ps2dev, NULL, 0xea)) { 307df08ef27SAndres Salomon return -1; 308df08ef27SAndres Salomon } 309df08ef27SAndres Salomon 310df08ef27SAndres Salomon /* probably won't see an ACK, the touchpad will be off */ 311df08ef27SAndres Salomon ps2_sendbyte(&psmouse->ps2dev, 0xec, 20); 312df08ef27SAndres Salomon } 313df08ef27SAndres Salomon 314df08ef27SAndres Salomon return 0; 315df08ef27SAndres Salomon } 316df08ef27SAndres Salomon 317df08ef27SAndres Salomon static int hgpk_poll(struct psmouse *psmouse) 318df08ef27SAndres Salomon { 319df08ef27SAndres Salomon /* We can't poll, so always return failure. */ 320df08ef27SAndres Salomon return -1; 321df08ef27SAndres Salomon } 322df08ef27SAndres Salomon 323df08ef27SAndres Salomon static int hgpk_reconnect(struct psmouse *psmouse) 324df08ef27SAndres Salomon { 325df08ef27SAndres Salomon /* During suspend/resume the ps2 rails remain powered. We don't want 326df08ef27SAndres Salomon * to do a reset because it's flush data out of buffers; however, 327df08ef27SAndres Salomon * earlier prototypes (B1) had some brokenness that required a reset. */ 328df08ef27SAndres Salomon if (olpc_board_at_least(olpc_board(0xb2))) 329df08ef27SAndres Salomon if (psmouse->ps2dev.serio->dev.power.power_state.event != 330df08ef27SAndres Salomon PM_EVENT_ON) 331df08ef27SAndres Salomon return 0; 332df08ef27SAndres Salomon 333df08ef27SAndres Salomon psmouse_reset(psmouse); 334df08ef27SAndres Salomon 335df08ef27SAndres Salomon return 0; 336df08ef27SAndres Salomon } 337df08ef27SAndres Salomon 338df08ef27SAndres Salomon static ssize_t hgpk_show_powered(struct psmouse *psmouse, void *data, char *buf) 339df08ef27SAndres Salomon { 340df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 341df08ef27SAndres Salomon 342df08ef27SAndres Salomon return sprintf(buf, "%d\n", priv->powered); 343df08ef27SAndres Salomon } 344df08ef27SAndres Salomon 345df08ef27SAndres Salomon static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data, 346df08ef27SAndres Salomon const char *buf, size_t count) 347df08ef27SAndres Salomon { 348df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 349df08ef27SAndres Salomon unsigned long value; 350df08ef27SAndres Salomon int err; 351df08ef27SAndres Salomon 352df08ef27SAndres Salomon err = strict_strtoul(buf, 10, &value); 353df08ef27SAndres Salomon if (err || value > 1) 354df08ef27SAndres Salomon return -EINVAL; 355df08ef27SAndres Salomon 356df08ef27SAndres Salomon if (value != priv->powered) { 357df08ef27SAndres Salomon /* 358df08ef27SAndres Salomon * hgpk_toggle_power will deal w/ state so 359df08ef27SAndres Salomon * we're not racing w/ irq 360df08ef27SAndres Salomon */ 361df08ef27SAndres Salomon err = hgpk_toggle_power(psmouse, value); 362df08ef27SAndres Salomon if (!err) 363df08ef27SAndres Salomon priv->powered = value; 364df08ef27SAndres Salomon } 365df08ef27SAndres Salomon 366df08ef27SAndres Salomon return err ? err : count; 367df08ef27SAndres Salomon } 368df08ef27SAndres Salomon 369df08ef27SAndres Salomon __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL, 370df08ef27SAndres Salomon hgpk_show_powered, hgpk_set_powered, 0); 371df08ef27SAndres Salomon 372df08ef27SAndres Salomon static void hgpk_disconnect(struct psmouse *psmouse) 373df08ef27SAndres Salomon { 374df08ef27SAndres Salomon struct hgpk_data *priv = psmouse->private; 375df08ef27SAndres Salomon 376df08ef27SAndres Salomon device_remove_file(&psmouse->ps2dev.serio->dev, 377df08ef27SAndres Salomon &psmouse_attr_powered.dattr); 378df08ef27SAndres Salomon psmouse_reset(psmouse); 379df08ef27SAndres Salomon kfree(priv); 380df08ef27SAndres Salomon } 381df08ef27SAndres Salomon 382df08ef27SAndres Salomon static void hgpk_recalib_work(struct work_struct *work) 383df08ef27SAndres Salomon { 384*bf6aede7SJean Delvare struct delayed_work *w = to_delayed_work(work); 385df08ef27SAndres Salomon struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); 386df08ef27SAndres Salomon struct psmouse *psmouse = priv->psmouse; 387df08ef27SAndres Salomon 388df08ef27SAndres Salomon hgpk_dbg(psmouse, "recalibrating touchpad..\n"); 389df08ef27SAndres Salomon 390df08ef27SAndres Salomon if (hgpk_force_recalibrate(psmouse)) 391df08ef27SAndres Salomon hgpk_err(psmouse, "recalibration failed!\n"); 392df08ef27SAndres Salomon } 393df08ef27SAndres Salomon 394df08ef27SAndres Salomon static int hgpk_register(struct psmouse *psmouse) 395df08ef27SAndres Salomon { 396df08ef27SAndres Salomon struct input_dev *dev = psmouse->dev; 397df08ef27SAndres Salomon int err; 398df08ef27SAndres Salomon 399df08ef27SAndres Salomon /* unset the things that psmouse-base sets which we don't have */ 400df08ef27SAndres Salomon __clear_bit(BTN_MIDDLE, dev->keybit); 401df08ef27SAndres Salomon 402df08ef27SAndres Salomon /* set the things we do have */ 403df08ef27SAndres Salomon __set_bit(EV_KEY, dev->evbit); 404df08ef27SAndres Salomon __set_bit(EV_REL, dev->evbit); 405df08ef27SAndres Salomon 406df08ef27SAndres Salomon __set_bit(REL_X, dev->relbit); 407df08ef27SAndres Salomon __set_bit(REL_Y, dev->relbit); 408df08ef27SAndres Salomon 409df08ef27SAndres Salomon __set_bit(BTN_LEFT, dev->keybit); 410df08ef27SAndres Salomon __set_bit(BTN_RIGHT, dev->keybit); 411df08ef27SAndres Salomon 412df08ef27SAndres Salomon /* register handlers */ 413df08ef27SAndres Salomon psmouse->protocol_handler = hgpk_process_byte; 414df08ef27SAndres Salomon psmouse->poll = hgpk_poll; 415df08ef27SAndres Salomon psmouse->disconnect = hgpk_disconnect; 416df08ef27SAndres Salomon psmouse->reconnect = hgpk_reconnect; 417df08ef27SAndres Salomon psmouse->pktsize = 3; 418df08ef27SAndres Salomon 419df08ef27SAndres Salomon /* Disable the idle resync. */ 420df08ef27SAndres Salomon psmouse->resync_time = 0; 421df08ef27SAndres Salomon /* Reset after a lot of bad bytes. */ 422df08ef27SAndres Salomon psmouse->resetafter = 1024; 423df08ef27SAndres Salomon 424df08ef27SAndres Salomon err = device_create_file(&psmouse->ps2dev.serio->dev, 425df08ef27SAndres Salomon &psmouse_attr_powered.dattr); 426df08ef27SAndres Salomon if (err) 427df08ef27SAndres Salomon hgpk_err(psmouse, "Failed to create sysfs attribute\n"); 428df08ef27SAndres Salomon 429df08ef27SAndres Salomon return err; 430df08ef27SAndres Salomon } 431df08ef27SAndres Salomon 432df08ef27SAndres Salomon int hgpk_init(struct psmouse *psmouse) 433df08ef27SAndres Salomon { 434df08ef27SAndres Salomon struct hgpk_data *priv; 435df08ef27SAndres Salomon int err = -ENOMEM; 436df08ef27SAndres Salomon 437df08ef27SAndres Salomon priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); 438df08ef27SAndres Salomon if (!priv) 439df08ef27SAndres Salomon goto alloc_fail; 440df08ef27SAndres Salomon 441df08ef27SAndres Salomon psmouse->private = priv; 442df08ef27SAndres Salomon priv->psmouse = psmouse; 443df08ef27SAndres Salomon priv->powered = 1; 444df08ef27SAndres Salomon INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); 445df08ef27SAndres Salomon 446df08ef27SAndres Salomon err = psmouse_reset(psmouse); 447df08ef27SAndres Salomon if (err) 448df08ef27SAndres Salomon goto init_fail; 449df08ef27SAndres Salomon 450df08ef27SAndres Salomon err = hgpk_register(psmouse); 451df08ef27SAndres Salomon if (err) 452df08ef27SAndres Salomon goto init_fail; 453df08ef27SAndres Salomon 454df08ef27SAndres Salomon return 0; 455df08ef27SAndres Salomon 456df08ef27SAndres Salomon init_fail: 457df08ef27SAndres Salomon kfree(priv); 458df08ef27SAndres Salomon alloc_fail: 459df08ef27SAndres Salomon return err; 460df08ef27SAndres Salomon } 461df08ef27SAndres Salomon 462df08ef27SAndres Salomon static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) 463df08ef27SAndres Salomon { 464df08ef27SAndres Salomon struct ps2dev *ps2dev = &psmouse->ps2dev; 465df08ef27SAndres Salomon unsigned char param[3]; 466df08ef27SAndres Salomon 467df08ef27SAndres Salomon /* E7, E7, E7, E9 gets us a 3 byte identifier */ 468df08ef27SAndres Salomon if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 469df08ef27SAndres Salomon ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 470df08ef27SAndres Salomon ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 471df08ef27SAndres Salomon ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 472df08ef27SAndres Salomon return -EIO; 473df08ef27SAndres Salomon } 474df08ef27SAndres Salomon 475df08ef27SAndres Salomon hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]); 476df08ef27SAndres Salomon 477df08ef27SAndres Salomon /* HGPK signature: 0x67, 0x00, 0x<model> */ 478df08ef27SAndres Salomon if (param[0] != 0x67 || param[1] != 0x00) 479df08ef27SAndres Salomon return -ENODEV; 480df08ef27SAndres Salomon 481df08ef27SAndres Salomon hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); 482df08ef27SAndres Salomon 483df08ef27SAndres Salomon return param[2]; 484df08ef27SAndres Salomon } 485df08ef27SAndres Salomon 486df08ef27SAndres Salomon int hgpk_detect(struct psmouse *psmouse, int set_properties) 487df08ef27SAndres Salomon { 488df08ef27SAndres Salomon int version; 489df08ef27SAndres Salomon 490df08ef27SAndres Salomon version = hgpk_get_model(psmouse); 491df08ef27SAndres Salomon if (version < 0) 492df08ef27SAndres Salomon return version; 493df08ef27SAndres Salomon 494df08ef27SAndres Salomon if (set_properties) { 495df08ef27SAndres Salomon psmouse->vendor = "ALPS"; 496df08ef27SAndres Salomon psmouse->name = "HGPK"; 497df08ef27SAndres Salomon psmouse->model = version; 498df08ef27SAndres Salomon } 499df08ef27SAndres Salomon 500df08ef27SAndres Salomon return 0; 501df08ef27SAndres Salomon } 502