diff options
Diffstat (limited to 'drivers/input/mouse/alps.c')
-rw-r--r-- | drivers/input/mouse/alps.c | 79 |
1 files changed, 73 insertions, 6 deletions
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 4c6a72d3d48..ecd93894c80 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -604,10 +604,54 @@ static void alps_process_packet_v3(struct psmouse *psmouse) static void alps_process_packet_v4(struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; + int offset; int x, y, z; int left, right; + int x1, y1, x2, y2; + int fingers = 0; + unsigned int x_bitmap, y_bitmap; + + /* + * v4 has a 6-byte encoding for bitmap data, but this data is + * broken up between 3 normal packets. Use priv->multi_packet to + * track our position in the bitmap packet. + */ + if (packet[6] & 0x40) { + /* sync, reset position */ + priv->multi_packet = 0; + } + + if (WARN_ON_ONCE(priv->multi_packet > 2)) + return; + + offset = 2 * priv->multi_packet; + priv->multi_data[offset] = packet[6]; + priv->multi_data[offset + 1] = packet[7]; + + if (++priv->multi_packet > 2) { + priv->multi_packet = 0; + + x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) | + ((priv->multi_data[3] & 0x60) << 3) | + ((priv->multi_data[0] & 0x3f) << 2) | + ((priv->multi_data[1] & 0x60) >> 5); + y_bitmap = ((priv->multi_data[5] & 0x01) << 10) | + ((priv->multi_data[3] & 0x1f) << 5) | + (priv->multi_data[1] & 0x1f); + + fingers = alps_process_bitmap(x_bitmap, y_bitmap, + &x1, &y1, &x2, &y2); + + /* Store MT data.*/ + priv->fingers = fingers; + priv->x1 = x1; + priv->x2 = x2; + priv->y1 = y1; + priv->y2 = y2; + } left = packet[4] & 0x01; right = packet[4] & 0x02; @@ -617,21 +661,44 @@ static void alps_process_packet_v4(struct psmouse *psmouse) y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); z = packet[5] & 0x7f; + /* + * If there were no contacts in the bitmap, use ST + * points in MT reports. + * If there were two contacts or more, report MT data. + */ + if (priv->fingers < 2) { + x1 = x; + y1 = y; + fingers = z > 0 ? 1 : 0; + } else { + fingers = priv->fingers; + x1 = priv->x1; + x2 = priv->x2; + y1 = priv->y1; + y2 = priv->y2; + } + if (z >= 64) input_report_key(dev, BTN_TOUCH, 1); else input_report_key(dev, BTN_TOUCH, 0); + alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); + + input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); + input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); + input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); + input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); + + input_report_key(dev, BTN_LEFT, left); + input_report_key(dev, BTN_RIGHT, right); + if (z > 0) { input_report_abs(dev, ABS_X, x); input_report_abs(dev, ABS_Y, y); } input_report_abs(dev, ABS_PRESSURE, z); - input_report_key(dev, BTN_TOOL_FINGER, z > 0); - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - input_sync(dev); } @@ -1557,6 +1624,7 @@ int alps_init(struct psmouse *psmouse) input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); break; case ALPS_PROTO_V3: + case ALPS_PROTO_V4: set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); input_mt_init_slots(dev1, 2); input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); @@ -1565,8 +1633,7 @@ int alps_init(struct psmouse *psmouse) set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); set_bit(BTN_TOOL_QUADTAP, dev1->keybit); - /* fall through */ - case ALPS_PROTO_V4: + input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); break; |