diff options
author | Andres Salomon <dilinger@debian.org> | 2008-07-06 03:26:01 -0400 |
---|---|---|
committer | Andres Salomon <dilinger@debian.org> | 2008-07-06 20:46:05 -0400 |
commit | 4def8439648ce8090b526c29aa2ab3a37b221b13 (patch) | |
tree | 5cce4beae132939349685582d4201efe78519620 /drivers/input | |
parent | a4933862c5862493ef4e9648c124655cc6e98b77 (diff) | |
download | lwn-4def8439648ce8090b526c29aa2ab3a37b221b13.tar.gz lwn-4def8439648ce8090b526c29aa2ab3a37b221b13.zip |
psmouse: olpc: drop advanced/stream mode stuff, use mouse mode instead
This drops lots of complexity, and works around a great deal of hardware
bugginess. With mouse mode, we have 3 byte packets, get them at a rate
of 12mS, and they're sent only when there's activity on the touchpad.
This makes deltas between packets _much_ smaller, making it much easier
to detect hardware miscalibration (100px deltas are a sign of
miscalibration; previously, due to the 24mS+ time between packets, we
couldn't guarantee that the delta wasn't intentional).
We also drop the PT/GS switching stuff; sorry, but PT mode is no longer
supported.
Signed-off-by: Andres Salomon <dilinger@debian.org>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/mouse/olpc.c | 678 | ||||
-rw-r--r-- | drivers/input/mouse/olpc.h | 34 |
2 files changed, 178 insertions, 534 deletions
diff --git a/drivers/input/mouse/olpc.c b/drivers/input/mouse/olpc.c index d7050b6b15a0..bc424c5f7977 100644 --- a/drivers/input/mouse/olpc.c +++ b/drivers/input/mouse/olpc.c @@ -43,291 +43,145 @@ static int tpdebug; module_param(tpdebug, int, 0644); -static int ignore_delta = 60; -module_param(ignore_delta, int, 0644); -MODULE_PARM_DESC(ignore_delta, "ignore packets that cause an X or Y delta larger than this value."); +static int recalib_delta = 100; +module_param(recalib_delta, int, 0644); +MODULE_PARM_DESC(recalib_delta, + "packets containing a delta this large will cause a recalibration."); /* - * With older hardware, a finger-up event is sometimes not sent. If it's been - * more than 50mS since the last packet, we can safely assume that there was - * a finger-up event that we never received. + * When the touchpad gets ultra-sensitive, one can keep their finger 1/2" + * above the pad and still have it send packets. This causes a jump cursor + * when one places their finger on the pad. We can probably detect the + * jump as we see a large deltas (>= 100px). In mouse mode, I've been + * unable to even come close to 100px deltas during normal usage, so I think + * this threshold is safe. If a large delta occurs, trigger a recalibration. */ -static void hgpk_fingerup_hack(struct psmouse *psmouse, struct hgpk_packet *p) +static void hgpk_jumpy_hack(struct psmouse *psmouse, int x, int y) { struct hgpk_data *priv = psmouse->private; - struct timeval now_tv; - s64 now_ns; - if (psmouse->model >= HGPK_MODEL_C) - return; - - if (p->gs_down || p->pt_down) { - do_gettimeofday(&now_tv); - now_ns = timeval_to_ns(&now_tv); - - if (priv->late && now_ns >= priv->late) { - struct input_dev *pt = psmouse->dev; - struct input_dev *gs = priv->gs; - - input_report_key(pt, BTN_TOUCH, 0); - input_report_key(gs, BTN_TOUCH, 0); - input_sync(pt); - input_sync(gs); - hgpk_dbg(psmouse, "Missing finger-up packet detected, " - "working around buggy hardware.\n"); - } - priv->late = now_ns + (50 * NSEC_PER_MSEC); - } else - priv->late = 0; -} - -/* - * C and D series touchpads send an extra finger-up packet to ensure we've - * seen it. That's all well and good, but for some uncomprehensible reason - * they sometimes also get stuck in a state where they also send an - * extra finger-down packet with coordinates of x=0, y=0. This royally - * screws relative positioning; end users see it as the touchpad jumping - * around when they first put their finger down. This works around that. - * - * *Sigh*. ALPS.. - */ -static void hgpk_fingerdown_hack(struct psmouse *psmouse, struct hgpk_packet *p) -{ - if (psmouse->model < HGPK_MODEL_C) - return; - - /* we only care about x=0, y=0 packets */ - if (p->x != 0 || p->y != 0) - return; - - /* - * if we're a gs_down packet but we were not previously down, - * we're going to assume that this is one of those spurious packets - * that needs to be worked around. - */ - if (p->gs_down && !test_bit(BTN_TOUCH, p->dev->key)) { - hgpk_dbg(psmouse, "spurious GS finger-down packet\n"); - p->gs_down = 0; - } else if (p->pt_down && !test_bit(BTN_TOUCH, p->dev->key)) { - hgpk_dbg(psmouse, "spurious PT finger-down packet\n"); - p->pt_down = 0; - } -} - -/* - * In general, we have lots of calibration problems that manifest - * themselves as jumpy mouse pointers. Miscalibration, capacitance issues - * with the hardware, etc; these make the touchpad detect errant packets - * at random places all over the place. Since we don't expect large deltas - * to ever actually be useful, we'll large axis changes that go over our - * threshold. - */ -static void hgpk_big_delta_hack(struct psmouse *psmouse, struct hgpk_packet *p) -{ - struct hgpk_data *priv = psmouse->private; - struct input_dev *dev = p->dev; - - /* afaik, this happens on all hardware */ - - /* ignore finger-up packets */ - if (!p->pt_down && !p->gs_down) - goto done; - - /* ensure that we're not a finger-down packet */ - if ((p->pt_down && !test_bit(BTN_TOUCH, dev->key)) || - (p->gs_down && !test_bit(BTN_TOUCH, dev->key))) - goto done; - - if (abs(dev->abs[ABS_X] - p->x) > ignore_delta || - abs(dev->abs[ABS_Y] - p->y) > ignore_delta) { - hgpk_dbg(psmouse, "axis change (%d,%d) => (%d,%d) is over " - "delta threshold\n", dev->abs[ABS_X], - dev->abs[ABS_Y], p->x, p->y); - input_report_key(dev, BTN_TOUCH, 0); - input_sync(dev); - - - /* two in a row is a pretty good indicator of miscalibration */ -// if (priv->axis_errors++) { - /* wait 2s for finger removal, and then recalibrate */ - queue_delayed_work(kpsmoused_wq, &priv->recalib_wq, - msecs_to_jiffies(2000)); -// priv->axis_errors = 0; -// } - return; + if (abs(x) > recalib_delta || abs(y) > recalib_delta) { + hgpk_err(psmouse, ">%dpx jump detected (%d,%d)\n", + recalib_delta, x, y); + /* My car gets fourty rods to the hogshead and that's the + * way I likes it! + */ + queue_delayed_work(kpsmoused_wq, &priv->recalib_wq, + msecs_to_jiffies(1000)); } -done: -// priv->axis_errors = 0; - return; } /* - * This is my favorite touchpad hardware bug. I'm entirely not sure what - * triggers it (I've seen it triggered while the laptop was left on overnight, - * but my cat could have very well been using it/sleeping on it). However, - * the touchpad will randomly get stuck in a state where it constantly spews - * packets without a finger being on it. A recalibration will fix it, but - * without that it will go on for days (auto-recalibration doesn't catch it, - * either). The packets tend to either have the same coordinates, or be - * 1px away from each other; ie, (283,139,6) -> (284,139,5) -> (285,139,5) -> - * (286,139,6) -> (286,139,6) -> etc. We have a number of workarounds here.. + * We have no idea why this particular hardware bug occurs. The touchpad + * will randomly start spewing packets without anything touching the + * pad. This wouldn't necessarily be bad, but it's indicative of a + * severely miscalibrated pad; attempting to use the touchpad while it's + * spewing means the cursor will jump all over the place, and act "drunk". + * + * The packets that are spewed tend to all have deltas between -2 and 2, and + * the cursor will move around without really going very far. It will + * tend to end up in the same location; if we tally up the changes over + * 100 packets, we end up w/ a final delta of close to 0. This happens + * pretty regularly when the touchpad is spewing, and is pretty hard to + * manually trigger (at least for *my* fingers). So, it makes a perfect + * scheme for detecting spews. */ -static void hgpk_spewing_hack(struct psmouse *psmouse, struct hgpk_packet *p) +static void hgpk_spewing_hack(struct psmouse *psmouse, int l, int r, int x, + int y) { struct hgpk_data *priv = psmouse->private; - struct input_dev *dev = p->dev; - int repeat_axes; - - if (psmouse->model < HGPK_MODEL_C) - return; - - /* ignore 0, 0 packets */ - if (p->x == 0 && p->y == 0) - return; + static int count = 0; + static int x_tally = 0; + static int y_tally = 0; - /* PT packets don't count */ - if (p->pt_down) { - priv->repeat_pkts = 0; + /* ignore button press packets; many in a row could trigger + * a false-positive! */ + if (l || r) return; - } - /* - * If we see 2s+ worth of packets that have at least 2 axis deltas of - * only 1px, that's a good indication that we're spewing packets. - * We're going to ignore z=15, though; that's pretty indicative of - * an actual finger on the touchpad just staying still. - */ - if (p->z == 0 || p->z == 15) - goto next_hack; - repeat_axes = abs(p->x - dev->abs[ABS_X]) < 2 ? 1 : 0; - repeat_axes += abs(p->y - dev->abs[ABS_Y]) < 2 ? 1 : 0; - repeat_axes += abs(p->z - dev->abs[ABS_PRESSURE]) < 2 ? 1 : 0; - if (repeat_axes > 1) { - priv->repeat_pkts++; - /* we get 1 packet about every 24mS */ - if (priv->repeat_pkts > 83) { - queue_delayed_work(kpsmoused_wq, &priv->recalib_wq, 0); - priv->repeat_pkts = 0; - } - } - else - priv->repeat_pkts = 0; - return; - -next_hack: - /* - * 10s of y and z not changing is another kind of miscalibration. - */ - repeat_axes = (p->y == dev->abs[ABS_Y]) ? 1 : 0; - repeat_axes += (p->z == dev->abs[ABS_PRESSURE]) ? 1 : 0; - if (repeat_axes > 1) { - priv->repeat_pkts++; - if (priv->repeat_pkts > 416) { - queue_delayed_work(kpsmoused_wq, &priv->recalib_wq, 0); - priv->repeat_pkts = 0; + count++; + x_tally += x; + y_tally += y; + if (count > 100) { + if (abs(x_tally) < 3 && abs(y_tally) < 3) { + hgpk_dbg(psmouse, "packet spew detected (%d,%d). :(\n", + x_tally, y_tally); + /* We had to say dickety 'cause that Kaiser had + * stolen our word twenty. I chased that rascal to + * get it back, but gave up after dickety six miles + */ + queue_delayed_work(kpsmoused_wq, &priv->recalib_wq, + msecs_to_jiffies(1000)); } + /* reset every 100 packets */ + count = 0; + x_tally = 0; + y_tally = 0; } - else - priv->repeat_pkts = 0; } /* - * HGPK Advanced Mode - single-mode format - * - * byte 0(PT): 1 1 0 0 1 1 1 1 - * byte 0(GS): 1 1 1 1 1 1 1 1 - * byte 1: 0 x6 x5 x4 x3 x2 x1 x0 - * byte 2(PT): 0 0 x9 x8 x7 ? pt-dsw 0 - * byte 2(GS): 0 x10 x9 x8 x7 ? gs-dsw pt-dsw - * byte 3: 0 y9 y8 y7 1 0 swr swl - * byte 4: 0 y6 y5 y4 y3 y2 y1 y0 - * byte 5: 0 z6 z5 z4 z3 z2 z1 z0 + * HGPK Mouse Mode format * - * ?'s are not defined in the protocol spec, may vary between models. + * byte 0: y-over x-over y-neg x-neg 1 0 swr swl + * byte 1: x7 x6 x5 x4 x3 x2 x1 x0 + * byte 2: y7 y6 y5 y4 y3 y2 y1 y0 * * swr/swl are the left/right buttons. - * - * pt-dsw/gs-dsw indicate that the pt/gs sensor is detecting a - * pen/finger + * x-neg/y-neg are the x and y delta negative bits + * x-over/y-over are the x and y overflow bits */ -static int hgpk_validate_byte(unsigned char *packet, int pktcnt) +static int hgpk_validate_byte(unsigned char *packet) { - BUG_ON(pktcnt < 1); - - if (packet[0] != HGPK_PT && packet[0] != HGPK_GS) - return -1; - - /* bytes 2 - 6 should have 0 in the highest bit */ - if (pktcnt >= 2 && pktcnt <= 6 && (packet[pktcnt - 1] & 0x80)) + if (!(packet[0] & (1<<3)) || (packet[0] & (1<<2))) return -1; return 0; } -static void hgpk_decode_packet(struct psmouse *psmouse, struct hgpk_packet *p) +static void hgpk_process_packet(struct psmouse *psmouse) { + struct input_dev *dev = psmouse->dev; unsigned char *packet = psmouse->packet; + int x, y, left, right; - BUG_ON(psmouse->pktcnt < 6); - - p->left = packet[3] & 1; - p->right = !!(packet[3] & 2); - p->x = packet[1] | ((packet[2] & 0x78) << 4); - p->y = packet[4] | ((packet[3] & 0x70) << 3); - p->z = packet[5]; - - if (packet[0] == HGPK_GS) { - p->pt_down = !!(packet[2] & 1); - p->gs_down = !!(packet[2] & 2); - p->dev = ((struct hgpk_data *) psmouse->private)->gs; - if (p->pt_down) { - /* we miss spurious PT finger-downs if pt_down is set */ - p->mode_switch = HGPK_PT; - p->pt_down = 0; - } else { - p->mode_switch = 0; - } - } else if (packet[0] == HGPK_PT) { - p->pt_down = !!(packet[2] & 2); - p->gs_down = 0; - p->dev = psmouse->dev; - p->mode_switch = !p->pt_down ? HGPK_GS : 0; - } + left = packet[0] & 1; + right = (packet[0] >> 1) & 1; - if (tpdebug) { - hgpk_dbg(psmouse, "l=%d r=%d p=%d g=%d x=%d y=%d z=%d m=%x\n", - p->left, p->right, p->pt_down, p->gs_down, - p->x, p->y, p->z, p->mode_switch); - } -} + x = packet[1] - ((packet[0] << 4) & 0x100); + y = ((packet[0] << 3) & 0x100) - packet[2]; -static void hgpk_process_packet_gspt(struct psmouse *psmouse) -{ - struct hgpk_data *priv = psmouse->private; - struct input_dev *pt = psmouse->dev; - struct input_dev *gs = priv->gs; - struct hgpk_packet pkt; + hgpk_jumpy_hack(psmouse, x, y); + hgpk_spewing_hack(psmouse, left, right, x, y); - hgpk_decode_packet(psmouse, &pkt); + if (tpdebug) + hgpk_dbg(psmouse, "l=%d r=%d x=%d y=%d\n", left, right, x, y); - hgpk_fingerup_hack(psmouse, &pkt); - hgpk_fingerdown_hack(psmouse, &pkt); - hgpk_big_delta_hack(psmouse, &pkt); - hgpk_spewing_hack(psmouse, &pkt); + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); - input_report_key(pkt.dev, BTN_LEFT, pkt.left); - input_report_key(pkt.dev, BTN_RIGHT, pkt.right); + input_report_rel(dev, REL_X, x); + input_report_rel(dev, REL_Y, y); - input_report_key(pt, BTN_TOUCH, pkt.pt_down); - input_report_key(gs, BTN_TOUCH, pkt.gs_down); + input_sync(dev); +} + +static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) +{ + struct hgpk_data *priv = psmouse->private; - input_report_abs(pkt.dev, ABS_X, pkt.x); - input_report_abs(pkt.dev, ABS_Y, pkt.y); - input_report_abs(pkt.dev, ABS_PRESSURE, pkt.z); + if (hgpk_validate_byte(psmouse->packet)) { + hgpk_dbg(psmouse, "%s: (%d) %02x %02x %02x\n", + __func__, psmouse->pktcnt, psmouse->packet[0], + psmouse->packet[1], psmouse->packet[2]); + return PSMOUSE_BAD_DATA; + } - input_sync(pt); - input_sync(gs); + if (psmouse->pktcnt >= psmouse->pktsize) { + hgpk_process_packet(psmouse); + return PSMOUSE_FULL_PACKET; + } if (priv->recalib_window) { if (time_before(jiffies, priv->recalib_window)) { @@ -335,47 +189,13 @@ static void hgpk_process_packet_gspt(struct psmouse *psmouse) * ugh, got a packet inside our recalibration * window, schedule another recalibration. */ - hgpk_dbg(psmouse, "packet inside calibration window, " - "queueing another recalibration\n"); + hgpk_dbg(psmouse, "packet inside calibration window, queueing another recalibration\n"); queue_delayed_work(kpsmoused_wq, &priv->recalib_wq, msecs_to_jiffies(1000)); } priv->recalib_window = 0; } - if (psmouse->model != HGPK_MODEL_A) { - if (priv->pending_mode && (!pkt.mode_switch || - priv->current_mode == pkt.mode_switch)) { - priv->pending_mode = 0; - cancel_delayed_work(&priv->switch_wq); - } - else if (priv->pending_mode != pkt.mode_switch) { - priv->pending_mode = pkt.mode_switch; - - /* allow for spurious mode_switch packets by delaying */ - queue_delayed_work(kpsmoused_wq, &priv->switch_wq, - msecs_to_jiffies(50)); - } - } -} - -static psmouse_ret_t hgpk_process_byte(struct psmouse *psmouse) -{ - if (hgpk_validate_byte(psmouse->packet, psmouse->pktcnt)) { - hgpk_dbg(psmouse, - "invalid packet (%d bytes): %02x %02x %02x %02x %02x %02x\n", - psmouse->pktcnt, psmouse->packet[0], - psmouse->packet[1], psmouse->packet[2], - psmouse->packet[3], psmouse->packet[4], - psmouse->packet[5]); - return PSMOUSE_BAD_DATA; - } - - if (psmouse->pktcnt == 6) { - hgpk_process_packet_gspt(psmouse); - return PSMOUSE_FULL_PACKET; - } - return PSMOUSE_GOOD_DATA; } @@ -383,25 +203,22 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; struct hgpk_data *priv = psmouse->private; - struct input_dev *pt = psmouse->dev; - struct input_dev *gs = priv->gs; /* C-series touchpads added the recalibrate command */ if (psmouse->model < HGPK_MODEL_C) return 0; + /* start by resetting the device */ + psmouse_reset(psmouse); + + /* send the recalibrate request */ if (ps2_command(ps2dev, NULL, 0xf5) || ps2_command(ps2dev, NULL, 0xf5) || ps2_command(ps2dev, NULL, 0xe6) || ps2_command(ps2dev, NULL, 0xf5)) return -1; - /* send a finger-up event so the cursor doesn't jump around */ - input_report_key(pt, BTN_TOUCH, 0); - input_report_key(gs, BTN_TOUCH, 0); - input_sync(pt); - input_sync(gs); - + /* according to ALPS, 150mS is required for recalibration */ msleep(150); @@ -425,81 +242,6 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse) return 0; } -static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - unsigned char param[3]; - - /* E7, E7, E7, E9 gets us a 3 byte identifier */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return -EIO; - - hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]); - - /* HGPK signature: 0x67, 0x00, 0x<model> */ - if (param[0] != 0x67 || param[1] != 0x00) - return -ENODEV; - - hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); - return param[2]; -} - -/* - * Touchpad should be disabled before calling this! - */ -static int hgpk_new_mode(struct psmouse *psmouse, int mode) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - struct hgpk_data *priv = psmouse->private; - - /* - * PT mode: F2, F2, F2, E7 - * GS mode: F2, F2, F2, E6 - */ - if (ps2_command(ps2dev, NULL, 0xF2) || - ps2_command(ps2dev, NULL, 0xF2) || - ps2_command(ps2dev, NULL, 0xF2)) - return -EIO; - - if (mode == HGPK_GS) { - if (ps2_command(ps2dev, NULL, 0xE6)) - return -EIO; - } else { - if (ps2_command(ps2dev, NULL, 0xE7)) - return -EIO; - } - - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) - return -EIO; - - /* tell the irq handler to stop ignoring packets */ - psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); - - priv->current_mode = mode; - priv->pending_mode = 0; - if (tpdebug) - hgpk_warn(psmouse, "Switched to mode 0x%x successful.\n", mode); - - return 0; -} - -static int hgpk_advanced_mode(struct psmouse *psmouse) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - - /* Switch to 'Advanced mode.', four disables in a row. */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) - return -1; - - return hgpk_new_mode(psmouse, HGPK_GS); -} - /* * This kills power to the touchpad; according to ALPS, current consumption * goes down to 50uA after running this. To turn power back on, we drive @@ -533,10 +275,6 @@ static int hgpk_toggle_power(struct psmouse *psmouse, int enable) psmouse_reset(psmouse); - if (hgpk_advanced_mode(psmouse)) { - hgpk_err(psmouse, "Failed to reinit touchpad!\n"); - return -1; - } } else { hgpk_dbg(psmouse, "Powering off touchpad.\n"); psmouse_set_state(psmouse, PSMOUSE_IGNORE); @@ -569,16 +307,10 @@ static int hgpk_reconnect(struct psmouse *psmouse) return 0; psmouse_reset(psmouse); - - if (hgpk_advanced_mode(psmouse)) { - hgpk_err(psmouse, "failed to reenable advanced mode.\n"); - return -1; - } - return 0; } -static ssize_t hgpk_show_powered(struct device *dev, +static ssize_t hgpk_show_pwred(struct device *dev, struct device_attribute *attr, char *buf) { struct serio *serio = to_serio_port(dev); @@ -598,7 +330,7 @@ static ssize_t hgpk_show_powered(struct device *dev, return retval; } -static ssize_t hgpk_set_powered(struct device *dev, +static ssize_t hgpk_set_pwred(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct serio *serio = to_serio_port(dev); @@ -640,184 +372,116 @@ done: return retval ? retval : count; } -static DEVICE_ATTR(powered, S_IWUSR | S_IRUGO, hgpk_show_powered, - hgpk_set_powered); +static DEVICE_ATTR(pwred, S_IWUSR | S_IRUGO, hgpk_show_pwred, hgpk_set_pwred); static void hgpk_disconnect(struct psmouse *psmouse) { struct hgpk_data *priv = psmouse->private; - device_remove_file(&psmouse->ps2dev.serio->dev, &dev_attr_powered); + device_remove_file(&psmouse->ps2dev.serio->dev, &dev_attr_pwred); psmouse_reset(psmouse); flush_scheduled_work(); - input_unregister_device(priv->gs); kfree(priv); } -static void hgpk_mode_switch(struct work_struct *work) +static void hgpk_recalib_work(struct work_struct *work) { struct delayed_work *w = container_of(work, struct delayed_work, work); - struct hgpk_data *priv = container_of(w, struct hgpk_data, switch_wq); + struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); struct psmouse *psmouse = priv->psmouse; - struct ps2dev *ps2dev = &psmouse->ps2dev; - int pending_mode; - if (tpdebug) - hgpk_dbg(psmouse, "Starting mode switch to 0x%x. [%lu]\n", - priv->pending_mode, jiffies); + hgpk_dbg(psmouse, "recalibrating touchpad..\n"); - if (priv->pending_mode == priv->current_mode) { - priv->pending_mode = 0; - hgpk_dbg(psmouse, "Already in target mode, no-op.\n"); - return; - } + if (hgpk_force_recalibrate(psmouse)) + hgpk_err(psmouse, "recalibration failed!\n"); +} - /* tell the irq handler to ignore any further packets */ - psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - priv->late = 0; +static int hgpk_init(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + int err; - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) - goto bad; + /* unset the things that psmouse-base sets which we don't have */ + clear_bit(BTN_MIDDLE, dev->keybit); - /* - * ALPS tells us that it may take up to 20msec for the disable to - * take effect; however, ps2_command() will wait up to 200msec for - * the ACK to come back (and I'm assuming that by the time the - * hardware sends back its ACK, it has stopped sending bytes). - */ - pending_mode = priv->pending_mode; + /* set the things we do have */ + set_bit(EV_KEY, dev->evbit); + set_bit(EV_REL, dev->evbit); - if (hgpk_new_mode(psmouse, priv->pending_mode)) - goto bad; + set_bit(REL_X, dev->relbit); + set_bit(REL_Y, dev->relbit); - /* - * Deal with a potential race condition. - * - * If there is a brief tap of a stylus or a fingernail that - * triggers a mode switch to PT mode, and the stylus/fingernail is - * lifted after the DISABLE above, but before we reenable in the - * new mode then we can get stuck in PT mode. - */ - if (pending_mode == HGPK_PT) { - priv->pending_mode = HGPK_GS; - queue_delayed_work(kpsmoused_wq, &priv->switch_wq, - msecs_to_jiffies(50)); - } + set_bit(BTN_LEFT, dev->keybit); + set_bit(BTN_RIGHT, dev->keybit); - return; -bad: - hgpk_warn(psmouse, "Failure to switch modes, resetting device...\n"); - hgpk_reconnect(psmouse); -} + /* register handlers */ + psmouse->protocol_handler = hgpk_process_byte; + psmouse->poll = hgpk_poll; + psmouse->disconnect = hgpk_disconnect; + psmouse->reconnect = hgpk_reconnect; + psmouse->pktsize = 3; -static void hgpk_recalib_work(struct work_struct *work) -{ - struct delayed_work *w = container_of(work, struct delayed_work, work); - struct hgpk_data *priv = container_of(w, struct hgpk_data, recalib_wq); - struct psmouse *psmouse = priv->psmouse; + /* Disable the idle resync. */ + psmouse->resync_time = 0; + /* Reset after a lot of bad bytes. */ + psmouse->resetafter = 1024; - hgpk_dbg(psmouse, "Recalibrating touchpad..\n"); + err = device_create_file(&psmouse->ps2dev.serio->dev, &dev_attr_pwred); + if (err) + hgpk_err(psmouse, "Failed to create sysfs attribute\n"); - if (hgpk_force_recalibrate(psmouse)) - hgpk_err(psmouse, "Recalibration failed!\n"); + return err; } int olpc_init(struct psmouse *psmouse) { struct hgpk_data *priv; - struct input_dev *pt = psmouse->dev; - struct input_dev *gs; + int err = -ENOMEM; priv = kzalloc(sizeof(struct hgpk_data), GFP_KERNEL); - gs = input_allocate_device(); - if (!priv || !gs) - goto init_fail; + if (!priv) + goto alloc_fail; psmouse->private = priv; - priv->gs = gs; priv->psmouse = psmouse; priv->powered = 1; + INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); - psmouse_reset(psmouse); - - if (hgpk_advanced_mode(psmouse)) { - hgpk_err(psmouse, "failed to enable advanced mode\n"); + err = psmouse_reset(psmouse); + if (err) goto init_fail; - } - - /* Unset all the things that psmouse-base sets */ - pt->evbit[0] &= ~(BIT_MASK(EV_KEY) | BIT_MASK(EV_REL)); - pt->keybit[BIT_WORD(BTN_MOUSE)] &= ~(BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT)); - pt->relbit[0] &= ~(BIT_MASK(REL_X) | BIT_MASK(REL_Y)); - - /* Set all the things we *do* have */ - set_bit(EV_KEY, pt->evbit); - set_bit(EV_ABS, pt->evbit); - - set_bit(BTN_LEFT, pt->keybit); - set_bit(BTN_RIGHT, pt->keybit); - set_bit(BTN_TOUCH, pt->keybit); - - input_set_abs_params(pt, ABS_X, 2, 1000, 0, 0); - input_set_abs_params(pt, ABS_Y, 0, 717, 0, 0); - input_set_abs_params(pt, ABS_PRESSURE, 0, 127, 0, 0); - - snprintf(priv->phys, sizeof(priv->phys), - "%s/input1", psmouse->ps2dev.serio->phys); - gs->phys = priv->phys; - gs->name = "OLPC ALPS GlideSensor"; - gs->id.bustype = BUS_I8042; - gs->id.vendor = 0x0002; - gs->id.product = PSMOUSE_OLPC; - gs->id.version = psmouse->model; - - set_bit(EV_KEY, gs->evbit); - set_bit(EV_ABS, gs->evbit); - - set_bit(BTN_LEFT, gs->keybit); - set_bit(BTN_RIGHT, gs->keybit); - set_bit(BTN_TOUCH, gs->keybit); - - input_set_abs_params(gs, ABS_X, 350, 512, 0, 0); - input_set_abs_params(gs, ABS_Y, 70, 325, 0, 0); - input_set_abs_params(gs, ABS_PRESSURE, 0, 15, 0, 0); - - if (input_register_device(gs)) { - hgpk_err(psmouse, "Failed to register GlideSensor\n"); + err = hgpk_init(psmouse); + if (err) goto init_fail; - } - - psmouse->protocol_handler = hgpk_process_byte; - psmouse->poll = hgpk_poll; - psmouse->disconnect = hgpk_disconnect; - psmouse->reconnect = hgpk_reconnect; - psmouse->pktsize = 6; - /* Disable the idle resync. */ - psmouse->resync_time = 0; - /* Reset after a lot of bad bytes. */ - psmouse->resetafter = 1024; + return 0; - INIT_DELAYED_WORK(&priv->switch_wq, hgpk_mode_switch); - INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work); +init_fail: + kfree(priv); +alloc_fail: + return err; +} - if (device_create_file(&psmouse->ps2dev.serio->dev, - &dev_attr_powered)) { - hgpk_err(psmouse, "Failed to create sysfs attribute\n"); - goto attr_fail; - } +static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + /* E7, E7, E7, E9 gets us a 3 byte identifier */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -EIO; - return 0; + hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]); -attr_fail: - input_unregister_device(gs); - gs = NULL; -init_fail: - input_free_device(gs); - kfree(priv); - return -1; + /* HGPK signature: 0x67, 0x00, 0x<model> */ + if (param[0] != 0x67 || param[1] != 0x00) + return -ENODEV; + + hgpk_info(psmouse, "OLPC touchpad revision 0x%x\n", param[2]); + return param[2]; } int olpc_detect(struct psmouse *psmouse, int set_properties) @@ -830,7 +494,7 @@ int olpc_detect(struct psmouse *psmouse, int set_properties) if (set_properties) { psmouse->vendor = "ALPS"; - psmouse->name = "PenTablet"; + psmouse->name = "HGPK"; psmouse->model = version; } return 0; diff --git a/drivers/input/mouse/olpc.h b/drivers/input/mouse/olpc.h index 777616d61f57..c2d161b6d03d 100644 --- a/drivers/input/mouse/olpc.h +++ b/drivers/input/mouse/olpc.h @@ -16,37 +16,17 @@ #define _OLPC_H enum hgpk_model_t { - HGPK_MODEL_PreA = 0x0a, /* pre-B1s */ - HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ + HGPK_MODEL_PREA = 0x0a, /* pre-B1s */ + HGPK_MODEL_A = 0x14, /* found on B1s, PT disabled in hardware */ HGPK_MODEL_B = 0x28, /* B2s, has capacitance issues */ HGPK_MODEL_C = 0x3c, - HGPK_MODEL_D = 0x50, /* C1, mass production */ -}; - -#define HGPK_GS 0xff /* The GlideSensor */ -#define HGPK_PT 0xcf /* The PenTablet */ - -struct hgpk_packet { - struct input_dev *dev; - int x, y, z; - unsigned char mode_switch; - unsigned int pt_down:1, gs_down:1; - unsigned int left:1, right:1; + HGPK_MODEL_D = 0x50, /* C1, mass production */ }; struct hgpk_data { - struct input_dev *gs; /* GlideSensor */ struct psmouse *psmouse; - char name[32]; /* Name */ - char phys[32]; /* Phys */ - int pending_mode; - int current_mode; - s64 late; - int axis_errors; - int repeat_pkts; int powered; unsigned long recalib_window; - struct delayed_work switch_wq; struct delayed_work recalib_wq; }; @@ -65,13 +45,13 @@ struct hgpk_data { int olpc_detect(struct psmouse *psmouse, int set_properties); int olpc_init(struct psmouse *psmouse); #else -inline int olpc_detect(struct psmouse *psmouse, int set_properties) +static inline int olpc_detect(struct psmouse *psmouse, int set_properties) { - return -ENOSYS; + return -ENODEV; } -inline int olpc_init(struct psmouse *psmouse) +static inline int olpc_init(struct psmouse *psmouse) { - return -ENOSYS; + return -ENODEV; } #endif |