11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * ALPS touchpad PS/2 mouse driver 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2003 Neil Brown <neilb@cse.unsw.edu.au> 5963f626dSPeter Osterlund * Copyright (c) 2003-2005 Peter Osterlund <petero2@telia.com> 61da177e4SLinus Torvalds * Copyright (c) 2004 Dmitry Torokhov <dtor@mail.ru> 71da177e4SLinus Torvalds * Copyright (c) 2005 Vojtech Pavlik <vojtech@suse.cz> 81d9f2626SSebastian Kapfer * Copyright (c) 2009 Sebastian Kapfer <sebastian_kapfer@gmx.net> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * ALPS detection, tap switching and status querying info is taken from 111da177e4SLinus Torvalds * tpconfig utility (by C. Scott Ananian and Bruce Kall). 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify it 141da177e4SLinus Torvalds * under the terms of the GNU General Public License version 2 as published by 151da177e4SLinus Torvalds * the Free Software Foundation. 161da177e4SLinus Torvalds */ 171da177e4SLinus Torvalds 185a0e3ad6STejun Heo #include <linux/slab.h> 191da177e4SLinus Torvalds #include <linux/input.h> 2001ce661fSSeth Forshee #include <linux/input/mt.h> 211da177e4SLinus Torvalds #include <linux/serio.h> 221da177e4SLinus Torvalds #include <linux/libps2.h> 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds #include "psmouse.h" 251da177e4SLinus Torvalds #include "alps.h" 261da177e4SLinus Torvalds 2725bded7cSSeth Forshee /* 2825bded7cSSeth Forshee * Definitions for ALPS version 3 and 4 command mode protocol 2925bded7cSSeth Forshee */ 3001ce661fSSeth Forshee #define ALPS_V3_X_MAX 2000 3101ce661fSSeth Forshee #define ALPS_V3_Y_MAX 1400 3201ce661fSSeth Forshee 3301ce661fSSeth Forshee #define ALPS_BITMAP_X_BITS 15 3401ce661fSSeth Forshee #define ALPS_BITMAP_Y_BITS 11 3501ce661fSSeth Forshee 3625bded7cSSeth Forshee #define ALPS_CMD_NIBBLE_10 0x01f2 3725bded7cSSeth Forshee 3825bded7cSSeth Forshee static const struct alps_nibble_commands alps_v3_nibble_commands[] = { 3925bded7cSSeth Forshee { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ 4025bded7cSSeth Forshee { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ 4125bded7cSSeth Forshee { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ 4225bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ 4325bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ 4425bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ 4525bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ 4625bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ 4725bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ 4825bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ 4925bded7cSSeth Forshee { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ 5025bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ 5125bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ 5225bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ 5325bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ 5425bded7cSSeth Forshee { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ 5525bded7cSSeth Forshee }; 5625bded7cSSeth Forshee 5725bded7cSSeth Forshee static const struct alps_nibble_commands alps_v4_nibble_commands[] = { 5825bded7cSSeth Forshee { PSMOUSE_CMD_ENABLE, 0x00 }, /* 0 */ 5925bded7cSSeth Forshee { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ 6025bded7cSSeth Forshee { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* 2 */ 6125bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x0a }, /* 3 */ 6225bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x14 }, /* 4 */ 6325bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x28 }, /* 5 */ 6425bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x3c }, /* 6 */ 6525bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x50 }, /* 7 */ 6625bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0x64 }, /* 8 */ 6725bded7cSSeth Forshee { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 9 */ 6825bded7cSSeth Forshee { ALPS_CMD_NIBBLE_10, 0x00 }, /* a */ 6925bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x00 }, /* b */ 7025bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x01 }, /* c */ 7125bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x02 }, /* d */ 7225bded7cSSeth Forshee { PSMOUSE_CMD_SETRES, 0x03 }, /* e */ 7325bded7cSSeth Forshee { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ 7425bded7cSSeth Forshee }; 7525bded7cSSeth Forshee 7625bded7cSSeth Forshee 7771bb21b6SMaxim Levitsky #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ 7871bb21b6SMaxim Levitsky #define ALPS_PASS 0x04 /* device has a pass-through port */ 7971bb21b6SMaxim Levitsky 8071bb21b6SMaxim Levitsky #define ALPS_WHEEL 0x08 /* hardware wheel present */ 8171bb21b6SMaxim Levitsky #define ALPS_FW_BK_1 0x10 /* front & back buttons present */ 8271bb21b6SMaxim Levitsky #define ALPS_FW_BK_2 0x20 /* front & back buttons present */ 8371bb21b6SMaxim Levitsky #define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ 841d9f2626SSebastian Kapfer #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with 851d9f2626SSebastian Kapfer 6-byte ALPS packet */ 861da177e4SLinus Torvalds 87e38de678SHelge Deller static const struct alps_model_info alps_model_data[] = { 8825bded7cSSeth Forshee { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ 8925bded7cSSeth Forshee { { 0x33, 0x02, 0x0a }, 0x00, ALPS_PROTO_V1, 0x88, 0xf8, 0 }, /* UMAX-530T */ 9025bded7cSSeth Forshee { { 0x53, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, 9125bded7cSSeth Forshee { { 0x53, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, 9225bded7cSSeth Forshee { { 0x60, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, /* HP ze1115 */ 9325bded7cSSeth Forshee { { 0x63, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, 9425bded7cSSeth Forshee { { 0x63, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, 9525bded7cSSeth Forshee { { 0x63, 0x02, 0x28 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Fujitsu Siemens S6010 */ 9625bded7cSSeth Forshee { { 0x63, 0x02, 0x3c }, 0x00, ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */ 9725bded7cSSeth Forshee { { 0x63, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ 9825bded7cSSeth Forshee { { 0x63, 0x02, 0x64 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, 9925bded7cSSeth Forshee { { 0x63, 0x03, 0xc8 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */ 10025bded7cSSeth Forshee { { 0x73, 0x00, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ 10125bded7cSSeth Forshee { { 0x73, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, 0 }, 10225bded7cSSeth Forshee { { 0x73, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ 10325bded7cSSeth Forshee { { 0x20, 0x02, 0x0e }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ 10425bded7cSSeth Forshee { { 0x22, 0x02, 0x0a }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, 10525bded7cSSeth Forshee { { 0x22, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ 1061d9f2626SSebastian Kapfer /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ 10725bded7cSSeth Forshee { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, 1081d9f2626SSebastian Kapfer ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, 10925bded7cSSeth Forshee { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ 11025bded7cSSeth Forshee { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, 111eb8bff85SThomas Bächler ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ 11225bded7cSSeth Forshee { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, 11325bded7cSSeth Forshee { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, 11425bded7cSSeth Forshee { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, 1151da177e4SLinus Torvalds }; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds /* 1181da177e4SLinus Torvalds * XXX - this entry is suspicious. First byte has zero lower nibble, 1191da177e4SLinus Torvalds * which is what a normal mouse would report. Also, the value 0x0e 1201da177e4SLinus Torvalds * isn't valid per PS/2 spec. 1211da177e4SLinus Torvalds */ 1221da177e4SLinus Torvalds 123d4b347b2SSeth Forshee /* Packet formats are described in Documentation/input/alps.txt */ 1241da177e4SLinus Torvalds 1251d9f2626SSebastian Kapfer static bool alps_is_valid_first_byte(const struct alps_model_info *model, 1261d9f2626SSebastian Kapfer unsigned char data) 1271d9f2626SSebastian Kapfer { 1281d9f2626SSebastian Kapfer return (data & model->mask0) == model->byte0; 1291d9f2626SSebastian Kapfer } 1301d9f2626SSebastian Kapfer 1311d9f2626SSebastian Kapfer static void alps_report_buttons(struct psmouse *psmouse, 1321d9f2626SSebastian Kapfer struct input_dev *dev1, struct input_dev *dev2, 1331d9f2626SSebastian Kapfer int left, int right, int middle) 1341d9f2626SSebastian Kapfer { 1351d9f2626SSebastian Kapfer struct input_dev *dev; 1361d9f2626SSebastian Kapfer 1371d9f2626SSebastian Kapfer /* 1381d9f2626SSebastian Kapfer * If shared button has already been reported on the 1391d9f2626SSebastian Kapfer * other device (dev2) then this event should be also 1401d9f2626SSebastian Kapfer * sent through that device. 1411d9f2626SSebastian Kapfer */ 1421d9f2626SSebastian Kapfer dev = test_bit(BTN_LEFT, dev2->key) ? dev2 : dev1; 1431d9f2626SSebastian Kapfer input_report_key(dev, BTN_LEFT, left); 1441d9f2626SSebastian Kapfer 1451d9f2626SSebastian Kapfer dev = test_bit(BTN_RIGHT, dev2->key) ? dev2 : dev1; 1461d9f2626SSebastian Kapfer input_report_key(dev, BTN_RIGHT, right); 1471d9f2626SSebastian Kapfer 1481d9f2626SSebastian Kapfer dev = test_bit(BTN_MIDDLE, dev2->key) ? dev2 : dev1; 1491d9f2626SSebastian Kapfer input_report_key(dev, BTN_MIDDLE, middle); 1501d9f2626SSebastian Kapfer 1511d9f2626SSebastian Kapfer /* 1521d9f2626SSebastian Kapfer * Sync the _other_ device now, we'll do the first 1531d9f2626SSebastian Kapfer * device later once we report the rest of the events. 1541d9f2626SSebastian Kapfer */ 1551d9f2626SSebastian Kapfer input_sync(dev2); 1561d9f2626SSebastian Kapfer } 1571d9f2626SSebastian Kapfer 15825bded7cSSeth Forshee static void alps_process_packet_v1_v2(struct psmouse *psmouse) 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 16171bb21b6SMaxim Levitsky const struct alps_model_info *model = priv->i; 1621da177e4SLinus Torvalds unsigned char *packet = psmouse->packet; 1632e5b636bSDmitry Torokhov struct input_dev *dev = psmouse->dev; 1642e5b636bSDmitry Torokhov struct input_dev *dev2 = priv->dev2; 1651da177e4SLinus Torvalds int x, y, z, ges, fin, left, right, middle; 166c30b4c10SIvan Casado Ruiz int back = 0, forward = 0; 1671da177e4SLinus Torvalds 168fa629ef5SSeth Forshee if (model->proto_version == ALPS_PROTO_V1) { 169d2f4012fSYotam Medini left = packet[2] & 0x10; 170d2f4012fSYotam Medini right = packet[2] & 0x08; 1711da177e4SLinus Torvalds middle = 0; 1721da177e4SLinus Torvalds x = packet[1] | ((packet[0] & 0x07) << 7); 1731da177e4SLinus Torvalds y = packet[4] | ((packet[3] & 0x07) << 7); 1741da177e4SLinus Torvalds z = packet[5]; 1751da177e4SLinus Torvalds } else { 1761da177e4SLinus Torvalds left = packet[3] & 1; 1771da177e4SLinus Torvalds right = packet[3] & 2; 1781da177e4SLinus Torvalds middle = packet[3] & 4; 1791da177e4SLinus Torvalds x = packet[1] | ((packet[2] & 0x78) << (7 - 3)); 1801da177e4SLinus Torvalds y = packet[4] | ((packet[3] & 0x70) << (7 - 4)); 1811da177e4SLinus Torvalds z = packet[5]; 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds 18471bb21b6SMaxim Levitsky if (model->flags & ALPS_FW_BK_1) { 1853c00bb96SLaszlo Kajan back = packet[0] & 0x10; 1863c00bb96SLaszlo Kajan forward = packet[2] & 4; 187c30b4c10SIvan Casado Ruiz } 188c30b4c10SIvan Casado Ruiz 18971bb21b6SMaxim Levitsky if (model->flags & ALPS_FW_BK_2) { 190c30b4c10SIvan Casado Ruiz back = packet[3] & 4; 191c30b4c10SIvan Casado Ruiz forward = packet[2] & 4; 192c30b4c10SIvan Casado Ruiz if ((middle = forward && back)) 193c30b4c10SIvan Casado Ruiz forward = back = 0; 194c30b4c10SIvan Casado Ruiz } 195c30b4c10SIvan Casado Ruiz 1961da177e4SLinus Torvalds ges = packet[2] & 1; 1971da177e4SLinus Torvalds fin = packet[2] & 2; 1981da177e4SLinus Torvalds 19971bb21b6SMaxim Levitsky if ((model->flags & ALPS_DUALPOINT) && z == 127) { 2001da177e4SLinus Torvalds input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); 2011da177e4SLinus Torvalds input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); 202d7ed5d88SUlrich Dangel 2031d9f2626SSebastian Kapfer alps_report_buttons(psmouse, dev2, dev, left, right, middle); 204d7ed5d88SUlrich Dangel 2051da177e4SLinus Torvalds input_sync(dev2); 2061da177e4SLinus Torvalds return; 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 2091d9f2626SSebastian Kapfer alps_report_buttons(psmouse, dev, dev2, left, right, middle); 210d7ed5d88SUlrich Dangel 2111da177e4SLinus Torvalds /* Convert hardware tap to a reasonable Z value */ 21271bb21b6SMaxim Levitsky if (ges && !fin) 21371bb21b6SMaxim Levitsky z = 40; 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds /* 2161da177e4SLinus Torvalds * A "tap and drag" operation is reported by the hardware as a transition 2171da177e4SLinus Torvalds * from (!fin && ges) to (fin && ges). This should be translated to the 2181da177e4SLinus Torvalds * sequence Z>0, Z==0, Z>0, so the Z==0 event has to be generated manually. 2191da177e4SLinus Torvalds */ 2201da177e4SLinus Torvalds if (ges && fin && !priv->prev_fin) { 2211da177e4SLinus Torvalds input_report_abs(dev, ABS_X, x); 2221da177e4SLinus Torvalds input_report_abs(dev, ABS_Y, y); 2231da177e4SLinus Torvalds input_report_abs(dev, ABS_PRESSURE, 0); 2241da177e4SLinus Torvalds input_report_key(dev, BTN_TOOL_FINGER, 0); 2251da177e4SLinus Torvalds input_sync(dev); 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds priv->prev_fin = fin; 2281da177e4SLinus Torvalds 22971bb21b6SMaxim Levitsky if (z > 30) 23071bb21b6SMaxim Levitsky input_report_key(dev, BTN_TOUCH, 1); 23171bb21b6SMaxim Levitsky if (z < 25) 23271bb21b6SMaxim Levitsky input_report_key(dev, BTN_TOUCH, 0); 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds if (z > 0) { 2351da177e4SLinus Torvalds input_report_abs(dev, ABS_X, x); 2361da177e4SLinus Torvalds input_report_abs(dev, ABS_Y, y); 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds input_report_abs(dev, ABS_PRESSURE, z); 2401da177e4SLinus Torvalds input_report_key(dev, BTN_TOOL_FINGER, z > 0); 2411da177e4SLinus Torvalds 24271bb21b6SMaxim Levitsky if (model->flags & ALPS_WHEEL) 243e6c047b9SVojtech Pavlik input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); 2441da177e4SLinus Torvalds 24571bb21b6SMaxim Levitsky if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { 246c30b4c10SIvan Casado Ruiz input_report_key(dev, BTN_FORWARD, forward); 247c30b4c10SIvan Casado Ruiz input_report_key(dev, BTN_BACK, back); 2481da177e4SLinus Torvalds } 2491da177e4SLinus Torvalds 25071bb21b6SMaxim Levitsky if (model->flags & ALPS_FOUR_BUTTONS) { 25171bb21b6SMaxim Levitsky input_report_key(dev, BTN_0, packet[2] & 4); 25271bb21b6SMaxim Levitsky input_report_key(dev, BTN_1, packet[0] & 0x10); 25371bb21b6SMaxim Levitsky input_report_key(dev, BTN_2, packet[3] & 4); 25471bb21b6SMaxim Levitsky input_report_key(dev, BTN_3, packet[0] & 0x20); 25571bb21b6SMaxim Levitsky } 25671bb21b6SMaxim Levitsky 2571da177e4SLinus Torvalds input_sync(dev); 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 26001ce661fSSeth Forshee /* 26101ce661fSSeth Forshee * Process bitmap data from v3 and v4 protocols. Returns the number of 26201ce661fSSeth Forshee * fingers detected. A return value of 0 means at least one of the 26301ce661fSSeth Forshee * bitmaps was empty. 26401ce661fSSeth Forshee * 26501ce661fSSeth Forshee * The bitmaps don't have enough data to track fingers, so this function 26601ce661fSSeth Forshee * only generates points representing a bounding box of all contacts. 26701ce661fSSeth Forshee * These points are returned in x1, y1, x2, and y2 when the return value 26801ce661fSSeth Forshee * is greater than 0. 26901ce661fSSeth Forshee */ 27001ce661fSSeth Forshee static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, 27101ce661fSSeth Forshee int *x1, int *y1, int *x2, int *y2) 27201ce661fSSeth Forshee { 27301ce661fSSeth Forshee struct alps_bitmap_point { 27401ce661fSSeth Forshee int start_bit; 27501ce661fSSeth Forshee int num_bits; 27601ce661fSSeth Forshee }; 27701ce661fSSeth Forshee 27801ce661fSSeth Forshee int fingers_x = 0, fingers_y = 0, fingers; 27901ce661fSSeth Forshee int i, bit, prev_bit; 28001ce661fSSeth Forshee struct alps_bitmap_point x_low = {0,}, x_high = {0,}; 28101ce661fSSeth Forshee struct alps_bitmap_point y_low = {0,}, y_high = {0,}; 28201ce661fSSeth Forshee struct alps_bitmap_point *point; 28301ce661fSSeth Forshee 28401ce661fSSeth Forshee if (!x_map || !y_map) 28501ce661fSSeth Forshee return 0; 28601ce661fSSeth Forshee 28701ce661fSSeth Forshee *x1 = *y1 = *x2 = *y2 = 0; 28801ce661fSSeth Forshee 28901ce661fSSeth Forshee prev_bit = 0; 29001ce661fSSeth Forshee point = &x_low; 29101ce661fSSeth Forshee for (i = 0; x_map != 0; i++, x_map >>= 1) { 29201ce661fSSeth Forshee bit = x_map & 1; 29301ce661fSSeth Forshee if (bit) { 29401ce661fSSeth Forshee if (!prev_bit) { 29501ce661fSSeth Forshee point->start_bit = i; 29601ce661fSSeth Forshee fingers_x++; 29701ce661fSSeth Forshee } 29801ce661fSSeth Forshee point->num_bits++; 29901ce661fSSeth Forshee } else { 30001ce661fSSeth Forshee if (prev_bit) 30101ce661fSSeth Forshee point = &x_high; 30201ce661fSSeth Forshee else 30301ce661fSSeth Forshee point->num_bits = 0; 30401ce661fSSeth Forshee } 30501ce661fSSeth Forshee prev_bit = bit; 30601ce661fSSeth Forshee } 30701ce661fSSeth Forshee 30801ce661fSSeth Forshee /* 30901ce661fSSeth Forshee * y bitmap is reversed for what we need (lower positions are in 31001ce661fSSeth Forshee * higher bits), so we process from the top end. 31101ce661fSSeth Forshee */ 31201ce661fSSeth Forshee y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS); 31301ce661fSSeth Forshee prev_bit = 0; 31401ce661fSSeth Forshee point = &y_low; 31501ce661fSSeth Forshee for (i = 0; y_map != 0; i++, y_map <<= 1) { 31601ce661fSSeth Forshee bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1)); 31701ce661fSSeth Forshee if (bit) { 31801ce661fSSeth Forshee if (!prev_bit) { 31901ce661fSSeth Forshee point->start_bit = i; 32001ce661fSSeth Forshee fingers_y++; 32101ce661fSSeth Forshee } 32201ce661fSSeth Forshee point->num_bits++; 32301ce661fSSeth Forshee } else { 32401ce661fSSeth Forshee if (prev_bit) 32501ce661fSSeth Forshee point = &y_high; 32601ce661fSSeth Forshee else 32701ce661fSSeth Forshee point->num_bits = 0; 32801ce661fSSeth Forshee } 32901ce661fSSeth Forshee prev_bit = bit; 33001ce661fSSeth Forshee } 33101ce661fSSeth Forshee 33201ce661fSSeth Forshee /* 33301ce661fSSeth Forshee * Fingers can overlap, so we use the maximum count of fingers 33401ce661fSSeth Forshee * on either axis as the finger count. 33501ce661fSSeth Forshee */ 33601ce661fSSeth Forshee fingers = max(fingers_x, fingers_y); 33701ce661fSSeth Forshee 33801ce661fSSeth Forshee /* 33901ce661fSSeth Forshee * If total fingers is > 1 but either axis reports only a single 34001ce661fSSeth Forshee * contact, we have overlapping or adjacent fingers. For the 34101ce661fSSeth Forshee * purposes of creating a bounding box, divide the single contact 34201ce661fSSeth Forshee * (roughly) equally between the two points. 34301ce661fSSeth Forshee */ 34401ce661fSSeth Forshee if (fingers > 1) { 34501ce661fSSeth Forshee if (fingers_x == 1) { 34601ce661fSSeth Forshee i = x_low.num_bits / 2; 34701ce661fSSeth Forshee x_low.num_bits = x_low.num_bits - i; 34801ce661fSSeth Forshee x_high.start_bit = x_low.start_bit + i; 34901ce661fSSeth Forshee x_high.num_bits = max(i, 1); 35001ce661fSSeth Forshee } else if (fingers_y == 1) { 35101ce661fSSeth Forshee i = y_low.num_bits / 2; 35201ce661fSSeth Forshee y_low.num_bits = y_low.num_bits - i; 35301ce661fSSeth Forshee y_high.start_bit = y_low.start_bit + i; 35401ce661fSSeth Forshee y_high.num_bits = max(i, 1); 35501ce661fSSeth Forshee } 35601ce661fSSeth Forshee } 35701ce661fSSeth Forshee 35801ce661fSSeth Forshee *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) / 35901ce661fSSeth Forshee (2 * (ALPS_BITMAP_X_BITS - 1)); 36001ce661fSSeth Forshee *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) / 36101ce661fSSeth Forshee (2 * (ALPS_BITMAP_Y_BITS - 1)); 36201ce661fSSeth Forshee 36301ce661fSSeth Forshee if (fingers > 1) { 36401ce661fSSeth Forshee *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) / 36501ce661fSSeth Forshee (2 * (ALPS_BITMAP_X_BITS - 1)); 36601ce661fSSeth Forshee *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) / 36701ce661fSSeth Forshee (2 * (ALPS_BITMAP_Y_BITS - 1)); 36801ce661fSSeth Forshee } 36901ce661fSSeth Forshee 37001ce661fSSeth Forshee return fingers; 37101ce661fSSeth Forshee } 37201ce661fSSeth Forshee 37301ce661fSSeth Forshee static void alps_set_slot(struct input_dev *dev, int slot, bool active, 37401ce661fSSeth Forshee int x, int y) 37501ce661fSSeth Forshee { 37601ce661fSSeth Forshee input_mt_slot(dev, slot); 37701ce661fSSeth Forshee input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); 37801ce661fSSeth Forshee if (active) { 37901ce661fSSeth Forshee input_report_abs(dev, ABS_MT_POSITION_X, x); 38001ce661fSSeth Forshee input_report_abs(dev, ABS_MT_POSITION_Y, y); 38101ce661fSSeth Forshee } 38201ce661fSSeth Forshee } 38301ce661fSSeth Forshee 38401ce661fSSeth Forshee static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, 38501ce661fSSeth Forshee int x1, int y1, int x2, int y2) 38601ce661fSSeth Forshee { 38701ce661fSSeth Forshee alps_set_slot(dev, 0, num_fingers != 0, x1, y1); 38801ce661fSSeth Forshee alps_set_slot(dev, 1, num_fingers == 2, x2, y2); 38901ce661fSSeth Forshee } 39001ce661fSSeth Forshee 39125bded7cSSeth Forshee static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) 39225bded7cSSeth Forshee { 39325bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 39425bded7cSSeth Forshee unsigned char *packet = psmouse->packet; 39525bded7cSSeth Forshee struct input_dev *dev = priv->dev2; 39625bded7cSSeth Forshee int x, y, z, left, right, middle; 39725bded7cSSeth Forshee 39825bded7cSSeth Forshee /* Sanity check packet */ 39925bded7cSSeth Forshee if (!(packet[0] & 0x40)) { 40025bded7cSSeth Forshee psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); 40125bded7cSSeth Forshee return; 40225bded7cSSeth Forshee } 40325bded7cSSeth Forshee 40425bded7cSSeth Forshee /* 40525bded7cSSeth Forshee * There's a special packet that seems to indicate the end 40625bded7cSSeth Forshee * of a stream of trackstick data. Filter these out. 40725bded7cSSeth Forshee */ 40825bded7cSSeth Forshee if (packet[1] == 0x7f && packet[2] == 0x7f && packet[4] == 0x7f) 40925bded7cSSeth Forshee return; 41025bded7cSSeth Forshee 41125bded7cSSeth Forshee x = (s8)(((packet[0] & 0x20) << 2) | (packet[1] & 0x7f)); 41225bded7cSSeth Forshee y = (s8)(((packet[0] & 0x10) << 3) | (packet[2] & 0x7f)); 41325bded7cSSeth Forshee z = (packet[4] & 0x7c) >> 2; 41425bded7cSSeth Forshee 41525bded7cSSeth Forshee /* 41625bded7cSSeth Forshee * The x and y values tend to be quite large, and when used 41725bded7cSSeth Forshee * alone the trackstick is difficult to use. Scale them down 41825bded7cSSeth Forshee * to compensate. 41925bded7cSSeth Forshee */ 42025bded7cSSeth Forshee x /= 8; 42125bded7cSSeth Forshee y /= 8; 42225bded7cSSeth Forshee 42325bded7cSSeth Forshee input_report_rel(dev, REL_X, x); 42425bded7cSSeth Forshee input_report_rel(dev, REL_Y, -y); 42525bded7cSSeth Forshee 42625bded7cSSeth Forshee /* 42725bded7cSSeth Forshee * Most ALPS models report the trackstick buttons in the touchpad 42825bded7cSSeth Forshee * packets, but a few report them here. No reliable way has been 42925bded7cSSeth Forshee * found to differentiate between the models upfront, so we enable 43025bded7cSSeth Forshee * the quirk in response to seeing a button press in the trackstick 43125bded7cSSeth Forshee * packet. 43225bded7cSSeth Forshee */ 43325bded7cSSeth Forshee left = packet[3] & 0x01; 43425bded7cSSeth Forshee right = packet[3] & 0x02; 43525bded7cSSeth Forshee middle = packet[3] & 0x04; 43625bded7cSSeth Forshee 43725bded7cSSeth Forshee if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) && 43825bded7cSSeth Forshee (left || right || middle)) 43925bded7cSSeth Forshee priv->quirks |= ALPS_QUIRK_TRACKSTICK_BUTTONS; 44025bded7cSSeth Forshee 44125bded7cSSeth Forshee if (priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS) { 44225bded7cSSeth Forshee input_report_key(dev, BTN_LEFT, left); 44325bded7cSSeth Forshee input_report_key(dev, BTN_RIGHT, right); 44425bded7cSSeth Forshee input_report_key(dev, BTN_MIDDLE, middle); 44525bded7cSSeth Forshee } 44625bded7cSSeth Forshee 44725bded7cSSeth Forshee input_sync(dev); 44825bded7cSSeth Forshee return; 44925bded7cSSeth Forshee } 45025bded7cSSeth Forshee 45125bded7cSSeth Forshee static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) 45225bded7cSSeth Forshee { 45325bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 45425bded7cSSeth Forshee unsigned char *packet = psmouse->packet; 45525bded7cSSeth Forshee struct input_dev *dev = psmouse->dev; 45625bded7cSSeth Forshee struct input_dev *dev2 = priv->dev2; 45725bded7cSSeth Forshee int x, y, z; 45825bded7cSSeth Forshee int left, right, middle; 45901ce661fSSeth Forshee int x1 = 0, y1 = 0, x2 = 0, y2 = 0; 46001ce661fSSeth Forshee int fingers = 0, bmap_fingers; 46101ce661fSSeth Forshee unsigned int x_bitmap, y_bitmap; 46225bded7cSSeth Forshee 46325bded7cSSeth Forshee /* 46401ce661fSSeth Forshee * There's no single feature of touchpad position and bitmap packets 46501ce661fSSeth Forshee * that can be used to distinguish between them. We rely on the fact 46601ce661fSSeth Forshee * that a bitmap packet should always follow a position packet with 46701ce661fSSeth Forshee * bit 6 of packet[4] set. 46825bded7cSSeth Forshee */ 46925bded7cSSeth Forshee if (priv->multi_packet) { 47025bded7cSSeth Forshee /* 47125bded7cSSeth Forshee * Sometimes a position packet will indicate a multi-packet 47225bded7cSSeth Forshee * sequence, but then what follows is another position 47325bded7cSSeth Forshee * packet. Check for this, and when it happens process the 47425bded7cSSeth Forshee * position packet as usual. 47525bded7cSSeth Forshee */ 47625bded7cSSeth Forshee if (packet[0] & 0x40) { 47701ce661fSSeth Forshee fingers = (packet[5] & 0x3) + 1; 47801ce661fSSeth Forshee x_bitmap = ((packet[4] & 0x7e) << 8) | 47901ce661fSSeth Forshee ((packet[1] & 0x7f) << 2) | 48001ce661fSSeth Forshee ((packet[0] & 0x30) >> 4); 48101ce661fSSeth Forshee y_bitmap = ((packet[3] & 0x70) << 4) | 48201ce661fSSeth Forshee ((packet[2] & 0x7f) << 1) | 48301ce661fSSeth Forshee (packet[4] & 0x01); 48401ce661fSSeth Forshee 48501ce661fSSeth Forshee bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap, 48601ce661fSSeth Forshee &x1, &y1, &x2, &y2); 48701ce661fSSeth Forshee 48825bded7cSSeth Forshee /* 48901ce661fSSeth Forshee * We shouldn't report more than one finger if 49001ce661fSSeth Forshee * we don't have two coordinates. 49125bded7cSSeth Forshee */ 49201ce661fSSeth Forshee if (fingers > 1 && bmap_fingers < 2) 49301ce661fSSeth Forshee fingers = bmap_fingers; 49401ce661fSSeth Forshee 49501ce661fSSeth Forshee /* Now process position packet */ 49601ce661fSSeth Forshee packet = priv->multi_data; 49701ce661fSSeth Forshee } else { 49801ce661fSSeth Forshee priv->multi_packet = 0; 49925bded7cSSeth Forshee } 50025bded7cSSeth Forshee } 50125bded7cSSeth Forshee 50201ce661fSSeth Forshee /* 50301ce661fSSeth Forshee * Bit 6 of byte 0 is not usually set in position packets. The only 50401ce661fSSeth Forshee * times it seems to be set is in situations where the data is 50501ce661fSSeth Forshee * suspect anyway, e.g. a palm resting flat on the touchpad. Given 50601ce661fSSeth Forshee * this combined with the fact that this bit is useful for filtering 50701ce661fSSeth Forshee * out misidentified bitmap packets, we reject anything with this 50801ce661fSSeth Forshee * bit set. 50901ce661fSSeth Forshee */ 51001ce661fSSeth Forshee if (packet[0] & 0x40) 51101ce661fSSeth Forshee return; 51201ce661fSSeth Forshee 51301ce661fSSeth Forshee if (!priv->multi_packet && (packet[4] & 0x40)) { 51425bded7cSSeth Forshee priv->multi_packet = 1; 51501ce661fSSeth Forshee memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); 51601ce661fSSeth Forshee return; 51701ce661fSSeth Forshee } 51801ce661fSSeth Forshee 51925bded7cSSeth Forshee priv->multi_packet = 0; 52025bded7cSSeth Forshee 52125bded7cSSeth Forshee left = packet[3] & 0x01; 52225bded7cSSeth Forshee right = packet[3] & 0x02; 52325bded7cSSeth Forshee middle = packet[3] & 0x04; 52425bded7cSSeth Forshee 52525bded7cSSeth Forshee x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) | 52625bded7cSSeth Forshee ((packet[0] & 0x30) >> 4); 52725bded7cSSeth Forshee y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f); 52825bded7cSSeth Forshee z = packet[5] & 0x7f; 52925bded7cSSeth Forshee 53025bded7cSSeth Forshee /* 53125bded7cSSeth Forshee * Sometimes the hardware sends a single packet with z = 0 53225bded7cSSeth Forshee * in the middle of a stream. Real releases generate packets 53325bded7cSSeth Forshee * with x, y, and z all zero, so these seem to be flukes. 53425bded7cSSeth Forshee * Ignore them. 53525bded7cSSeth Forshee */ 53625bded7cSSeth Forshee if (x && y && !z) 53725bded7cSSeth Forshee return; 53825bded7cSSeth Forshee 53901ce661fSSeth Forshee /* 54001ce661fSSeth Forshee * If we don't have MT data or the bitmaps were empty, we have 54101ce661fSSeth Forshee * to rely on ST data. 54201ce661fSSeth Forshee */ 54301ce661fSSeth Forshee if (!fingers) { 54401ce661fSSeth Forshee x1 = x; 54501ce661fSSeth Forshee y1 = y; 54601ce661fSSeth Forshee fingers = z > 0 ? 1 : 0; 54701ce661fSSeth Forshee } 54801ce661fSSeth Forshee 54925bded7cSSeth Forshee if (z >= 64) 55025bded7cSSeth Forshee input_report_key(dev, BTN_TOUCH, 1); 55125bded7cSSeth Forshee else 55225bded7cSSeth Forshee input_report_key(dev, BTN_TOUCH, 0); 55325bded7cSSeth Forshee 55401ce661fSSeth Forshee alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); 55501ce661fSSeth Forshee 556616575c2SDmitry Torokhov input_mt_report_finger_count(dev, fingers); 55701ce661fSSeth Forshee 55801ce661fSSeth Forshee input_report_key(dev, BTN_LEFT, left); 55901ce661fSSeth Forshee input_report_key(dev, BTN_RIGHT, right); 56001ce661fSSeth Forshee input_report_key(dev, BTN_MIDDLE, middle); 56101ce661fSSeth Forshee 56225bded7cSSeth Forshee if (z > 0) { 56325bded7cSSeth Forshee input_report_abs(dev, ABS_X, x); 56425bded7cSSeth Forshee input_report_abs(dev, ABS_Y, y); 56525bded7cSSeth Forshee } 56625bded7cSSeth Forshee input_report_abs(dev, ABS_PRESSURE, z); 56725bded7cSSeth Forshee 56825bded7cSSeth Forshee input_sync(dev); 56925bded7cSSeth Forshee 57025bded7cSSeth Forshee if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { 57125bded7cSSeth Forshee left = packet[3] & 0x10; 57225bded7cSSeth Forshee right = packet[3] & 0x20; 57325bded7cSSeth Forshee middle = packet[3] & 0x40; 57425bded7cSSeth Forshee 57525bded7cSSeth Forshee input_report_key(dev2, BTN_LEFT, left); 57625bded7cSSeth Forshee input_report_key(dev2, BTN_RIGHT, right); 57725bded7cSSeth Forshee input_report_key(dev2, BTN_MIDDLE, middle); 57825bded7cSSeth Forshee input_sync(dev2); 57925bded7cSSeth Forshee } 58025bded7cSSeth Forshee } 58125bded7cSSeth Forshee 58225bded7cSSeth Forshee static void alps_process_packet_v3(struct psmouse *psmouse) 58325bded7cSSeth Forshee { 58425bded7cSSeth Forshee unsigned char *packet = psmouse->packet; 58525bded7cSSeth Forshee 58625bded7cSSeth Forshee /* 58725bded7cSSeth Forshee * v3 protocol packets come in three types, two representing 58825bded7cSSeth Forshee * touchpad data and one representing trackstick data. 58925bded7cSSeth Forshee * Trackstick packets seem to be distinguished by always 59025bded7cSSeth Forshee * having 0x3f in the last byte. This value has never been 59125bded7cSSeth Forshee * observed in the last byte of either of the other types 59225bded7cSSeth Forshee * of packets. 59325bded7cSSeth Forshee */ 59425bded7cSSeth Forshee if (packet[5] == 0x3f) { 59525bded7cSSeth Forshee alps_process_trackstick_packet_v3(psmouse); 59625bded7cSSeth Forshee return; 59725bded7cSSeth Forshee } 59825bded7cSSeth Forshee 59925bded7cSSeth Forshee alps_process_touchpad_packet_v3(psmouse); 60025bded7cSSeth Forshee } 60125bded7cSSeth Forshee 60225bded7cSSeth Forshee static void alps_process_packet_v4(struct psmouse *psmouse) 60325bded7cSSeth Forshee { 6043b7e09faSGeorge Pantalos struct alps_data *priv = psmouse->private; 60525bded7cSSeth Forshee unsigned char *packet = psmouse->packet; 60625bded7cSSeth Forshee struct input_dev *dev = psmouse->dev; 6073b7e09faSGeorge Pantalos int offset; 60825bded7cSSeth Forshee int x, y, z; 60925bded7cSSeth Forshee int left, right; 6103b7e09faSGeorge Pantalos int x1, y1, x2, y2; 6113b7e09faSGeorge Pantalos int fingers = 0; 6123b7e09faSGeorge Pantalos unsigned int x_bitmap, y_bitmap; 6133b7e09faSGeorge Pantalos 6143b7e09faSGeorge Pantalos /* 6153b7e09faSGeorge Pantalos * v4 has a 6-byte encoding for bitmap data, but this data is 6163b7e09faSGeorge Pantalos * broken up between 3 normal packets. Use priv->multi_packet to 6173b7e09faSGeorge Pantalos * track our position in the bitmap packet. 6183b7e09faSGeorge Pantalos */ 6193b7e09faSGeorge Pantalos if (packet[6] & 0x40) { 6203b7e09faSGeorge Pantalos /* sync, reset position */ 6213b7e09faSGeorge Pantalos priv->multi_packet = 0; 6223b7e09faSGeorge Pantalos } 6233b7e09faSGeorge Pantalos 6243b7e09faSGeorge Pantalos if (WARN_ON_ONCE(priv->multi_packet > 2)) 6253b7e09faSGeorge Pantalos return; 6263b7e09faSGeorge Pantalos 6273b7e09faSGeorge Pantalos offset = 2 * priv->multi_packet; 6283b7e09faSGeorge Pantalos priv->multi_data[offset] = packet[6]; 6293b7e09faSGeorge Pantalos priv->multi_data[offset + 1] = packet[7]; 6303b7e09faSGeorge Pantalos 6313b7e09faSGeorge Pantalos if (++priv->multi_packet > 2) { 6323b7e09faSGeorge Pantalos priv->multi_packet = 0; 6333b7e09faSGeorge Pantalos 6343b7e09faSGeorge Pantalos x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) | 6353b7e09faSGeorge Pantalos ((priv->multi_data[3] & 0x60) << 3) | 6363b7e09faSGeorge Pantalos ((priv->multi_data[0] & 0x3f) << 2) | 6373b7e09faSGeorge Pantalos ((priv->multi_data[1] & 0x60) >> 5); 6383b7e09faSGeorge Pantalos y_bitmap = ((priv->multi_data[5] & 0x01) << 10) | 6393b7e09faSGeorge Pantalos ((priv->multi_data[3] & 0x1f) << 5) | 6403b7e09faSGeorge Pantalos (priv->multi_data[1] & 0x1f); 6413b7e09faSGeorge Pantalos 6423b7e09faSGeorge Pantalos fingers = alps_process_bitmap(x_bitmap, y_bitmap, 6433b7e09faSGeorge Pantalos &x1, &y1, &x2, &y2); 6443b7e09faSGeorge Pantalos 6453b7e09faSGeorge Pantalos /* Store MT data.*/ 6463b7e09faSGeorge Pantalos priv->fingers = fingers; 6473b7e09faSGeorge Pantalos priv->x1 = x1; 6483b7e09faSGeorge Pantalos priv->x2 = x2; 6493b7e09faSGeorge Pantalos priv->y1 = y1; 6503b7e09faSGeorge Pantalos priv->y2 = y2; 6513b7e09faSGeorge Pantalos } 65225bded7cSSeth Forshee 65325bded7cSSeth Forshee left = packet[4] & 0x01; 65425bded7cSSeth Forshee right = packet[4] & 0x02; 65525bded7cSSeth Forshee 65625bded7cSSeth Forshee x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | 65725bded7cSSeth Forshee ((packet[0] & 0x30) >> 4); 65825bded7cSSeth Forshee y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); 65925bded7cSSeth Forshee z = packet[5] & 0x7f; 66025bded7cSSeth Forshee 6613b7e09faSGeorge Pantalos /* 6623b7e09faSGeorge Pantalos * If there were no contacts in the bitmap, use ST 6633b7e09faSGeorge Pantalos * points in MT reports. 6643b7e09faSGeorge Pantalos * If there were two contacts or more, report MT data. 6653b7e09faSGeorge Pantalos */ 6663b7e09faSGeorge Pantalos if (priv->fingers < 2) { 6673b7e09faSGeorge Pantalos x1 = x; 6683b7e09faSGeorge Pantalos y1 = y; 6693b7e09faSGeorge Pantalos fingers = z > 0 ? 1 : 0; 6703b7e09faSGeorge Pantalos } else { 6713b7e09faSGeorge Pantalos fingers = priv->fingers; 6723b7e09faSGeorge Pantalos x1 = priv->x1; 6733b7e09faSGeorge Pantalos x2 = priv->x2; 6743b7e09faSGeorge Pantalos y1 = priv->y1; 6753b7e09faSGeorge Pantalos y2 = priv->y2; 6763b7e09faSGeorge Pantalos } 6773b7e09faSGeorge Pantalos 67825bded7cSSeth Forshee if (z >= 64) 67925bded7cSSeth Forshee input_report_key(dev, BTN_TOUCH, 1); 68025bded7cSSeth Forshee else 68125bded7cSSeth Forshee input_report_key(dev, BTN_TOUCH, 0); 68225bded7cSSeth Forshee 6833b7e09faSGeorge Pantalos alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); 6843b7e09faSGeorge Pantalos 685616575c2SDmitry Torokhov input_mt_report_finger_count(dev, fingers); 6863b7e09faSGeorge Pantalos 6873b7e09faSGeorge Pantalos input_report_key(dev, BTN_LEFT, left); 6883b7e09faSGeorge Pantalos input_report_key(dev, BTN_RIGHT, right); 6893b7e09faSGeorge Pantalos 69025bded7cSSeth Forshee if (z > 0) { 69125bded7cSSeth Forshee input_report_abs(dev, ABS_X, x); 69225bded7cSSeth Forshee input_report_abs(dev, ABS_Y, y); 69325bded7cSSeth Forshee } 69425bded7cSSeth Forshee input_report_abs(dev, ABS_PRESSURE, z); 69525bded7cSSeth Forshee 69625bded7cSSeth Forshee input_sync(dev); 69725bded7cSSeth Forshee } 69825bded7cSSeth Forshee 69925bded7cSSeth Forshee static void alps_process_packet(struct psmouse *psmouse) 70025bded7cSSeth Forshee { 70125bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 70225bded7cSSeth Forshee const struct alps_model_info *model = priv->i; 70325bded7cSSeth Forshee 70425bded7cSSeth Forshee switch (model->proto_version) { 70525bded7cSSeth Forshee case ALPS_PROTO_V1: 70625bded7cSSeth Forshee case ALPS_PROTO_V2: 70725bded7cSSeth Forshee alps_process_packet_v1_v2(psmouse); 70825bded7cSSeth Forshee break; 70925bded7cSSeth Forshee case ALPS_PROTO_V3: 71025bded7cSSeth Forshee alps_process_packet_v3(psmouse); 71125bded7cSSeth Forshee break; 71225bded7cSSeth Forshee case ALPS_PROTO_V4: 71325bded7cSSeth Forshee alps_process_packet_v4(psmouse); 71425bded7cSSeth Forshee break; 71525bded7cSSeth Forshee } 71625bded7cSSeth Forshee } 71725bded7cSSeth Forshee 7181d9f2626SSebastian Kapfer static void alps_report_bare_ps2_packet(struct psmouse *psmouse, 7191d9f2626SSebastian Kapfer unsigned char packet[], 7201d9f2626SSebastian Kapfer bool report_buttons) 7211d9f2626SSebastian Kapfer { 7221d9f2626SSebastian Kapfer struct alps_data *priv = psmouse->private; 7231d9f2626SSebastian Kapfer struct input_dev *dev2 = priv->dev2; 7241d9f2626SSebastian Kapfer 7251d9f2626SSebastian Kapfer if (report_buttons) 7261d9f2626SSebastian Kapfer alps_report_buttons(psmouse, dev2, psmouse->dev, 7271d9f2626SSebastian Kapfer packet[0] & 1, packet[0] & 2, packet[0] & 4); 7281d9f2626SSebastian Kapfer 7291d9f2626SSebastian Kapfer input_report_rel(dev2, REL_X, 7301d9f2626SSebastian Kapfer packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0); 7311d9f2626SSebastian Kapfer input_report_rel(dev2, REL_Y, 7321d9f2626SSebastian Kapfer packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0); 7331d9f2626SSebastian Kapfer 7341d9f2626SSebastian Kapfer input_sync(dev2); 7351d9f2626SSebastian Kapfer } 7361d9f2626SSebastian Kapfer 7371d9f2626SSebastian Kapfer static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) 7381da177e4SLinus Torvalds { 7391da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 7401da177e4SLinus Torvalds 7411d9f2626SSebastian Kapfer if (psmouse->pktcnt < 6) 7421d9f2626SSebastian Kapfer return PSMOUSE_GOOD_DATA; 7431d9f2626SSebastian Kapfer 7441d9f2626SSebastian Kapfer if (psmouse->pktcnt == 6) { 7451d9f2626SSebastian Kapfer /* 7461d9f2626SSebastian Kapfer * Start a timer to flush the packet if it ends up last 7471d9f2626SSebastian Kapfer * 6-byte packet in the stream. Timer needs to fire 7481d9f2626SSebastian Kapfer * psmouse core times out itself. 20 ms should be enough 7491d9f2626SSebastian Kapfer * to decide if we are getting more data or not. 7501d9f2626SSebastian Kapfer */ 7511d9f2626SSebastian Kapfer mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20)); 7521d9f2626SSebastian Kapfer return PSMOUSE_GOOD_DATA; 7531d9f2626SSebastian Kapfer } 7541d9f2626SSebastian Kapfer 7551d9f2626SSebastian Kapfer del_timer(&priv->timer); 7561d9f2626SSebastian Kapfer 7571d9f2626SSebastian Kapfer if (psmouse->packet[6] & 0x80) { 7581d9f2626SSebastian Kapfer 7591d9f2626SSebastian Kapfer /* 7601d9f2626SSebastian Kapfer * Highest bit is set - that means we either had 7611d9f2626SSebastian Kapfer * complete ALPS packet and this is start of the 7621d9f2626SSebastian Kapfer * next packet or we got garbage. 7631d9f2626SSebastian Kapfer */ 7641d9f2626SSebastian Kapfer 7651d9f2626SSebastian Kapfer if (((psmouse->packet[3] | 7661d9f2626SSebastian Kapfer psmouse->packet[4] | 7671d9f2626SSebastian Kapfer psmouse->packet[5]) & 0x80) || 7681d9f2626SSebastian Kapfer (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { 769b5d21704SDmitry Torokhov psmouse_dbg(psmouse, 770b5d21704SDmitry Torokhov "refusing packet %x %x %x %x (suspected interleaved ps/2)\n", 7711d9f2626SSebastian Kapfer psmouse->packet[3], psmouse->packet[4], 7721d9f2626SSebastian Kapfer psmouse->packet[5], psmouse->packet[6]); 7731d9f2626SSebastian Kapfer return PSMOUSE_BAD_DATA; 7741d9f2626SSebastian Kapfer } 7751d9f2626SSebastian Kapfer 7761d9f2626SSebastian Kapfer alps_process_packet(psmouse); 7771d9f2626SSebastian Kapfer 7781d9f2626SSebastian Kapfer /* Continue with the next packet */ 7791d9f2626SSebastian Kapfer psmouse->packet[0] = psmouse->packet[6]; 7801d9f2626SSebastian Kapfer psmouse->pktcnt = 1; 7811d9f2626SSebastian Kapfer 7821d9f2626SSebastian Kapfer } else { 7831d9f2626SSebastian Kapfer 7841d9f2626SSebastian Kapfer /* 7851d9f2626SSebastian Kapfer * High bit is 0 - that means that we indeed got a PS/2 7861d9f2626SSebastian Kapfer * packet in the middle of ALPS packet. 7871d9f2626SSebastian Kapfer * 7881d9f2626SSebastian Kapfer * There is also possibility that we got 6-byte ALPS 7891d9f2626SSebastian Kapfer * packet followed by 3-byte packet from trackpoint. We 7901d9f2626SSebastian Kapfer * can not distinguish between these 2 scenarios but 791b5d21704SDmitry Torokhov * because the latter is unlikely to happen in course of 7921d9f2626SSebastian Kapfer * normal operation (user would need to press all 7931d9f2626SSebastian Kapfer * buttons on the pad and start moving trackpoint 7941d9f2626SSebastian Kapfer * without touching the pad surface) we assume former. 7951d9f2626SSebastian Kapfer * Even if we are wrong the wost thing that would happen 7961d9f2626SSebastian Kapfer * the cursor would jump but we should not get protocol 797b5d21704SDmitry Torokhov * de-synchronization. 7981d9f2626SSebastian Kapfer */ 7991d9f2626SSebastian Kapfer 8001d9f2626SSebastian Kapfer alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], 8011d9f2626SSebastian Kapfer false); 8021d9f2626SSebastian Kapfer 8031d9f2626SSebastian Kapfer /* 8041d9f2626SSebastian Kapfer * Continue with the standard ALPS protocol handling, 8051d9f2626SSebastian Kapfer * but make sure we won't process it as an interleaved 8061d9f2626SSebastian Kapfer * packet again, which may happen if all buttons are 8071d9f2626SSebastian Kapfer * pressed. To avoid this let's reset the 4th bit which 8081d9f2626SSebastian Kapfer * is normally 1. 8091d9f2626SSebastian Kapfer */ 8101d9f2626SSebastian Kapfer psmouse->packet[3] = psmouse->packet[6] & 0xf7; 8111d9f2626SSebastian Kapfer psmouse->pktcnt = 4; 8121d9f2626SSebastian Kapfer } 8131d9f2626SSebastian Kapfer 8141d9f2626SSebastian Kapfer return PSMOUSE_GOOD_DATA; 8151d9f2626SSebastian Kapfer } 8161d9f2626SSebastian Kapfer 8171d9f2626SSebastian Kapfer static void alps_flush_packet(unsigned long data) 8181d9f2626SSebastian Kapfer { 8191d9f2626SSebastian Kapfer struct psmouse *psmouse = (struct psmouse *)data; 8201d9f2626SSebastian Kapfer 8211d9f2626SSebastian Kapfer serio_pause_rx(psmouse->ps2dev.serio); 8221d9f2626SSebastian Kapfer 823b46615feSSeth Forshee if (psmouse->pktcnt == psmouse->pktsize) { 8241d9f2626SSebastian Kapfer 8251d9f2626SSebastian Kapfer /* 8261d9f2626SSebastian Kapfer * We did not any more data in reasonable amount of time. 8271d9f2626SSebastian Kapfer * Validate the last 3 bytes and process as a standard 8281d9f2626SSebastian Kapfer * ALPS packet. 8291d9f2626SSebastian Kapfer */ 8301d9f2626SSebastian Kapfer if ((psmouse->packet[3] | 8311d9f2626SSebastian Kapfer psmouse->packet[4] | 8321d9f2626SSebastian Kapfer psmouse->packet[5]) & 0x80) { 833b5d21704SDmitry Torokhov psmouse_dbg(psmouse, 834b5d21704SDmitry Torokhov "refusing packet %x %x %x (suspected interleaved ps/2)\n", 8351d9f2626SSebastian Kapfer psmouse->packet[3], psmouse->packet[4], 8361d9f2626SSebastian Kapfer psmouse->packet[5]); 8371d9f2626SSebastian Kapfer } else { 8381d9f2626SSebastian Kapfer alps_process_packet(psmouse); 8391d9f2626SSebastian Kapfer } 8401d9f2626SSebastian Kapfer psmouse->pktcnt = 0; 8411d9f2626SSebastian Kapfer } 8421d9f2626SSebastian Kapfer 8431d9f2626SSebastian Kapfer serio_continue_rx(psmouse->ps2dev.serio); 8441d9f2626SSebastian Kapfer } 8451d9f2626SSebastian Kapfer 8461d9f2626SSebastian Kapfer static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) 8471d9f2626SSebastian Kapfer { 8481d9f2626SSebastian Kapfer struct alps_data *priv = psmouse->private; 8491d9f2626SSebastian Kapfer const struct alps_model_info *model = priv->i; 8501d9f2626SSebastian Kapfer 8511da177e4SLinus Torvalds if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ 8521da177e4SLinus Torvalds if (psmouse->pktcnt == 3) { 8531d9f2626SSebastian Kapfer alps_report_bare_ps2_packet(psmouse, psmouse->packet, 8541d9f2626SSebastian Kapfer true); 8551da177e4SLinus Torvalds return PSMOUSE_FULL_PACKET; 8561da177e4SLinus Torvalds } 8571da177e4SLinus Torvalds return PSMOUSE_GOOD_DATA; 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds 8601d9f2626SSebastian Kapfer /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ 8611d9f2626SSebastian Kapfer 8621d9f2626SSebastian Kapfer if ((model->flags & ALPS_PS2_INTERLEAVED) && 8631d9f2626SSebastian Kapfer psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) { 8641d9f2626SSebastian Kapfer return alps_handle_interleaved_ps2(psmouse); 8651d9f2626SSebastian Kapfer } 8661d9f2626SSebastian Kapfer 8671d9f2626SSebastian Kapfer if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { 868b5d21704SDmitry Torokhov psmouse_dbg(psmouse, 869b5d21704SDmitry Torokhov "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", 8701d9f2626SSebastian Kapfer psmouse->packet[0], model->mask0, model->byte0); 8711da177e4SLinus Torvalds return PSMOUSE_BAD_DATA; 8721d9f2626SSebastian Kapfer } 8731da177e4SLinus Torvalds 874b46615feSSeth Forshee /* Bytes 2 - pktsize should have 0 in the highest bit */ 875b46615feSSeth Forshee if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && 8761d9f2626SSebastian Kapfer (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { 877b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", 878b5d21704SDmitry Torokhov psmouse->pktcnt - 1, 879b5d21704SDmitry Torokhov psmouse->packet[psmouse->pktcnt - 1]); 8801da177e4SLinus Torvalds return PSMOUSE_BAD_DATA; 8811d9f2626SSebastian Kapfer } 8821da177e4SLinus Torvalds 883b46615feSSeth Forshee if (psmouse->pktcnt == psmouse->pktsize) { 8847d12e780SDavid Howells alps_process_packet(psmouse); 8851da177e4SLinus Torvalds return PSMOUSE_FULL_PACKET; 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds 8881da177e4SLinus Torvalds return PSMOUSE_GOOD_DATA; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds 89125bded7cSSeth Forshee static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble) 89225bded7cSSeth Forshee { 89325bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 89425bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 89525bded7cSSeth Forshee int command; 89625bded7cSSeth Forshee unsigned char *param; 89725bded7cSSeth Forshee unsigned char dummy[4]; 89825bded7cSSeth Forshee 89925bded7cSSeth Forshee BUG_ON(nibble > 0xf); 90025bded7cSSeth Forshee 90125bded7cSSeth Forshee command = priv->nibble_commands[nibble].command; 90225bded7cSSeth Forshee param = (command & 0x0f00) ? 90325bded7cSSeth Forshee dummy : (unsigned char *)&priv->nibble_commands[nibble].data; 90425bded7cSSeth Forshee 90525bded7cSSeth Forshee if (ps2_command(ps2dev, param, command)) 90625bded7cSSeth Forshee return -1; 90725bded7cSSeth Forshee 90825bded7cSSeth Forshee return 0; 90925bded7cSSeth Forshee } 91025bded7cSSeth Forshee 91125bded7cSSeth Forshee static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr) 91225bded7cSSeth Forshee { 91325bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 91425bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 91525bded7cSSeth Forshee int i, nibble; 91625bded7cSSeth Forshee 91725bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, priv->addr_command)) 91825bded7cSSeth Forshee return -1; 91925bded7cSSeth Forshee 92025bded7cSSeth Forshee for (i = 12; i >= 0; i -= 4) { 92125bded7cSSeth Forshee nibble = (addr >> i) & 0xf; 92225bded7cSSeth Forshee if (alps_command_mode_send_nibble(psmouse, nibble)) 92325bded7cSSeth Forshee return -1; 92425bded7cSSeth Forshee } 92525bded7cSSeth Forshee 92625bded7cSSeth Forshee return 0; 92725bded7cSSeth Forshee } 92825bded7cSSeth Forshee 92925bded7cSSeth Forshee static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr) 93025bded7cSSeth Forshee { 93125bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 93225bded7cSSeth Forshee unsigned char param[4]; 93325bded7cSSeth Forshee 93425bded7cSSeth Forshee if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 93525bded7cSSeth Forshee return -1; 93625bded7cSSeth Forshee 93725bded7cSSeth Forshee /* 93825bded7cSSeth Forshee * The address being read is returned in the first two bytes 93925bded7cSSeth Forshee * of the result. Check that this address matches the expected 94025bded7cSSeth Forshee * address. 94125bded7cSSeth Forshee */ 94225bded7cSSeth Forshee if (addr != ((param[0] << 8) | param[1])) 94325bded7cSSeth Forshee return -1; 94425bded7cSSeth Forshee 94525bded7cSSeth Forshee return param[2]; 94625bded7cSSeth Forshee } 94725bded7cSSeth Forshee 94825bded7cSSeth Forshee static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr) 94925bded7cSSeth Forshee { 95025bded7cSSeth Forshee if (alps_command_mode_set_addr(psmouse, addr)) 95125bded7cSSeth Forshee return -1; 95225bded7cSSeth Forshee return __alps_command_mode_read_reg(psmouse, addr); 95325bded7cSSeth Forshee } 95425bded7cSSeth Forshee 95525bded7cSSeth Forshee static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value) 95625bded7cSSeth Forshee { 95725bded7cSSeth Forshee if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) 95825bded7cSSeth Forshee return -1; 95925bded7cSSeth Forshee if (alps_command_mode_send_nibble(psmouse, value & 0xf)) 96025bded7cSSeth Forshee return -1; 96125bded7cSSeth Forshee return 0; 96225bded7cSSeth Forshee } 96325bded7cSSeth Forshee 96425bded7cSSeth Forshee static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, 96525bded7cSSeth Forshee u8 value) 96625bded7cSSeth Forshee { 96725bded7cSSeth Forshee if (alps_command_mode_set_addr(psmouse, addr)) 96825bded7cSSeth Forshee return -1; 96925bded7cSSeth Forshee return __alps_command_mode_write_reg(psmouse, value); 97025bded7cSSeth Forshee } 97125bded7cSSeth Forshee 97225bded7cSSeth Forshee static int alps_enter_command_mode(struct psmouse *psmouse, 97325bded7cSSeth Forshee unsigned char *resp) 97425bded7cSSeth Forshee { 97525bded7cSSeth Forshee unsigned char param[4]; 97625bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 97725bded7cSSeth Forshee 97825bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || 97925bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || 98025bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || 98125bded7cSSeth Forshee ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 98225bded7cSSeth Forshee psmouse_err(psmouse, "failed to enter command mode\n"); 98325bded7cSSeth Forshee return -1; 98425bded7cSSeth Forshee } 98525bded7cSSeth Forshee 98625bded7cSSeth Forshee if (param[0] != 0x88 && param[1] != 0x07) { 98725bded7cSSeth Forshee psmouse_dbg(psmouse, 98825bded7cSSeth Forshee "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", 98925bded7cSSeth Forshee param[0], param[1], param[2]); 99025bded7cSSeth Forshee return -1; 99125bded7cSSeth Forshee } 99225bded7cSSeth Forshee 99325bded7cSSeth Forshee if (resp) 99425bded7cSSeth Forshee *resp = param[2]; 99525bded7cSSeth Forshee return 0; 99625bded7cSSeth Forshee } 99725bded7cSSeth Forshee 99825bded7cSSeth Forshee static inline int alps_exit_command_mode(struct psmouse *psmouse) 99925bded7cSSeth Forshee { 100025bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 100125bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) 100225bded7cSSeth Forshee return -1; 100325bded7cSSeth Forshee return 0; 100425bded7cSSeth Forshee } 100525bded7cSSeth Forshee 1006e38de678SHelge Deller static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) 10071da177e4SLinus Torvalds { 10081da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 1009e38de678SHelge Deller static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; 10101da177e4SLinus Torvalds unsigned char param[4]; 101125bded7cSSeth Forshee const struct alps_model_info *model = NULL; 10121da177e4SLinus Torvalds int i; 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds /* 10151da177e4SLinus Torvalds * First try "E6 report". 101699c90ab3SAkio Idehara * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. 101799c90ab3SAkio Idehara * The bits 0-2 of the first byte will be 1s if some buttons are 101899c90ab3SAkio Idehara * pressed. 10191da177e4SLinus Torvalds */ 10201da177e4SLinus Torvalds param[0] = 0; 10211da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || 10221da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 10231da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 10241da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) 10251da177e4SLinus Torvalds return NULL; 10261da177e4SLinus Torvalds 10271da177e4SLinus Torvalds param[0] = param[1] = param[2] = 0xff; 10281da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 10291da177e4SLinus Torvalds return NULL; 10301da177e4SLinus Torvalds 1031b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", 1032b5d21704SDmitry Torokhov param[0], param[1], param[2]); 10331da177e4SLinus Torvalds 103499c90ab3SAkio Idehara if ((param[0] & 0xf8) != 0 || param[1] != 0 || 103599c90ab3SAkio Idehara (param[2] != 10 && param[2] != 100)) 10361da177e4SLinus Torvalds return NULL; 10371da177e4SLinus Torvalds 10381da177e4SLinus Torvalds /* 10391da177e4SLinus Torvalds * Now try "E7 report". Allowed responses are in 10401da177e4SLinus Torvalds * alps_model_data[].signature 10411da177e4SLinus Torvalds */ 10421da177e4SLinus Torvalds param[0] = 0; 10431da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || 10441da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 10451da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 10461da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) 10471da177e4SLinus Torvalds return NULL; 10481da177e4SLinus Torvalds 10491da177e4SLinus Torvalds param[0] = param[1] = param[2] = 0xff; 10501da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 10511da177e4SLinus Torvalds return NULL; 10521da177e4SLinus Torvalds 1053b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", 1054b5d21704SDmitry Torokhov param[0], param[1], param[2]); 10551da177e4SLinus Torvalds 10561e0c5b12SDmitry Torokhov if (version) { 10571e0c5b12SDmitry Torokhov for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) 10581e0c5b12SDmitry Torokhov /* empty */; 10591da177e4SLinus Torvalds *version = (param[0] << 8) | (param[1] << 4) | i; 10601e0c5b12SDmitry Torokhov } 10611da177e4SLinus Torvalds 106225bded7cSSeth Forshee for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { 10631e0c5b12SDmitry Torokhov if (!memcmp(param, alps_model_data[i].signature, 106425bded7cSSeth Forshee sizeof(alps_model_data[i].signature))) { 106525bded7cSSeth Forshee model = alps_model_data + i; 106625bded7cSSeth Forshee break; 106725bded7cSSeth Forshee } 106825bded7cSSeth Forshee } 10691da177e4SLinus Torvalds 107025bded7cSSeth Forshee if (model && model->proto_version > ALPS_PROTO_V2) { 107125bded7cSSeth Forshee /* 107225bded7cSSeth Forshee * Need to check command mode response to identify 107325bded7cSSeth Forshee * model 107425bded7cSSeth Forshee */ 107525bded7cSSeth Forshee model = NULL; 107625bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, param)) { 107725bded7cSSeth Forshee psmouse_warn(psmouse, 107825bded7cSSeth Forshee "touchpad failed to enter command mode\n"); 107925bded7cSSeth Forshee } else { 108025bded7cSSeth Forshee for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { 108125bded7cSSeth Forshee if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && 108225bded7cSSeth Forshee alps_model_data[i].command_mode_resp == param[0]) { 108325bded7cSSeth Forshee model = alps_model_data + i; 108425bded7cSSeth Forshee break; 108525bded7cSSeth Forshee } 108625bded7cSSeth Forshee } 108725bded7cSSeth Forshee alps_exit_command_mode(psmouse); 108825bded7cSSeth Forshee 108925bded7cSSeth Forshee if (!model) 109025bded7cSSeth Forshee psmouse_dbg(psmouse, 109125bded7cSSeth Forshee "Unknown command mode response %2.2x\n", 109225bded7cSSeth Forshee param[0]); 109325bded7cSSeth Forshee } 109425bded7cSSeth Forshee } 109525bded7cSSeth Forshee 109625bded7cSSeth Forshee return model; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds /* 11001da177e4SLinus Torvalds * For DualPoint devices select the device that should respond to 11011da177e4SLinus Torvalds * subsequent commands. It looks like glidepad is behind stickpointer, 11021da177e4SLinus Torvalds * I'd thought it would be other way around... 11031da177e4SLinus Torvalds */ 110425bded7cSSeth Forshee static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable) 11051da177e4SLinus Torvalds { 11061da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11071da177e4SLinus Torvalds int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds if (ps2_command(ps2dev, NULL, cmd) || 11101da177e4SLinus Torvalds ps2_command(ps2dev, NULL, cmd) || 11111da177e4SLinus Torvalds ps2_command(ps2dev, NULL, cmd) || 11121da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) 11131da177e4SLinus Torvalds return -1; 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds /* we may get 3 more bytes, just ignore them */ 1116c611763dSDmitry Torokhov ps2_drain(ps2dev, 3, 100); 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds return 0; 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds 112125bded7cSSeth Forshee static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) 11221da177e4SLinus Torvalds { 11231da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds /* Try ALPS magic knock - 4 disable before enable */ 11261da177e4SLinus Torvalds if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11271da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11281da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11291da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11301da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) 11311da177e4SLinus Torvalds return -1; 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds /* 11341da177e4SLinus Torvalds * Switch mouse to poll (remote) mode so motion data will not 11351da177e4SLinus Torvalds * get in our way 11361da177e4SLinus Torvalds */ 11371da177e4SLinus Torvalds return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds 11401da177e4SLinus Torvalds static int alps_get_status(struct psmouse *psmouse, char *param) 11411da177e4SLinus Torvalds { 11421da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11431da177e4SLinus Torvalds 11441da177e4SLinus Torvalds /* Get status: 0xF5 0xF5 0xF5 0xE9 */ 11451da177e4SLinus Torvalds if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11461da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11471da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11481da177e4SLinus Torvalds ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 11491da177e4SLinus Torvalds return -1; 11501da177e4SLinus Torvalds 1151b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x", 1152b5d21704SDmitry Torokhov param[0], param[1], param[2]); 11531da177e4SLinus Torvalds 11541da177e4SLinus Torvalds return 0; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 11571da177e4SLinus Torvalds /* 11581da177e4SLinus Torvalds * Turn touchpad tapping on or off. The sequences are: 11591da177e4SLinus Torvalds * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, 11601da177e4SLinus Torvalds * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. 11611da177e4SLinus Torvalds * My guess that 0xE9 (GetInfo) is here as a sync point. 11621da177e4SLinus Torvalds * For models that also have stickpointer (DualPoints) its tapping 11631da177e4SLinus Torvalds * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but 11641da177e4SLinus Torvalds * we don't fiddle with it. 11651da177e4SLinus Torvalds */ 11661da177e4SLinus Torvalds static int alps_tap_mode(struct psmouse *psmouse, int enable) 11671da177e4SLinus Torvalds { 11681da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11691da177e4SLinus Torvalds int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES; 11701da177e4SLinus Torvalds unsigned char tap_arg = enable ? 0x0A : 0x00; 11711da177e4SLinus Torvalds unsigned char param[4]; 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) || 11741da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11751da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11761da177e4SLinus Torvalds ps2_command(ps2dev, &tap_arg, cmd)) 11771da177e4SLinus Torvalds return -1; 11781da177e4SLinus Torvalds 11791da177e4SLinus Torvalds if (alps_get_status(psmouse, param)) 11801da177e4SLinus Torvalds return -1; 11811da177e4SLinus Torvalds 11821da177e4SLinus Torvalds return 0; 11831da177e4SLinus Torvalds } 11841da177e4SLinus Torvalds 1185f0d5c6f4SDmitry Torokhov /* 1186f0d5c6f4SDmitry Torokhov * alps_poll() - poll the touchpad for current motion packet. 1187f0d5c6f4SDmitry Torokhov * Used in resync. 1188f0d5c6f4SDmitry Torokhov */ 1189f0d5c6f4SDmitry Torokhov static int alps_poll(struct psmouse *psmouse) 1190f0d5c6f4SDmitry Torokhov { 1191f0d5c6f4SDmitry Torokhov struct alps_data *priv = psmouse->private; 1192b46615feSSeth Forshee unsigned char buf[sizeof(psmouse->packet)]; 1193b7802c5cSDmitry Torokhov bool poll_failed; 1194f0d5c6f4SDmitry Torokhov 1195f0d5c6f4SDmitry Torokhov if (priv->i->flags & ALPS_PASS) 119625bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, true); 1197f0d5c6f4SDmitry Torokhov 1198f0d5c6f4SDmitry Torokhov poll_failed = ps2_command(&psmouse->ps2dev, buf, 1199f0d5c6f4SDmitry Torokhov PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; 1200f0d5c6f4SDmitry Torokhov 1201f0d5c6f4SDmitry Torokhov if (priv->i->flags & ALPS_PASS) 120225bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, false); 1203f0d5c6f4SDmitry Torokhov 1204f0d5c6f4SDmitry Torokhov if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) 1205f0d5c6f4SDmitry Torokhov return -1; 1206f0d5c6f4SDmitry Torokhov 1207f0d5c6f4SDmitry Torokhov if ((psmouse->badbyte & 0xc8) == 0x08) { 1208f0d5c6f4SDmitry Torokhov /* 1209f0d5c6f4SDmitry Torokhov * Poll the track stick ... 1210f0d5c6f4SDmitry Torokhov */ 1211f0d5c6f4SDmitry Torokhov if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8))) 1212f0d5c6f4SDmitry Torokhov return -1; 1213f0d5c6f4SDmitry Torokhov } 1214f0d5c6f4SDmitry Torokhov 1215f0d5c6f4SDmitry Torokhov memcpy(psmouse->packet, buf, sizeof(buf)); 1216f0d5c6f4SDmitry Torokhov return 0; 1217f0d5c6f4SDmitry Torokhov } 1218f0d5c6f4SDmitry Torokhov 121925bded7cSSeth Forshee static int alps_hw_init_v1_v2(struct psmouse *psmouse) 12201da177e4SLinus Torvalds { 12211da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 122271bb21b6SMaxim Levitsky const struct alps_model_info *model = priv->i; 12231da177e4SLinus Torvalds 122471bb21b6SMaxim Levitsky if ((model->flags & ALPS_PASS) && 122525bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, true)) { 12261da177e4SLinus Torvalds return -1; 1227b7802c5cSDmitry Torokhov } 12281da177e4SLinus Torvalds 1229b7802c5cSDmitry Torokhov if (alps_tap_mode(psmouse, true)) { 1230b5d21704SDmitry Torokhov psmouse_warn(psmouse, "Failed to enable hardware tapping\n"); 12311da177e4SLinus Torvalds return -1; 1232963f626dSPeter Osterlund } 12331da177e4SLinus Torvalds 123425bded7cSSeth Forshee if (alps_absolute_mode_v1_v2(psmouse)) { 1235b5d21704SDmitry Torokhov psmouse_err(psmouse, "Failed to enable absolute mode\n"); 12361da177e4SLinus Torvalds return -1; 12371da177e4SLinus Torvalds } 12381da177e4SLinus Torvalds 123971bb21b6SMaxim Levitsky if ((model->flags & ALPS_PASS) && 124025bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, false)) { 12411da177e4SLinus Torvalds return -1; 1242b7802c5cSDmitry Torokhov } 12431da177e4SLinus Torvalds 12441e0c5b12SDmitry Torokhov /* ALPS needs stream mode, otherwise it won't report any data */ 12451e0c5b12SDmitry Torokhov if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { 1246b5d21704SDmitry Torokhov psmouse_err(psmouse, "Failed to enable stream mode\n"); 12471e0c5b12SDmitry Torokhov return -1; 12481e0c5b12SDmitry Torokhov } 12491e0c5b12SDmitry Torokhov 12501e0c5b12SDmitry Torokhov return 0; 12511e0c5b12SDmitry Torokhov } 12521e0c5b12SDmitry Torokhov 125325bded7cSSeth Forshee /* 125425bded7cSSeth Forshee * Enable or disable passthrough mode to the trackstick. Must be in 125525bded7cSSeth Forshee * command mode when calling this function. 125625bded7cSSeth Forshee */ 125725bded7cSSeth Forshee static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) 125825bded7cSSeth Forshee { 125925bded7cSSeth Forshee int reg_val; 126025bded7cSSeth Forshee 126125bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0008); 126225bded7cSSeth Forshee if (reg_val == -1) 126325bded7cSSeth Forshee return -1; 126425bded7cSSeth Forshee 126525bded7cSSeth Forshee if (enable) 126625bded7cSSeth Forshee reg_val |= 0x01; 126725bded7cSSeth Forshee else 126825bded7cSSeth Forshee reg_val &= ~0x01; 126925bded7cSSeth Forshee 127025bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val)) 127125bded7cSSeth Forshee return -1; 127225bded7cSSeth Forshee 127325bded7cSSeth Forshee return 0; 127425bded7cSSeth Forshee } 127525bded7cSSeth Forshee 127625bded7cSSeth Forshee /* Must be in command mode when calling this function */ 127725bded7cSSeth Forshee static int alps_absolute_mode_v3(struct psmouse *psmouse) 127825bded7cSSeth Forshee { 127925bded7cSSeth Forshee int reg_val; 128025bded7cSSeth Forshee 128125bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0004); 128225bded7cSSeth Forshee if (reg_val == -1) 128325bded7cSSeth Forshee return -1; 128425bded7cSSeth Forshee 128525bded7cSSeth Forshee reg_val |= 0x06; 128625bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val)) 128725bded7cSSeth Forshee return -1; 128825bded7cSSeth Forshee 128925bded7cSSeth Forshee return 0; 129025bded7cSSeth Forshee } 129125bded7cSSeth Forshee 129225bded7cSSeth Forshee static int alps_hw_init_v3(struct psmouse *psmouse) 129325bded7cSSeth Forshee { 129425bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 129525bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 129625bded7cSSeth Forshee int reg_val; 129725bded7cSSeth Forshee unsigned char param[4]; 129825bded7cSSeth Forshee 129925bded7cSSeth Forshee priv->nibble_commands = alps_v3_nibble_commands; 130025bded7cSSeth Forshee priv->addr_command = PSMOUSE_CMD_RESET_WRAP; 130125bded7cSSeth Forshee 130225bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, NULL)) 130325bded7cSSeth Forshee goto error; 130425bded7cSSeth Forshee 130525bded7cSSeth Forshee /* Check for trackstick */ 130625bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0008); 130725bded7cSSeth Forshee if (reg_val == -1) 130825bded7cSSeth Forshee goto error; 130925bded7cSSeth Forshee if (reg_val & 0x80) { 131025bded7cSSeth Forshee if (alps_passthrough_mode_v3(psmouse, true)) 131125bded7cSSeth Forshee goto error; 131225bded7cSSeth Forshee if (alps_exit_command_mode(psmouse)) 131325bded7cSSeth Forshee goto error; 131425bded7cSSeth Forshee 131525bded7cSSeth Forshee /* 131625bded7cSSeth Forshee * E7 report for the trackstick 131725bded7cSSeth Forshee * 131825bded7cSSeth Forshee * There have been reports of failures to seem to trace back 131925bded7cSSeth Forshee * to the above trackstick check failing. When these occur 132025bded7cSSeth Forshee * this E7 report fails, so when that happens we continue 132125bded7cSSeth Forshee * with the assumption that there isn't a trackstick after 132225bded7cSSeth Forshee * all. 132325bded7cSSeth Forshee */ 132425bded7cSSeth Forshee param[0] = 0x64; 132525bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 132625bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 132725bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 132825bded7cSSeth Forshee ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 132925bded7cSSeth Forshee psmouse_warn(psmouse, "trackstick E7 report failed\n"); 133025bded7cSSeth Forshee } else { 133125bded7cSSeth Forshee psmouse_dbg(psmouse, 133225bded7cSSeth Forshee "trackstick E7 report: %2.2x %2.2x %2.2x\n", 133325bded7cSSeth Forshee param[0], param[1], param[2]); 133425bded7cSSeth Forshee 133525bded7cSSeth Forshee /* 133625bded7cSSeth Forshee * Not sure what this does, but it is absolutely 133725bded7cSSeth Forshee * essential. Without it, the touchpad does not 133825bded7cSSeth Forshee * work at all and the trackstick just emits normal 133925bded7cSSeth Forshee * PS/2 packets. 134025bded7cSSeth Forshee */ 134125bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 134225bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 134325bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 134425bded7cSSeth Forshee alps_command_mode_send_nibble(psmouse, 0x9) || 134525bded7cSSeth Forshee alps_command_mode_send_nibble(psmouse, 0x4)) { 134625bded7cSSeth Forshee psmouse_err(psmouse, 134725bded7cSSeth Forshee "Error sending magic E6 sequence\n"); 134825bded7cSSeth Forshee goto error_passthrough; 134925bded7cSSeth Forshee } 135025bded7cSSeth Forshee } 135125bded7cSSeth Forshee 135225bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, NULL)) 135325bded7cSSeth Forshee goto error_passthrough; 135425bded7cSSeth Forshee if (alps_passthrough_mode_v3(psmouse, false)) 135525bded7cSSeth Forshee goto error; 135625bded7cSSeth Forshee } 135725bded7cSSeth Forshee 135825bded7cSSeth Forshee if (alps_absolute_mode_v3(psmouse)) { 135925bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enter absolute mode\n"); 136025bded7cSSeth Forshee goto error; 136125bded7cSSeth Forshee } 136225bded7cSSeth Forshee 136325bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0006); 136425bded7cSSeth Forshee if (reg_val == -1) 136525bded7cSSeth Forshee goto error; 136625bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) 136725bded7cSSeth Forshee goto error; 136825bded7cSSeth Forshee 136925bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0007); 137025bded7cSSeth Forshee if (reg_val == -1) 137125bded7cSSeth Forshee goto error; 137225bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) 137325bded7cSSeth Forshee goto error; 137425bded7cSSeth Forshee 137525bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) 137625bded7cSSeth Forshee goto error; 137725bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, 0x04)) 137825bded7cSSeth Forshee goto error; 137925bded7cSSeth Forshee 138025bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) 138125bded7cSSeth Forshee goto error; 138225bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, 0x03)) 138325bded7cSSeth Forshee goto error; 138425bded7cSSeth Forshee 138525bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) 138625bded7cSSeth Forshee goto error; 138725bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) 138825bded7cSSeth Forshee goto error; 138925bded7cSSeth Forshee 139025bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) 139125bded7cSSeth Forshee goto error; 139225bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) 139325bded7cSSeth Forshee goto error; 139425bded7cSSeth Forshee 139525bded7cSSeth Forshee /* 139625bded7cSSeth Forshee * This ensures the trackstick packets are in the format 139725bded7cSSeth Forshee * supported by this driver. If bit 1 isn't set the packet 139825bded7cSSeth Forshee * format is different. 139925bded7cSSeth Forshee */ 140025bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) 140125bded7cSSeth Forshee goto error; 140225bded7cSSeth Forshee 140325bded7cSSeth Forshee alps_exit_command_mode(psmouse); 140425bded7cSSeth Forshee 140525bded7cSSeth Forshee /* Set rate and enable data reporting */ 140625bded7cSSeth Forshee param[0] = 0x64; 140725bded7cSSeth Forshee if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || 140825bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { 140925bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enable data reporting\n"); 141025bded7cSSeth Forshee return -1; 141125bded7cSSeth Forshee } 141225bded7cSSeth Forshee 141325bded7cSSeth Forshee return 0; 141425bded7cSSeth Forshee 141525bded7cSSeth Forshee error_passthrough: 141625bded7cSSeth Forshee /* Something failed while in passthrough mode, so try to get out */ 141725bded7cSSeth Forshee if (!alps_enter_command_mode(psmouse, NULL)) 141825bded7cSSeth Forshee alps_passthrough_mode_v3(psmouse, false); 141925bded7cSSeth Forshee error: 142025bded7cSSeth Forshee /* 142125bded7cSSeth Forshee * Leaving the touchpad in command mode will essentially render 142225bded7cSSeth Forshee * it unusable until the machine reboots, so exit it here just 142325bded7cSSeth Forshee * to be safe 142425bded7cSSeth Forshee */ 142525bded7cSSeth Forshee alps_exit_command_mode(psmouse); 142625bded7cSSeth Forshee return -1; 142725bded7cSSeth Forshee } 142825bded7cSSeth Forshee 142925bded7cSSeth Forshee /* Must be in command mode when calling this function */ 143025bded7cSSeth Forshee static int alps_absolute_mode_v4(struct psmouse *psmouse) 143125bded7cSSeth Forshee { 143225bded7cSSeth Forshee int reg_val; 143325bded7cSSeth Forshee 143425bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0004); 143525bded7cSSeth Forshee if (reg_val == -1) 143625bded7cSSeth Forshee return -1; 143725bded7cSSeth Forshee 143825bded7cSSeth Forshee reg_val |= 0x02; 143925bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val)) 144025bded7cSSeth Forshee return -1; 144125bded7cSSeth Forshee 144225bded7cSSeth Forshee return 0; 144325bded7cSSeth Forshee } 144425bded7cSSeth Forshee 144525bded7cSSeth Forshee static int alps_hw_init_v4(struct psmouse *psmouse) 144625bded7cSSeth Forshee { 144725bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 144825bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 144925bded7cSSeth Forshee unsigned char param[4]; 145025bded7cSSeth Forshee 145125bded7cSSeth Forshee priv->nibble_commands = alps_v4_nibble_commands; 145225bded7cSSeth Forshee priv->addr_command = PSMOUSE_CMD_DISABLE; 145325bded7cSSeth Forshee 145425bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, NULL)) 145525bded7cSSeth Forshee goto error; 145625bded7cSSeth Forshee 145725bded7cSSeth Forshee if (alps_absolute_mode_v4(psmouse)) { 145825bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enter absolute mode\n"); 145925bded7cSSeth Forshee goto error; 146025bded7cSSeth Forshee } 146125bded7cSSeth Forshee 146225bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c)) 146325bded7cSSeth Forshee goto error; 146425bded7cSSeth Forshee 146525bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03)) 146625bded7cSSeth Forshee goto error; 146725bded7cSSeth Forshee 146825bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03)) 146925bded7cSSeth Forshee goto error; 147025bded7cSSeth Forshee 147125bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15)) 147225bded7cSSeth Forshee goto error; 147325bded7cSSeth Forshee 147425bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01)) 147525bded7cSSeth Forshee goto error; 147625bded7cSSeth Forshee 147725bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03)) 147825bded7cSSeth Forshee goto error; 147925bded7cSSeth Forshee 148025bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03)) 148125bded7cSSeth Forshee goto error; 148225bded7cSSeth Forshee 148325bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03)) 148425bded7cSSeth Forshee goto error; 148525bded7cSSeth Forshee 148625bded7cSSeth Forshee alps_exit_command_mode(psmouse); 148725bded7cSSeth Forshee 148825bded7cSSeth Forshee /* 148925bded7cSSeth Forshee * This sequence changes the output from a 9-byte to an 149025bded7cSSeth Forshee * 8-byte format. All the same data seems to be present, 149125bded7cSSeth Forshee * just in a more compact format. 149225bded7cSSeth Forshee */ 149325bded7cSSeth Forshee param[0] = 0xc8; 149425bded7cSSeth Forshee param[1] = 0x64; 149525bded7cSSeth Forshee param[2] = 0x50; 149625bded7cSSeth Forshee if (ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || 149725bded7cSSeth Forshee ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE) || 149825bded7cSSeth Forshee ps2_command(ps2dev, ¶m[2], PSMOUSE_CMD_SETRATE) || 149925bded7cSSeth Forshee ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) 150025bded7cSSeth Forshee return -1; 150125bded7cSSeth Forshee 150225bded7cSSeth Forshee /* Set rate and enable data reporting */ 150325bded7cSSeth Forshee param[0] = 0x64; 150425bded7cSSeth Forshee if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || 150525bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { 150625bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enable data reporting\n"); 150725bded7cSSeth Forshee return -1; 150825bded7cSSeth Forshee } 150925bded7cSSeth Forshee 151025bded7cSSeth Forshee return 0; 151125bded7cSSeth Forshee 151225bded7cSSeth Forshee error: 151325bded7cSSeth Forshee /* 151425bded7cSSeth Forshee * Leaving the touchpad in command mode will essentially render 151525bded7cSSeth Forshee * it unusable until the machine reboots, so exit it here just 151625bded7cSSeth Forshee * to be safe 151725bded7cSSeth Forshee */ 151825bded7cSSeth Forshee alps_exit_command_mode(psmouse); 151925bded7cSSeth Forshee return -1; 152025bded7cSSeth Forshee } 152125bded7cSSeth Forshee 152225bded7cSSeth Forshee static int alps_hw_init(struct psmouse *psmouse) 152325bded7cSSeth Forshee { 152425bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 152525bded7cSSeth Forshee const struct alps_model_info *model = priv->i; 152625bded7cSSeth Forshee int ret = -1; 152725bded7cSSeth Forshee 152825bded7cSSeth Forshee switch (model->proto_version) { 152925bded7cSSeth Forshee case ALPS_PROTO_V1: 153025bded7cSSeth Forshee case ALPS_PROTO_V2: 153125bded7cSSeth Forshee ret = alps_hw_init_v1_v2(psmouse); 153225bded7cSSeth Forshee break; 153325bded7cSSeth Forshee case ALPS_PROTO_V3: 153425bded7cSSeth Forshee ret = alps_hw_init_v3(psmouse); 153525bded7cSSeth Forshee break; 153625bded7cSSeth Forshee case ALPS_PROTO_V4: 153725bded7cSSeth Forshee ret = alps_hw_init_v4(psmouse); 153825bded7cSSeth Forshee break; 153925bded7cSSeth Forshee } 154025bded7cSSeth Forshee 154125bded7cSSeth Forshee return ret; 154225bded7cSSeth Forshee } 154325bded7cSSeth Forshee 15441e0c5b12SDmitry Torokhov static int alps_reconnect(struct psmouse *psmouse) 15451e0c5b12SDmitry Torokhov { 154671bb21b6SMaxim Levitsky const struct alps_model_info *model; 154771bb21b6SMaxim Levitsky 15481e0c5b12SDmitry Torokhov psmouse_reset(psmouse); 15491e0c5b12SDmitry Torokhov 155071bb21b6SMaxim Levitsky model = alps_get_model(psmouse, NULL); 155171bb21b6SMaxim Levitsky if (!model) 15521e0c5b12SDmitry Torokhov return -1; 15531e0c5b12SDmitry Torokhov 155471bb21b6SMaxim Levitsky return alps_hw_init(psmouse); 15551da177e4SLinus Torvalds } 15561da177e4SLinus Torvalds 15571da177e4SLinus Torvalds static void alps_disconnect(struct psmouse *psmouse) 15581da177e4SLinus Torvalds { 15591da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 15602e5b636bSDmitry Torokhov 15611da177e4SLinus Torvalds psmouse_reset(psmouse); 15621d9f2626SSebastian Kapfer del_timer_sync(&priv->timer); 15632e5b636bSDmitry Torokhov input_unregister_device(priv->dev2); 15641da177e4SLinus Torvalds kfree(priv); 15651da177e4SLinus Torvalds } 15661da177e4SLinus Torvalds 15671da177e4SLinus Torvalds int alps_init(struct psmouse *psmouse) 15681da177e4SLinus Torvalds { 15691da177e4SLinus Torvalds struct alps_data *priv; 157071bb21b6SMaxim Levitsky const struct alps_model_info *model; 15712e5b636bSDmitry Torokhov struct input_dev *dev1 = psmouse->dev, *dev2; 15721da177e4SLinus Torvalds int version; 15731da177e4SLinus Torvalds 1574f42649e8SDmitry Torokhov priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); 15752e5b636bSDmitry Torokhov dev2 = input_allocate_device(); 15762e5b636bSDmitry Torokhov if (!priv || !dev2) 15771da177e4SLinus Torvalds goto init_fail; 15782e5b636bSDmitry Torokhov 15792e5b636bSDmitry Torokhov priv->dev2 = dev2; 15801d9f2626SSebastian Kapfer setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse); 15811d9f2626SSebastian Kapfer 15821e0c5b12SDmitry Torokhov psmouse->private = priv; 15831da177e4SLinus Torvalds 158425bded7cSSeth Forshee psmouse_reset(psmouse); 158525bded7cSSeth Forshee 158671bb21b6SMaxim Levitsky model = alps_get_model(psmouse, &version); 158771bb21b6SMaxim Levitsky if (!model) 158871bb21b6SMaxim Levitsky goto init_fail; 158971bb21b6SMaxim Levitsky 159071bb21b6SMaxim Levitsky priv->i = model; 159171bb21b6SMaxim Levitsky 159271bb21b6SMaxim Levitsky if (alps_hw_init(psmouse)) 15931da177e4SLinus Torvalds goto init_fail; 15941da177e4SLinus Torvalds 15957105d2eaSDmitry Torokhov /* 15967105d2eaSDmitry Torokhov * Undo part of setup done for us by psmouse core since touchpad 15977105d2eaSDmitry Torokhov * is not a relative device. 15987105d2eaSDmitry Torokhov */ 15997105d2eaSDmitry Torokhov __clear_bit(EV_REL, dev1->evbit); 16007105d2eaSDmitry Torokhov __clear_bit(REL_X, dev1->relbit); 16017105d2eaSDmitry Torokhov __clear_bit(REL_Y, dev1->relbit); 16027105d2eaSDmitry Torokhov 16037105d2eaSDmitry Torokhov /* 16047105d2eaSDmitry Torokhov * Now set up our capabilities. 16057105d2eaSDmitry Torokhov */ 16067b19ada2SJiri Slaby dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); 16077b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); 16087b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER); 160971bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_LEFT)] |= 161071bb21b6SMaxim Levitsky BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); 16111da177e4SLinus Torvalds 16127b19ada2SJiri Slaby dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); 161325bded7cSSeth Forshee 161425bded7cSSeth Forshee switch (model->proto_version) { 161525bded7cSSeth Forshee case ALPS_PROTO_V1: 161625bded7cSSeth Forshee case ALPS_PROTO_V2: 16172e5b636bSDmitry Torokhov input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); 16182e5b636bSDmitry Torokhov input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); 161925bded7cSSeth Forshee break; 162025bded7cSSeth Forshee case ALPS_PROTO_V3: 16213b7e09faSGeorge Pantalos case ALPS_PROTO_V4: 162201ce661fSSeth Forshee set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); 162301ce661fSSeth Forshee input_mt_init_slots(dev1, 2); 162401ce661fSSeth Forshee input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); 162501ce661fSSeth Forshee input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); 162601ce661fSSeth Forshee 162701ce661fSSeth Forshee set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); 162801ce661fSSeth Forshee set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); 162901ce661fSSeth Forshee set_bit(BTN_TOOL_QUADTAP, dev1->keybit); 16303b7e09faSGeorge Pantalos 163101ce661fSSeth Forshee input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); 163201ce661fSSeth Forshee input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); 163325bded7cSSeth Forshee break; 163425bded7cSSeth Forshee } 163525bded7cSSeth Forshee 16362e5b636bSDmitry Torokhov input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); 16371da177e4SLinus Torvalds 163871bb21b6SMaxim Levitsky if (model->flags & ALPS_WHEEL) { 16397b19ada2SJiri Slaby dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); 16407b19ada2SJiri Slaby dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); 16411da177e4SLinus Torvalds } 16421da177e4SLinus Torvalds 164371bb21b6SMaxim Levitsky if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { 16447b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); 16457b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); 16461da177e4SLinus Torvalds } 16471da177e4SLinus Torvalds 164871bb21b6SMaxim Levitsky if (model->flags & ALPS_FOUR_BUTTONS) { 164971bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); 165071bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); 165171bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); 165271bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); 165371bb21b6SMaxim Levitsky } else { 165471bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); 165571bb21b6SMaxim Levitsky } 165671bb21b6SMaxim Levitsky 165708ffce45SDmitry Torokhov snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); 16582e5b636bSDmitry Torokhov dev2->phys = priv->phys; 165971bb21b6SMaxim Levitsky dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; 16602e5b636bSDmitry Torokhov dev2->id.bustype = BUS_I8042; 16612e5b636bSDmitry Torokhov dev2->id.vendor = 0x0002; 16622e5b636bSDmitry Torokhov dev2->id.product = PSMOUSE_ALPS; 16632e5b636bSDmitry Torokhov dev2->id.version = 0x0000; 16641db3a345SDmitry Torokhov dev2->dev.parent = &psmouse->ps2dev.serio->dev; 16651da177e4SLinus Torvalds 16667b19ada2SJiri Slaby dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 166771bb21b6SMaxim Levitsky dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 166871bb21b6SMaxim Levitsky dev2->keybit[BIT_WORD(BTN_LEFT)] = 166971bb21b6SMaxim Levitsky BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); 16701da177e4SLinus Torvalds 1671f42649e8SDmitry Torokhov if (input_register_device(priv->dev2)) 1672f42649e8SDmitry Torokhov goto init_fail; 16731da177e4SLinus Torvalds 16741da177e4SLinus Torvalds psmouse->protocol_handler = alps_process_byte; 1675f0d5c6f4SDmitry Torokhov psmouse->poll = alps_poll; 16761da177e4SLinus Torvalds psmouse->disconnect = alps_disconnect; 16771da177e4SLinus Torvalds psmouse->reconnect = alps_reconnect; 167825bded7cSSeth Forshee psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; 16791da177e4SLinus Torvalds 1680f0d5c6f4SDmitry Torokhov /* We are having trouble resyncing ALPS touchpads so disable it for now */ 1681f0d5c6f4SDmitry Torokhov psmouse->resync_time = 0; 1682f0d5c6f4SDmitry Torokhov 16831da177e4SLinus Torvalds return 0; 16841da177e4SLinus Torvalds 16851da177e4SLinus Torvalds init_fail: 1686f42649e8SDmitry Torokhov psmouse_reset(psmouse); 16872e5b636bSDmitry Torokhov input_free_device(dev2); 16881da177e4SLinus Torvalds kfree(priv); 16891e0c5b12SDmitry Torokhov psmouse->private = NULL; 16901da177e4SLinus Torvalds return -1; 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds 1693b7802c5cSDmitry Torokhov int alps_detect(struct psmouse *psmouse, bool set_properties) 16941da177e4SLinus Torvalds { 16951da177e4SLinus Torvalds int version; 1696e38de678SHelge Deller const struct alps_model_info *model; 16971da177e4SLinus Torvalds 1698f42649e8SDmitry Torokhov model = alps_get_model(psmouse, &version); 1699f42649e8SDmitry Torokhov if (!model) 17001da177e4SLinus Torvalds return -1; 17011da177e4SLinus Torvalds 17021da177e4SLinus Torvalds if (set_properties) { 17031da177e4SLinus Torvalds psmouse->vendor = "ALPS"; 1704968ac842SDmitry Torokhov psmouse->name = model->flags & ALPS_DUALPOINT ? 1705968ac842SDmitry Torokhov "DualPoint TouchPad" : "GlidePoint"; 17061da177e4SLinus Torvalds psmouse->model = version; 17071da177e4SLinus Torvalds } 17081da177e4SLinus Torvalds return 0; 17091da177e4SLinus Torvalds } 17101da177e4SLinus Torvalds 1711