summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@debian.org>2008-07-06 03:26:01 -0400
committerAndres Salomon <dilinger@debian.org>2008-07-06 20:46:05 -0400
commit4def8439648ce8090b526c29aa2ab3a37b221b13 (patch)
tree5cce4beae132939349685582d4201efe78519620 /drivers/input
parenta4933862c5862493ef4e9648c124655cc6e98b77 (diff)
downloadlwn-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.c678
-rw-r--r--drivers/input/mouse/olpc.h34
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