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 55601ce661fSSeth Forshee input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); 55701ce661fSSeth Forshee input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); 55801ce661fSSeth Forshee input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); 55901ce661fSSeth Forshee input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); 56001ce661fSSeth Forshee 56101ce661fSSeth Forshee input_report_key(dev, BTN_LEFT, left); 56201ce661fSSeth Forshee input_report_key(dev, BTN_RIGHT, right); 56301ce661fSSeth Forshee input_report_key(dev, BTN_MIDDLE, middle); 56401ce661fSSeth Forshee 56525bded7cSSeth Forshee if (z > 0) { 56625bded7cSSeth Forshee input_report_abs(dev, ABS_X, x); 56725bded7cSSeth Forshee input_report_abs(dev, ABS_Y, y); 56825bded7cSSeth Forshee } 56925bded7cSSeth Forshee input_report_abs(dev, ABS_PRESSURE, z); 57025bded7cSSeth Forshee 57125bded7cSSeth Forshee input_sync(dev); 57225bded7cSSeth Forshee 57325bded7cSSeth Forshee if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { 57425bded7cSSeth Forshee left = packet[3] & 0x10; 57525bded7cSSeth Forshee right = packet[3] & 0x20; 57625bded7cSSeth Forshee middle = packet[3] & 0x40; 57725bded7cSSeth Forshee 57825bded7cSSeth Forshee input_report_key(dev2, BTN_LEFT, left); 57925bded7cSSeth Forshee input_report_key(dev2, BTN_RIGHT, right); 58025bded7cSSeth Forshee input_report_key(dev2, BTN_MIDDLE, middle); 58125bded7cSSeth Forshee input_sync(dev2); 58225bded7cSSeth Forshee } 58325bded7cSSeth Forshee } 58425bded7cSSeth Forshee 58525bded7cSSeth Forshee static void alps_process_packet_v3(struct psmouse *psmouse) 58625bded7cSSeth Forshee { 58725bded7cSSeth Forshee unsigned char *packet = psmouse->packet; 58825bded7cSSeth Forshee 58925bded7cSSeth Forshee /* 59025bded7cSSeth Forshee * v3 protocol packets come in three types, two representing 59125bded7cSSeth Forshee * touchpad data and one representing trackstick data. 59225bded7cSSeth Forshee * Trackstick packets seem to be distinguished by always 59325bded7cSSeth Forshee * having 0x3f in the last byte. This value has never been 59425bded7cSSeth Forshee * observed in the last byte of either of the other types 59525bded7cSSeth Forshee * of packets. 59625bded7cSSeth Forshee */ 59725bded7cSSeth Forshee if (packet[5] == 0x3f) { 59825bded7cSSeth Forshee alps_process_trackstick_packet_v3(psmouse); 59925bded7cSSeth Forshee return; 60025bded7cSSeth Forshee } 60125bded7cSSeth Forshee 60225bded7cSSeth Forshee alps_process_touchpad_packet_v3(psmouse); 60325bded7cSSeth Forshee } 60425bded7cSSeth Forshee 60525bded7cSSeth Forshee static void alps_process_packet_v4(struct psmouse *psmouse) 60625bded7cSSeth Forshee { 6073b7e09faSGeorge Pantalos struct alps_data *priv = psmouse->private; 60825bded7cSSeth Forshee unsigned char *packet = psmouse->packet; 60925bded7cSSeth Forshee struct input_dev *dev = psmouse->dev; 6103b7e09faSGeorge Pantalos int offset; 61125bded7cSSeth Forshee int x, y, z; 61225bded7cSSeth Forshee int left, right; 6133b7e09faSGeorge Pantalos int x1, y1, x2, y2; 6143b7e09faSGeorge Pantalos int fingers = 0; 6153b7e09faSGeorge Pantalos unsigned int x_bitmap, y_bitmap; 6163b7e09faSGeorge Pantalos 6173b7e09faSGeorge Pantalos /* 6183b7e09faSGeorge Pantalos * v4 has a 6-byte encoding for bitmap data, but this data is 6193b7e09faSGeorge Pantalos * broken up between 3 normal packets. Use priv->multi_packet to 6203b7e09faSGeorge Pantalos * track our position in the bitmap packet. 6213b7e09faSGeorge Pantalos */ 6223b7e09faSGeorge Pantalos if (packet[6] & 0x40) { 6233b7e09faSGeorge Pantalos /* sync, reset position */ 6243b7e09faSGeorge Pantalos priv->multi_packet = 0; 6253b7e09faSGeorge Pantalos } 6263b7e09faSGeorge Pantalos 6273b7e09faSGeorge Pantalos if (WARN_ON_ONCE(priv->multi_packet > 2)) 6283b7e09faSGeorge Pantalos return; 6293b7e09faSGeorge Pantalos 6303b7e09faSGeorge Pantalos offset = 2 * priv->multi_packet; 6313b7e09faSGeorge Pantalos priv->multi_data[offset] = packet[6]; 6323b7e09faSGeorge Pantalos priv->multi_data[offset + 1] = packet[7]; 6333b7e09faSGeorge Pantalos 6343b7e09faSGeorge Pantalos if (++priv->multi_packet > 2) { 6353b7e09faSGeorge Pantalos priv->multi_packet = 0; 6363b7e09faSGeorge Pantalos 6373b7e09faSGeorge Pantalos x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) | 6383b7e09faSGeorge Pantalos ((priv->multi_data[3] & 0x60) << 3) | 6393b7e09faSGeorge Pantalos ((priv->multi_data[0] & 0x3f) << 2) | 6403b7e09faSGeorge Pantalos ((priv->multi_data[1] & 0x60) >> 5); 6413b7e09faSGeorge Pantalos y_bitmap = ((priv->multi_data[5] & 0x01) << 10) | 6423b7e09faSGeorge Pantalos ((priv->multi_data[3] & 0x1f) << 5) | 6433b7e09faSGeorge Pantalos (priv->multi_data[1] & 0x1f); 6443b7e09faSGeorge Pantalos 6453b7e09faSGeorge Pantalos fingers = alps_process_bitmap(x_bitmap, y_bitmap, 6463b7e09faSGeorge Pantalos &x1, &y1, &x2, &y2); 6473b7e09faSGeorge Pantalos 6483b7e09faSGeorge Pantalos /* Store MT data.*/ 6493b7e09faSGeorge Pantalos priv->fingers = fingers; 6503b7e09faSGeorge Pantalos priv->x1 = x1; 6513b7e09faSGeorge Pantalos priv->x2 = x2; 6523b7e09faSGeorge Pantalos priv->y1 = y1; 6533b7e09faSGeorge Pantalos priv->y2 = y2; 6543b7e09faSGeorge Pantalos } 65525bded7cSSeth Forshee 65625bded7cSSeth Forshee left = packet[4] & 0x01; 65725bded7cSSeth Forshee right = packet[4] & 0x02; 65825bded7cSSeth Forshee 65925bded7cSSeth Forshee x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) | 66025bded7cSSeth Forshee ((packet[0] & 0x30) >> 4); 66125bded7cSSeth Forshee y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f); 66225bded7cSSeth Forshee z = packet[5] & 0x7f; 66325bded7cSSeth Forshee 6643b7e09faSGeorge Pantalos /* 6653b7e09faSGeorge Pantalos * If there were no contacts in the bitmap, use ST 6663b7e09faSGeorge Pantalos * points in MT reports. 6673b7e09faSGeorge Pantalos * If there were two contacts or more, report MT data. 6683b7e09faSGeorge Pantalos */ 6693b7e09faSGeorge Pantalos if (priv->fingers < 2) { 6703b7e09faSGeorge Pantalos x1 = x; 6713b7e09faSGeorge Pantalos y1 = y; 6723b7e09faSGeorge Pantalos fingers = z > 0 ? 1 : 0; 6733b7e09faSGeorge Pantalos } else { 6743b7e09faSGeorge Pantalos fingers = priv->fingers; 6753b7e09faSGeorge Pantalos x1 = priv->x1; 6763b7e09faSGeorge Pantalos x2 = priv->x2; 6773b7e09faSGeorge Pantalos y1 = priv->y1; 6783b7e09faSGeorge Pantalos y2 = priv->y2; 6793b7e09faSGeorge Pantalos } 6803b7e09faSGeorge Pantalos 68125bded7cSSeth Forshee if (z >= 64) 68225bded7cSSeth Forshee input_report_key(dev, BTN_TOUCH, 1); 68325bded7cSSeth Forshee else 68425bded7cSSeth Forshee input_report_key(dev, BTN_TOUCH, 0); 68525bded7cSSeth Forshee 6863b7e09faSGeorge Pantalos alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2); 6873b7e09faSGeorge Pantalos 6883b7e09faSGeorge Pantalos input_report_key(dev, BTN_TOOL_FINGER, fingers == 1); 6893b7e09faSGeorge Pantalos input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2); 6903b7e09faSGeorge Pantalos input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); 6913b7e09faSGeorge Pantalos input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4); 6923b7e09faSGeorge Pantalos 6933b7e09faSGeorge Pantalos input_report_key(dev, BTN_LEFT, left); 6943b7e09faSGeorge Pantalos input_report_key(dev, BTN_RIGHT, right); 6953b7e09faSGeorge Pantalos 69625bded7cSSeth Forshee if (z > 0) { 69725bded7cSSeth Forshee input_report_abs(dev, ABS_X, x); 69825bded7cSSeth Forshee input_report_abs(dev, ABS_Y, y); 69925bded7cSSeth Forshee } 70025bded7cSSeth Forshee input_report_abs(dev, ABS_PRESSURE, z); 70125bded7cSSeth Forshee 70225bded7cSSeth Forshee input_sync(dev); 70325bded7cSSeth Forshee } 70425bded7cSSeth Forshee 70525bded7cSSeth Forshee static void alps_process_packet(struct psmouse *psmouse) 70625bded7cSSeth Forshee { 70725bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 70825bded7cSSeth Forshee const struct alps_model_info *model = priv->i; 70925bded7cSSeth Forshee 71025bded7cSSeth Forshee switch (model->proto_version) { 71125bded7cSSeth Forshee case ALPS_PROTO_V1: 71225bded7cSSeth Forshee case ALPS_PROTO_V2: 71325bded7cSSeth Forshee alps_process_packet_v1_v2(psmouse); 71425bded7cSSeth Forshee break; 71525bded7cSSeth Forshee case ALPS_PROTO_V3: 71625bded7cSSeth Forshee alps_process_packet_v3(psmouse); 71725bded7cSSeth Forshee break; 71825bded7cSSeth Forshee case ALPS_PROTO_V4: 71925bded7cSSeth Forshee alps_process_packet_v4(psmouse); 72025bded7cSSeth Forshee break; 72125bded7cSSeth Forshee } 72225bded7cSSeth Forshee } 72325bded7cSSeth Forshee 7241d9f2626SSebastian Kapfer static void alps_report_bare_ps2_packet(struct psmouse *psmouse, 7251d9f2626SSebastian Kapfer unsigned char packet[], 7261d9f2626SSebastian Kapfer bool report_buttons) 7271d9f2626SSebastian Kapfer { 7281d9f2626SSebastian Kapfer struct alps_data *priv = psmouse->private; 7291d9f2626SSebastian Kapfer struct input_dev *dev2 = priv->dev2; 7301d9f2626SSebastian Kapfer 7311d9f2626SSebastian Kapfer if (report_buttons) 7321d9f2626SSebastian Kapfer alps_report_buttons(psmouse, dev2, psmouse->dev, 7331d9f2626SSebastian Kapfer packet[0] & 1, packet[0] & 2, packet[0] & 4); 7341d9f2626SSebastian Kapfer 7351d9f2626SSebastian Kapfer input_report_rel(dev2, REL_X, 7361d9f2626SSebastian Kapfer packet[1] ? packet[1] - ((packet[0] << 4) & 0x100) : 0); 7371d9f2626SSebastian Kapfer input_report_rel(dev2, REL_Y, 7381d9f2626SSebastian Kapfer packet[2] ? ((packet[0] << 3) & 0x100) - packet[2] : 0); 7391d9f2626SSebastian Kapfer 7401d9f2626SSebastian Kapfer input_sync(dev2); 7411d9f2626SSebastian Kapfer } 7421d9f2626SSebastian Kapfer 7431d9f2626SSebastian Kapfer static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) 7441da177e4SLinus Torvalds { 7451da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 7461da177e4SLinus Torvalds 7471d9f2626SSebastian Kapfer if (psmouse->pktcnt < 6) 7481d9f2626SSebastian Kapfer return PSMOUSE_GOOD_DATA; 7491d9f2626SSebastian Kapfer 7501d9f2626SSebastian Kapfer if (psmouse->pktcnt == 6) { 7511d9f2626SSebastian Kapfer /* 7521d9f2626SSebastian Kapfer * Start a timer to flush the packet if it ends up last 7531d9f2626SSebastian Kapfer * 6-byte packet in the stream. Timer needs to fire 7541d9f2626SSebastian Kapfer * psmouse core times out itself. 20 ms should be enough 7551d9f2626SSebastian Kapfer * to decide if we are getting more data or not. 7561d9f2626SSebastian Kapfer */ 7571d9f2626SSebastian Kapfer mod_timer(&priv->timer, jiffies + msecs_to_jiffies(20)); 7581d9f2626SSebastian Kapfer return PSMOUSE_GOOD_DATA; 7591d9f2626SSebastian Kapfer } 7601d9f2626SSebastian Kapfer 7611d9f2626SSebastian Kapfer del_timer(&priv->timer); 7621d9f2626SSebastian Kapfer 7631d9f2626SSebastian Kapfer if (psmouse->packet[6] & 0x80) { 7641d9f2626SSebastian Kapfer 7651d9f2626SSebastian Kapfer /* 7661d9f2626SSebastian Kapfer * Highest bit is set - that means we either had 7671d9f2626SSebastian Kapfer * complete ALPS packet and this is start of the 7681d9f2626SSebastian Kapfer * next packet or we got garbage. 7691d9f2626SSebastian Kapfer */ 7701d9f2626SSebastian Kapfer 7711d9f2626SSebastian Kapfer if (((psmouse->packet[3] | 7721d9f2626SSebastian Kapfer psmouse->packet[4] | 7731d9f2626SSebastian Kapfer psmouse->packet[5]) & 0x80) || 7741d9f2626SSebastian Kapfer (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { 775b5d21704SDmitry Torokhov psmouse_dbg(psmouse, 776b5d21704SDmitry Torokhov "refusing packet %x %x %x %x (suspected interleaved ps/2)\n", 7771d9f2626SSebastian Kapfer psmouse->packet[3], psmouse->packet[4], 7781d9f2626SSebastian Kapfer psmouse->packet[5], psmouse->packet[6]); 7791d9f2626SSebastian Kapfer return PSMOUSE_BAD_DATA; 7801d9f2626SSebastian Kapfer } 7811d9f2626SSebastian Kapfer 7821d9f2626SSebastian Kapfer alps_process_packet(psmouse); 7831d9f2626SSebastian Kapfer 7841d9f2626SSebastian Kapfer /* Continue with the next packet */ 7851d9f2626SSebastian Kapfer psmouse->packet[0] = psmouse->packet[6]; 7861d9f2626SSebastian Kapfer psmouse->pktcnt = 1; 7871d9f2626SSebastian Kapfer 7881d9f2626SSebastian Kapfer } else { 7891d9f2626SSebastian Kapfer 7901d9f2626SSebastian Kapfer /* 7911d9f2626SSebastian Kapfer * High bit is 0 - that means that we indeed got a PS/2 7921d9f2626SSebastian Kapfer * packet in the middle of ALPS packet. 7931d9f2626SSebastian Kapfer * 7941d9f2626SSebastian Kapfer * There is also possibility that we got 6-byte ALPS 7951d9f2626SSebastian Kapfer * packet followed by 3-byte packet from trackpoint. We 7961d9f2626SSebastian Kapfer * can not distinguish between these 2 scenarios but 797b5d21704SDmitry Torokhov * because the latter is unlikely to happen in course of 7981d9f2626SSebastian Kapfer * normal operation (user would need to press all 7991d9f2626SSebastian Kapfer * buttons on the pad and start moving trackpoint 8001d9f2626SSebastian Kapfer * without touching the pad surface) we assume former. 8011d9f2626SSebastian Kapfer * Even if we are wrong the wost thing that would happen 8021d9f2626SSebastian Kapfer * the cursor would jump but we should not get protocol 803b5d21704SDmitry Torokhov * de-synchronization. 8041d9f2626SSebastian Kapfer */ 8051d9f2626SSebastian Kapfer 8061d9f2626SSebastian Kapfer alps_report_bare_ps2_packet(psmouse, &psmouse->packet[3], 8071d9f2626SSebastian Kapfer false); 8081d9f2626SSebastian Kapfer 8091d9f2626SSebastian Kapfer /* 8101d9f2626SSebastian Kapfer * Continue with the standard ALPS protocol handling, 8111d9f2626SSebastian Kapfer * but make sure we won't process it as an interleaved 8121d9f2626SSebastian Kapfer * packet again, which may happen if all buttons are 8131d9f2626SSebastian Kapfer * pressed. To avoid this let's reset the 4th bit which 8141d9f2626SSebastian Kapfer * is normally 1. 8151d9f2626SSebastian Kapfer */ 8161d9f2626SSebastian Kapfer psmouse->packet[3] = psmouse->packet[6] & 0xf7; 8171d9f2626SSebastian Kapfer psmouse->pktcnt = 4; 8181d9f2626SSebastian Kapfer } 8191d9f2626SSebastian Kapfer 8201d9f2626SSebastian Kapfer return PSMOUSE_GOOD_DATA; 8211d9f2626SSebastian Kapfer } 8221d9f2626SSebastian Kapfer 8231d9f2626SSebastian Kapfer static void alps_flush_packet(unsigned long data) 8241d9f2626SSebastian Kapfer { 8251d9f2626SSebastian Kapfer struct psmouse *psmouse = (struct psmouse *)data; 8261d9f2626SSebastian Kapfer 8271d9f2626SSebastian Kapfer serio_pause_rx(psmouse->ps2dev.serio); 8281d9f2626SSebastian Kapfer 829b46615feSSeth Forshee if (psmouse->pktcnt == psmouse->pktsize) { 8301d9f2626SSebastian Kapfer 8311d9f2626SSebastian Kapfer /* 8321d9f2626SSebastian Kapfer * We did not any more data in reasonable amount of time. 8331d9f2626SSebastian Kapfer * Validate the last 3 bytes and process as a standard 8341d9f2626SSebastian Kapfer * ALPS packet. 8351d9f2626SSebastian Kapfer */ 8361d9f2626SSebastian Kapfer if ((psmouse->packet[3] | 8371d9f2626SSebastian Kapfer psmouse->packet[4] | 8381d9f2626SSebastian Kapfer psmouse->packet[5]) & 0x80) { 839b5d21704SDmitry Torokhov psmouse_dbg(psmouse, 840b5d21704SDmitry Torokhov "refusing packet %x %x %x (suspected interleaved ps/2)\n", 8411d9f2626SSebastian Kapfer psmouse->packet[3], psmouse->packet[4], 8421d9f2626SSebastian Kapfer psmouse->packet[5]); 8431d9f2626SSebastian Kapfer } else { 8441d9f2626SSebastian Kapfer alps_process_packet(psmouse); 8451d9f2626SSebastian Kapfer } 8461d9f2626SSebastian Kapfer psmouse->pktcnt = 0; 8471d9f2626SSebastian Kapfer } 8481d9f2626SSebastian Kapfer 8491d9f2626SSebastian Kapfer serio_continue_rx(psmouse->ps2dev.serio); 8501d9f2626SSebastian Kapfer } 8511d9f2626SSebastian Kapfer 8521d9f2626SSebastian Kapfer static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) 8531d9f2626SSebastian Kapfer { 8541d9f2626SSebastian Kapfer struct alps_data *priv = psmouse->private; 8551d9f2626SSebastian Kapfer const struct alps_model_info *model = priv->i; 8561d9f2626SSebastian Kapfer 8571da177e4SLinus Torvalds if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ 8581da177e4SLinus Torvalds if (psmouse->pktcnt == 3) { 8591d9f2626SSebastian Kapfer alps_report_bare_ps2_packet(psmouse, psmouse->packet, 8601d9f2626SSebastian Kapfer true); 8611da177e4SLinus Torvalds return PSMOUSE_FULL_PACKET; 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds return PSMOUSE_GOOD_DATA; 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds 8661d9f2626SSebastian Kapfer /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ 8671d9f2626SSebastian Kapfer 8681d9f2626SSebastian Kapfer if ((model->flags & ALPS_PS2_INTERLEAVED) && 8691d9f2626SSebastian Kapfer psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) { 8701d9f2626SSebastian Kapfer return alps_handle_interleaved_ps2(psmouse); 8711d9f2626SSebastian Kapfer } 8721d9f2626SSebastian Kapfer 8731d9f2626SSebastian Kapfer if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { 874b5d21704SDmitry Torokhov psmouse_dbg(psmouse, 875b5d21704SDmitry Torokhov "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", 8761d9f2626SSebastian Kapfer psmouse->packet[0], model->mask0, model->byte0); 8771da177e4SLinus Torvalds return PSMOUSE_BAD_DATA; 8781d9f2626SSebastian Kapfer } 8791da177e4SLinus Torvalds 880b46615feSSeth Forshee /* Bytes 2 - pktsize should have 0 in the highest bit */ 881b46615feSSeth Forshee if (psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && 8821d9f2626SSebastian Kapfer (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { 883b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", 884b5d21704SDmitry Torokhov psmouse->pktcnt - 1, 885b5d21704SDmitry Torokhov psmouse->packet[psmouse->pktcnt - 1]); 8861da177e4SLinus Torvalds return PSMOUSE_BAD_DATA; 8871d9f2626SSebastian Kapfer } 8881da177e4SLinus Torvalds 889b46615feSSeth Forshee if (psmouse->pktcnt == psmouse->pktsize) { 8907d12e780SDavid Howells alps_process_packet(psmouse); 8911da177e4SLinus Torvalds return PSMOUSE_FULL_PACKET; 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds return PSMOUSE_GOOD_DATA; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds 89725bded7cSSeth Forshee static int alps_command_mode_send_nibble(struct psmouse *psmouse, int nibble) 89825bded7cSSeth Forshee { 89925bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 90025bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 90125bded7cSSeth Forshee int command; 90225bded7cSSeth Forshee unsigned char *param; 90325bded7cSSeth Forshee unsigned char dummy[4]; 90425bded7cSSeth Forshee 90525bded7cSSeth Forshee BUG_ON(nibble > 0xf); 90625bded7cSSeth Forshee 90725bded7cSSeth Forshee command = priv->nibble_commands[nibble].command; 90825bded7cSSeth Forshee param = (command & 0x0f00) ? 90925bded7cSSeth Forshee dummy : (unsigned char *)&priv->nibble_commands[nibble].data; 91025bded7cSSeth Forshee 91125bded7cSSeth Forshee if (ps2_command(ps2dev, param, command)) 91225bded7cSSeth Forshee return -1; 91325bded7cSSeth Forshee 91425bded7cSSeth Forshee return 0; 91525bded7cSSeth Forshee } 91625bded7cSSeth Forshee 91725bded7cSSeth Forshee static int alps_command_mode_set_addr(struct psmouse *psmouse, int addr) 91825bded7cSSeth Forshee { 91925bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 92025bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 92125bded7cSSeth Forshee int i, nibble; 92225bded7cSSeth Forshee 92325bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, priv->addr_command)) 92425bded7cSSeth Forshee return -1; 92525bded7cSSeth Forshee 92625bded7cSSeth Forshee for (i = 12; i >= 0; i -= 4) { 92725bded7cSSeth Forshee nibble = (addr >> i) & 0xf; 92825bded7cSSeth Forshee if (alps_command_mode_send_nibble(psmouse, nibble)) 92925bded7cSSeth Forshee return -1; 93025bded7cSSeth Forshee } 93125bded7cSSeth Forshee 93225bded7cSSeth Forshee return 0; 93325bded7cSSeth Forshee } 93425bded7cSSeth Forshee 93525bded7cSSeth Forshee static int __alps_command_mode_read_reg(struct psmouse *psmouse, int addr) 93625bded7cSSeth Forshee { 93725bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 93825bded7cSSeth Forshee unsigned char param[4]; 93925bded7cSSeth Forshee 94025bded7cSSeth Forshee if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 94125bded7cSSeth Forshee return -1; 94225bded7cSSeth Forshee 94325bded7cSSeth Forshee /* 94425bded7cSSeth Forshee * The address being read is returned in the first two bytes 94525bded7cSSeth Forshee * of the result. Check that this address matches the expected 94625bded7cSSeth Forshee * address. 94725bded7cSSeth Forshee */ 94825bded7cSSeth Forshee if (addr != ((param[0] << 8) | param[1])) 94925bded7cSSeth Forshee return -1; 95025bded7cSSeth Forshee 95125bded7cSSeth Forshee return param[2]; 95225bded7cSSeth Forshee } 95325bded7cSSeth Forshee 95425bded7cSSeth Forshee static int alps_command_mode_read_reg(struct psmouse *psmouse, int addr) 95525bded7cSSeth Forshee { 95625bded7cSSeth Forshee if (alps_command_mode_set_addr(psmouse, addr)) 95725bded7cSSeth Forshee return -1; 95825bded7cSSeth Forshee return __alps_command_mode_read_reg(psmouse, addr); 95925bded7cSSeth Forshee } 96025bded7cSSeth Forshee 96125bded7cSSeth Forshee static int __alps_command_mode_write_reg(struct psmouse *psmouse, u8 value) 96225bded7cSSeth Forshee { 96325bded7cSSeth Forshee if (alps_command_mode_send_nibble(psmouse, (value >> 4) & 0xf)) 96425bded7cSSeth Forshee return -1; 96525bded7cSSeth Forshee if (alps_command_mode_send_nibble(psmouse, value & 0xf)) 96625bded7cSSeth Forshee return -1; 96725bded7cSSeth Forshee return 0; 96825bded7cSSeth Forshee } 96925bded7cSSeth Forshee 97025bded7cSSeth Forshee static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, 97125bded7cSSeth Forshee u8 value) 97225bded7cSSeth Forshee { 97325bded7cSSeth Forshee if (alps_command_mode_set_addr(psmouse, addr)) 97425bded7cSSeth Forshee return -1; 97525bded7cSSeth Forshee return __alps_command_mode_write_reg(psmouse, value); 97625bded7cSSeth Forshee } 97725bded7cSSeth Forshee 97825bded7cSSeth Forshee static int alps_enter_command_mode(struct psmouse *psmouse, 97925bded7cSSeth Forshee unsigned char *resp) 98025bded7cSSeth Forshee { 98125bded7cSSeth Forshee unsigned char param[4]; 98225bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 98325bded7cSSeth Forshee 98425bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || 98525bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || 98625bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || 98725bded7cSSeth Forshee ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 98825bded7cSSeth Forshee psmouse_err(psmouse, "failed to enter command mode\n"); 98925bded7cSSeth Forshee return -1; 99025bded7cSSeth Forshee } 99125bded7cSSeth Forshee 99225bded7cSSeth Forshee if (param[0] != 0x88 && param[1] != 0x07) { 99325bded7cSSeth Forshee psmouse_dbg(psmouse, 99425bded7cSSeth Forshee "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", 99525bded7cSSeth Forshee param[0], param[1], param[2]); 99625bded7cSSeth Forshee return -1; 99725bded7cSSeth Forshee } 99825bded7cSSeth Forshee 99925bded7cSSeth Forshee if (resp) 100025bded7cSSeth Forshee *resp = param[2]; 100125bded7cSSeth Forshee return 0; 100225bded7cSSeth Forshee } 100325bded7cSSeth Forshee 100425bded7cSSeth Forshee static inline int alps_exit_command_mode(struct psmouse *psmouse) 100525bded7cSSeth Forshee { 100625bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 100725bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) 100825bded7cSSeth Forshee return -1; 100925bded7cSSeth Forshee return 0; 101025bded7cSSeth Forshee } 101125bded7cSSeth Forshee 1012e38de678SHelge Deller static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) 10131da177e4SLinus Torvalds { 10141da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 1015e38de678SHelge Deller static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; 10161da177e4SLinus Torvalds unsigned char param[4]; 101725bded7cSSeth Forshee const struct alps_model_info *model = NULL; 10181da177e4SLinus Torvalds int i; 10191da177e4SLinus Torvalds 10201da177e4SLinus Torvalds /* 10211da177e4SLinus Torvalds * First try "E6 report". 102299c90ab3SAkio Idehara * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. 102399c90ab3SAkio Idehara * The bits 0-2 of the first byte will be 1s if some buttons are 102499c90ab3SAkio Idehara * pressed. 10251da177e4SLinus Torvalds */ 10261da177e4SLinus Torvalds param[0] = 0; 10271da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || 10281da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 10291da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 10301da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) 10311da177e4SLinus Torvalds return NULL; 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds param[0] = param[1] = param[2] = 0xff; 10341da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 10351da177e4SLinus Torvalds return NULL; 10361da177e4SLinus Torvalds 1037b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", 1038b5d21704SDmitry Torokhov param[0], param[1], param[2]); 10391da177e4SLinus Torvalds 104099c90ab3SAkio Idehara if ((param[0] & 0xf8) != 0 || param[1] != 0 || 104199c90ab3SAkio Idehara (param[2] != 10 && param[2] != 100)) 10421da177e4SLinus Torvalds return NULL; 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds /* 10451da177e4SLinus Torvalds * Now try "E7 report". Allowed responses are in 10461da177e4SLinus Torvalds * alps_model_data[].signature 10471da177e4SLinus Torvalds */ 10481da177e4SLinus Torvalds param[0] = 0; 10491da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || 10501da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 10511da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 10521da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) 10531da177e4SLinus Torvalds return NULL; 10541da177e4SLinus Torvalds 10551da177e4SLinus Torvalds param[0] = param[1] = param[2] = 0xff; 10561da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 10571da177e4SLinus Torvalds return NULL; 10581da177e4SLinus Torvalds 1059b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", 1060b5d21704SDmitry Torokhov param[0], param[1], param[2]); 10611da177e4SLinus Torvalds 10621e0c5b12SDmitry Torokhov if (version) { 10631e0c5b12SDmitry Torokhov for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) 10641e0c5b12SDmitry Torokhov /* empty */; 10651da177e4SLinus Torvalds *version = (param[0] << 8) | (param[1] << 4) | i; 10661e0c5b12SDmitry Torokhov } 10671da177e4SLinus Torvalds 106825bded7cSSeth Forshee for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { 10691e0c5b12SDmitry Torokhov if (!memcmp(param, alps_model_data[i].signature, 107025bded7cSSeth Forshee sizeof(alps_model_data[i].signature))) { 107125bded7cSSeth Forshee model = alps_model_data + i; 107225bded7cSSeth Forshee break; 107325bded7cSSeth Forshee } 107425bded7cSSeth Forshee } 10751da177e4SLinus Torvalds 107625bded7cSSeth Forshee if (model && model->proto_version > ALPS_PROTO_V2) { 107725bded7cSSeth Forshee /* 107825bded7cSSeth Forshee * Need to check command mode response to identify 107925bded7cSSeth Forshee * model 108025bded7cSSeth Forshee */ 108125bded7cSSeth Forshee model = NULL; 108225bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, param)) { 108325bded7cSSeth Forshee psmouse_warn(psmouse, 108425bded7cSSeth Forshee "touchpad failed to enter command mode\n"); 108525bded7cSSeth Forshee } else { 108625bded7cSSeth Forshee for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { 108725bded7cSSeth Forshee if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && 108825bded7cSSeth Forshee alps_model_data[i].command_mode_resp == param[0]) { 108925bded7cSSeth Forshee model = alps_model_data + i; 109025bded7cSSeth Forshee break; 109125bded7cSSeth Forshee } 109225bded7cSSeth Forshee } 109325bded7cSSeth Forshee alps_exit_command_mode(psmouse); 109425bded7cSSeth Forshee 109525bded7cSSeth Forshee if (!model) 109625bded7cSSeth Forshee psmouse_dbg(psmouse, 109725bded7cSSeth Forshee "Unknown command mode response %2.2x\n", 109825bded7cSSeth Forshee param[0]); 109925bded7cSSeth Forshee } 110025bded7cSSeth Forshee } 110125bded7cSSeth Forshee 110225bded7cSSeth Forshee return model; 11031da177e4SLinus Torvalds } 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds /* 11061da177e4SLinus Torvalds * For DualPoint devices select the device that should respond to 11071da177e4SLinus Torvalds * subsequent commands. It looks like glidepad is behind stickpointer, 11081da177e4SLinus Torvalds * I'd thought it would be other way around... 11091da177e4SLinus Torvalds */ 111025bded7cSSeth Forshee static int alps_passthrough_mode_v2(struct psmouse *psmouse, bool enable) 11111da177e4SLinus Torvalds { 11121da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11131da177e4SLinus Torvalds int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11; 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds if (ps2_command(ps2dev, NULL, cmd) || 11161da177e4SLinus Torvalds ps2_command(ps2dev, NULL, cmd) || 11171da177e4SLinus Torvalds ps2_command(ps2dev, NULL, cmd) || 11181da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) 11191da177e4SLinus Torvalds return -1; 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds /* we may get 3 more bytes, just ignore them */ 1122c611763dSDmitry Torokhov ps2_drain(ps2dev, 3, 100); 11231da177e4SLinus Torvalds 11241da177e4SLinus Torvalds return 0; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds 112725bded7cSSeth Forshee static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) 11281da177e4SLinus Torvalds { 11291da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds /* Try ALPS magic knock - 4 disable before enable */ 11321da177e4SLinus Torvalds if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11331da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11341da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11351da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11361da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) 11371da177e4SLinus Torvalds return -1; 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds /* 11401da177e4SLinus Torvalds * Switch mouse to poll (remote) mode so motion data will not 11411da177e4SLinus Torvalds * get in our way 11421da177e4SLinus Torvalds */ 11431da177e4SLinus Torvalds return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds 11461da177e4SLinus Torvalds static int alps_get_status(struct psmouse *psmouse, char *param) 11471da177e4SLinus Torvalds { 11481da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds /* Get status: 0xF5 0xF5 0xF5 0xE9 */ 11511da177e4SLinus Torvalds if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11521da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11531da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11541da177e4SLinus Torvalds ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) 11551da177e4SLinus Torvalds return -1; 11561da177e4SLinus Torvalds 1157b5d21704SDmitry Torokhov psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x", 1158b5d21704SDmitry Torokhov param[0], param[1], param[2]); 11591da177e4SLinus Torvalds 11601da177e4SLinus Torvalds return 0; 11611da177e4SLinus Torvalds } 11621da177e4SLinus Torvalds 11631da177e4SLinus Torvalds /* 11641da177e4SLinus Torvalds * Turn touchpad tapping on or off. The sequences are: 11651da177e4SLinus Torvalds * 0xE9 0xF5 0xF5 0xF3 0x0A to enable, 11661da177e4SLinus Torvalds * 0xE9 0xF5 0xF5 0xE8 0x00 to disable. 11671da177e4SLinus Torvalds * My guess that 0xE9 (GetInfo) is here as a sync point. 11681da177e4SLinus Torvalds * For models that also have stickpointer (DualPoints) its tapping 11691da177e4SLinus Torvalds * is controlled separately (0xE6 0xE6 0xE6 0xF3 0x14|0x0A) but 11701da177e4SLinus Torvalds * we don't fiddle with it. 11711da177e4SLinus Torvalds */ 11721da177e4SLinus Torvalds static int alps_tap_mode(struct psmouse *psmouse, int enable) 11731da177e4SLinus Torvalds { 11741da177e4SLinus Torvalds struct ps2dev *ps2dev = &psmouse->ps2dev; 11751da177e4SLinus Torvalds int cmd = enable ? PSMOUSE_CMD_SETRATE : PSMOUSE_CMD_SETRES; 11761da177e4SLinus Torvalds unsigned char tap_arg = enable ? 0x0A : 0x00; 11771da177e4SLinus Torvalds unsigned char param[4]; 11781da177e4SLinus Torvalds 11791da177e4SLinus Torvalds if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) || 11801da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11811da177e4SLinus Torvalds ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || 11821da177e4SLinus Torvalds ps2_command(ps2dev, &tap_arg, cmd)) 11831da177e4SLinus Torvalds return -1; 11841da177e4SLinus Torvalds 11851da177e4SLinus Torvalds if (alps_get_status(psmouse, param)) 11861da177e4SLinus Torvalds return -1; 11871da177e4SLinus Torvalds 11881da177e4SLinus Torvalds return 0; 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds 1191f0d5c6f4SDmitry Torokhov /* 1192f0d5c6f4SDmitry Torokhov * alps_poll() - poll the touchpad for current motion packet. 1193f0d5c6f4SDmitry Torokhov * Used in resync. 1194f0d5c6f4SDmitry Torokhov */ 1195f0d5c6f4SDmitry Torokhov static int alps_poll(struct psmouse *psmouse) 1196f0d5c6f4SDmitry Torokhov { 1197f0d5c6f4SDmitry Torokhov struct alps_data *priv = psmouse->private; 1198b46615feSSeth Forshee unsigned char buf[sizeof(psmouse->packet)]; 1199b7802c5cSDmitry Torokhov bool poll_failed; 1200f0d5c6f4SDmitry Torokhov 1201f0d5c6f4SDmitry Torokhov if (priv->i->flags & ALPS_PASS) 120225bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, true); 1203f0d5c6f4SDmitry Torokhov 1204f0d5c6f4SDmitry Torokhov poll_failed = ps2_command(&psmouse->ps2dev, buf, 1205f0d5c6f4SDmitry Torokhov PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; 1206f0d5c6f4SDmitry Torokhov 1207f0d5c6f4SDmitry Torokhov if (priv->i->flags & ALPS_PASS) 120825bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, false); 1209f0d5c6f4SDmitry Torokhov 1210f0d5c6f4SDmitry Torokhov if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) 1211f0d5c6f4SDmitry Torokhov return -1; 1212f0d5c6f4SDmitry Torokhov 1213f0d5c6f4SDmitry Torokhov if ((psmouse->badbyte & 0xc8) == 0x08) { 1214f0d5c6f4SDmitry Torokhov /* 1215f0d5c6f4SDmitry Torokhov * Poll the track stick ... 1216f0d5c6f4SDmitry Torokhov */ 1217f0d5c6f4SDmitry Torokhov if (ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (3 << 8))) 1218f0d5c6f4SDmitry Torokhov return -1; 1219f0d5c6f4SDmitry Torokhov } 1220f0d5c6f4SDmitry Torokhov 1221f0d5c6f4SDmitry Torokhov memcpy(psmouse->packet, buf, sizeof(buf)); 1222f0d5c6f4SDmitry Torokhov return 0; 1223f0d5c6f4SDmitry Torokhov } 1224f0d5c6f4SDmitry Torokhov 122525bded7cSSeth Forshee static int alps_hw_init_v1_v2(struct psmouse *psmouse) 12261da177e4SLinus Torvalds { 12271da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 122871bb21b6SMaxim Levitsky const struct alps_model_info *model = priv->i; 12291da177e4SLinus Torvalds 123071bb21b6SMaxim Levitsky if ((model->flags & ALPS_PASS) && 123125bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, true)) { 12321da177e4SLinus Torvalds return -1; 1233b7802c5cSDmitry Torokhov } 12341da177e4SLinus Torvalds 1235b7802c5cSDmitry Torokhov if (alps_tap_mode(psmouse, true)) { 1236b5d21704SDmitry Torokhov psmouse_warn(psmouse, "Failed to enable hardware tapping\n"); 12371da177e4SLinus Torvalds return -1; 1238963f626dSPeter Osterlund } 12391da177e4SLinus Torvalds 124025bded7cSSeth Forshee if (alps_absolute_mode_v1_v2(psmouse)) { 1241b5d21704SDmitry Torokhov psmouse_err(psmouse, "Failed to enable absolute mode\n"); 12421da177e4SLinus Torvalds return -1; 12431da177e4SLinus Torvalds } 12441da177e4SLinus Torvalds 124571bb21b6SMaxim Levitsky if ((model->flags & ALPS_PASS) && 124625bded7cSSeth Forshee alps_passthrough_mode_v2(psmouse, false)) { 12471da177e4SLinus Torvalds return -1; 1248b7802c5cSDmitry Torokhov } 12491da177e4SLinus Torvalds 12501e0c5b12SDmitry Torokhov /* ALPS needs stream mode, otherwise it won't report any data */ 12511e0c5b12SDmitry Torokhov if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) { 1252b5d21704SDmitry Torokhov psmouse_err(psmouse, "Failed to enable stream mode\n"); 12531e0c5b12SDmitry Torokhov return -1; 12541e0c5b12SDmitry Torokhov } 12551e0c5b12SDmitry Torokhov 12561e0c5b12SDmitry Torokhov return 0; 12571e0c5b12SDmitry Torokhov } 12581e0c5b12SDmitry Torokhov 125925bded7cSSeth Forshee /* 126025bded7cSSeth Forshee * Enable or disable passthrough mode to the trackstick. Must be in 126125bded7cSSeth Forshee * command mode when calling this function. 126225bded7cSSeth Forshee */ 126325bded7cSSeth Forshee static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) 126425bded7cSSeth Forshee { 126525bded7cSSeth Forshee int reg_val; 126625bded7cSSeth Forshee 126725bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0008); 126825bded7cSSeth Forshee if (reg_val == -1) 126925bded7cSSeth Forshee return -1; 127025bded7cSSeth Forshee 127125bded7cSSeth Forshee if (enable) 127225bded7cSSeth Forshee reg_val |= 0x01; 127325bded7cSSeth Forshee else 127425bded7cSSeth Forshee reg_val &= ~0x01; 127525bded7cSSeth Forshee 127625bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val)) 127725bded7cSSeth Forshee return -1; 127825bded7cSSeth Forshee 127925bded7cSSeth Forshee return 0; 128025bded7cSSeth Forshee } 128125bded7cSSeth Forshee 128225bded7cSSeth Forshee /* Must be in command mode when calling this function */ 128325bded7cSSeth Forshee static int alps_absolute_mode_v3(struct psmouse *psmouse) 128425bded7cSSeth Forshee { 128525bded7cSSeth Forshee int reg_val; 128625bded7cSSeth Forshee 128725bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0004); 128825bded7cSSeth Forshee if (reg_val == -1) 128925bded7cSSeth Forshee return -1; 129025bded7cSSeth Forshee 129125bded7cSSeth Forshee reg_val |= 0x06; 129225bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val)) 129325bded7cSSeth Forshee return -1; 129425bded7cSSeth Forshee 129525bded7cSSeth Forshee return 0; 129625bded7cSSeth Forshee } 129725bded7cSSeth Forshee 129825bded7cSSeth Forshee static int alps_hw_init_v3(struct psmouse *psmouse) 129925bded7cSSeth Forshee { 130025bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 130125bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 130225bded7cSSeth Forshee int reg_val; 130325bded7cSSeth Forshee unsigned char param[4]; 130425bded7cSSeth Forshee 130525bded7cSSeth Forshee priv->nibble_commands = alps_v3_nibble_commands; 130625bded7cSSeth Forshee priv->addr_command = PSMOUSE_CMD_RESET_WRAP; 130725bded7cSSeth Forshee 130825bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, NULL)) 130925bded7cSSeth Forshee goto error; 131025bded7cSSeth Forshee 131125bded7cSSeth Forshee /* Check for trackstick */ 131225bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0008); 131325bded7cSSeth Forshee if (reg_val == -1) 131425bded7cSSeth Forshee goto error; 131525bded7cSSeth Forshee if (reg_val & 0x80) { 131625bded7cSSeth Forshee if (alps_passthrough_mode_v3(psmouse, true)) 131725bded7cSSeth Forshee goto error; 131825bded7cSSeth Forshee if (alps_exit_command_mode(psmouse)) 131925bded7cSSeth Forshee goto error; 132025bded7cSSeth Forshee 132125bded7cSSeth Forshee /* 132225bded7cSSeth Forshee * E7 report for the trackstick 132325bded7cSSeth Forshee * 132425bded7cSSeth Forshee * There have been reports of failures to seem to trace back 132525bded7cSSeth Forshee * to the above trackstick check failing. When these occur 132625bded7cSSeth Forshee * this E7 report fails, so when that happens we continue 132725bded7cSSeth Forshee * with the assumption that there isn't a trackstick after 132825bded7cSSeth Forshee * all. 132925bded7cSSeth Forshee */ 133025bded7cSSeth Forshee param[0] = 0x64; 133125bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 133225bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 133325bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || 133425bded7cSSeth Forshee ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { 133525bded7cSSeth Forshee psmouse_warn(psmouse, "trackstick E7 report failed\n"); 133625bded7cSSeth Forshee } else { 133725bded7cSSeth Forshee psmouse_dbg(psmouse, 133825bded7cSSeth Forshee "trackstick E7 report: %2.2x %2.2x %2.2x\n", 133925bded7cSSeth Forshee param[0], param[1], param[2]); 134025bded7cSSeth Forshee 134125bded7cSSeth Forshee /* 134225bded7cSSeth Forshee * Not sure what this does, but it is absolutely 134325bded7cSSeth Forshee * essential. Without it, the touchpad does not 134425bded7cSSeth Forshee * work at all and the trackstick just emits normal 134525bded7cSSeth Forshee * PS/2 packets. 134625bded7cSSeth Forshee */ 134725bded7cSSeth Forshee if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 134825bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 134925bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || 135025bded7cSSeth Forshee alps_command_mode_send_nibble(psmouse, 0x9) || 135125bded7cSSeth Forshee alps_command_mode_send_nibble(psmouse, 0x4)) { 135225bded7cSSeth Forshee psmouse_err(psmouse, 135325bded7cSSeth Forshee "Error sending magic E6 sequence\n"); 135425bded7cSSeth Forshee goto error_passthrough; 135525bded7cSSeth Forshee } 135625bded7cSSeth Forshee } 135725bded7cSSeth Forshee 135825bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, NULL)) 135925bded7cSSeth Forshee goto error_passthrough; 136025bded7cSSeth Forshee if (alps_passthrough_mode_v3(psmouse, false)) 136125bded7cSSeth Forshee goto error; 136225bded7cSSeth Forshee } 136325bded7cSSeth Forshee 136425bded7cSSeth Forshee if (alps_absolute_mode_v3(psmouse)) { 136525bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enter absolute mode\n"); 136625bded7cSSeth Forshee goto error; 136725bded7cSSeth Forshee } 136825bded7cSSeth Forshee 136925bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0006); 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 reg_val = alps_command_mode_read_reg(psmouse, 0x0007); 137625bded7cSSeth Forshee if (reg_val == -1) 137725bded7cSSeth Forshee goto error; 137825bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val | 0x01)) 137925bded7cSSeth Forshee goto error; 138025bded7cSSeth Forshee 138125bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0144) == -1) 138225bded7cSSeth Forshee goto error; 138325bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, 0x04)) 138425bded7cSSeth Forshee goto error; 138525bded7cSSeth Forshee 138625bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0159) == -1) 138725bded7cSSeth Forshee goto error; 138825bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, 0x03)) 138925bded7cSSeth Forshee goto error; 139025bded7cSSeth Forshee 139125bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0163) == -1) 139225bded7cSSeth Forshee goto error; 139325bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0163, 0x03)) 139425bded7cSSeth Forshee goto error; 139525bded7cSSeth Forshee 139625bded7cSSeth Forshee if (alps_command_mode_read_reg(psmouse, 0x0162) == -1) 139725bded7cSSeth Forshee goto error; 139825bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) 139925bded7cSSeth Forshee goto error; 140025bded7cSSeth Forshee 140125bded7cSSeth Forshee /* 140225bded7cSSeth Forshee * This ensures the trackstick packets are in the format 140325bded7cSSeth Forshee * supported by this driver. If bit 1 isn't set the packet 140425bded7cSSeth Forshee * format is different. 140525bded7cSSeth Forshee */ 140625bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) 140725bded7cSSeth Forshee goto error; 140825bded7cSSeth Forshee 140925bded7cSSeth Forshee alps_exit_command_mode(psmouse); 141025bded7cSSeth Forshee 141125bded7cSSeth Forshee /* Set rate and enable data reporting */ 141225bded7cSSeth Forshee param[0] = 0x64; 141325bded7cSSeth Forshee if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || 141425bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { 141525bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enable data reporting\n"); 141625bded7cSSeth Forshee return -1; 141725bded7cSSeth Forshee } 141825bded7cSSeth Forshee 141925bded7cSSeth Forshee return 0; 142025bded7cSSeth Forshee 142125bded7cSSeth Forshee error_passthrough: 142225bded7cSSeth Forshee /* Something failed while in passthrough mode, so try to get out */ 142325bded7cSSeth Forshee if (!alps_enter_command_mode(psmouse, NULL)) 142425bded7cSSeth Forshee alps_passthrough_mode_v3(psmouse, false); 142525bded7cSSeth Forshee error: 142625bded7cSSeth Forshee /* 142725bded7cSSeth Forshee * Leaving the touchpad in command mode will essentially render 142825bded7cSSeth Forshee * it unusable until the machine reboots, so exit it here just 142925bded7cSSeth Forshee * to be safe 143025bded7cSSeth Forshee */ 143125bded7cSSeth Forshee alps_exit_command_mode(psmouse); 143225bded7cSSeth Forshee return -1; 143325bded7cSSeth Forshee } 143425bded7cSSeth Forshee 143525bded7cSSeth Forshee /* Must be in command mode when calling this function */ 143625bded7cSSeth Forshee static int alps_absolute_mode_v4(struct psmouse *psmouse) 143725bded7cSSeth Forshee { 143825bded7cSSeth Forshee int reg_val; 143925bded7cSSeth Forshee 144025bded7cSSeth Forshee reg_val = alps_command_mode_read_reg(psmouse, 0x0004); 144125bded7cSSeth Forshee if (reg_val == -1) 144225bded7cSSeth Forshee return -1; 144325bded7cSSeth Forshee 144425bded7cSSeth Forshee reg_val |= 0x02; 144525bded7cSSeth Forshee if (__alps_command_mode_write_reg(psmouse, reg_val)) 144625bded7cSSeth Forshee return -1; 144725bded7cSSeth Forshee 144825bded7cSSeth Forshee return 0; 144925bded7cSSeth Forshee } 145025bded7cSSeth Forshee 145125bded7cSSeth Forshee static int alps_hw_init_v4(struct psmouse *psmouse) 145225bded7cSSeth Forshee { 145325bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 145425bded7cSSeth Forshee struct ps2dev *ps2dev = &psmouse->ps2dev; 145525bded7cSSeth Forshee unsigned char param[4]; 145625bded7cSSeth Forshee 145725bded7cSSeth Forshee priv->nibble_commands = alps_v4_nibble_commands; 145825bded7cSSeth Forshee priv->addr_command = PSMOUSE_CMD_DISABLE; 145925bded7cSSeth Forshee 146025bded7cSSeth Forshee if (alps_enter_command_mode(psmouse, NULL)) 146125bded7cSSeth Forshee goto error; 146225bded7cSSeth Forshee 146325bded7cSSeth Forshee if (alps_absolute_mode_v4(psmouse)) { 146425bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enter absolute mode\n"); 146525bded7cSSeth Forshee goto error; 146625bded7cSSeth Forshee } 146725bded7cSSeth Forshee 146825bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0007, 0x8c)) 146925bded7cSSeth Forshee goto error; 147025bded7cSSeth Forshee 147125bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0149, 0x03)) 147225bded7cSSeth Forshee goto error; 147325bded7cSSeth Forshee 147425bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0160, 0x03)) 147525bded7cSSeth Forshee goto error; 147625bded7cSSeth Forshee 147725bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x017f, 0x15)) 147825bded7cSSeth Forshee goto error; 147925bded7cSSeth Forshee 148025bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0151, 0x01)) 148125bded7cSSeth Forshee goto error; 148225bded7cSSeth Forshee 148325bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0168, 0x03)) 148425bded7cSSeth Forshee goto error; 148525bded7cSSeth Forshee 148625bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x014a, 0x03)) 148725bded7cSSeth Forshee goto error; 148825bded7cSSeth Forshee 148925bded7cSSeth Forshee if (alps_command_mode_write_reg(psmouse, 0x0161, 0x03)) 149025bded7cSSeth Forshee goto error; 149125bded7cSSeth Forshee 149225bded7cSSeth Forshee alps_exit_command_mode(psmouse); 149325bded7cSSeth Forshee 149425bded7cSSeth Forshee /* 149525bded7cSSeth Forshee * This sequence changes the output from a 9-byte to an 149625bded7cSSeth Forshee * 8-byte format. All the same data seems to be present, 149725bded7cSSeth Forshee * just in a more compact format. 149825bded7cSSeth Forshee */ 149925bded7cSSeth Forshee param[0] = 0xc8; 150025bded7cSSeth Forshee param[1] = 0x64; 150125bded7cSSeth Forshee param[2] = 0x50; 150225bded7cSSeth Forshee if (ps2_command(ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || 150325bded7cSSeth Forshee ps2_command(ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE) || 150425bded7cSSeth Forshee ps2_command(ps2dev, ¶m[2], PSMOUSE_CMD_SETRATE) || 150525bded7cSSeth Forshee ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) 150625bded7cSSeth Forshee return -1; 150725bded7cSSeth Forshee 150825bded7cSSeth Forshee /* Set rate and enable data reporting */ 150925bded7cSSeth Forshee param[0] = 0x64; 151025bded7cSSeth Forshee if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE) || 151125bded7cSSeth Forshee ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE)) { 151225bded7cSSeth Forshee psmouse_err(psmouse, "Failed to enable data reporting\n"); 151325bded7cSSeth Forshee return -1; 151425bded7cSSeth Forshee } 151525bded7cSSeth Forshee 151625bded7cSSeth Forshee return 0; 151725bded7cSSeth Forshee 151825bded7cSSeth Forshee error: 151925bded7cSSeth Forshee /* 152025bded7cSSeth Forshee * Leaving the touchpad in command mode will essentially render 152125bded7cSSeth Forshee * it unusable until the machine reboots, so exit it here just 152225bded7cSSeth Forshee * to be safe 152325bded7cSSeth Forshee */ 152425bded7cSSeth Forshee alps_exit_command_mode(psmouse); 152525bded7cSSeth Forshee return -1; 152625bded7cSSeth Forshee } 152725bded7cSSeth Forshee 152825bded7cSSeth Forshee static int alps_hw_init(struct psmouse *psmouse) 152925bded7cSSeth Forshee { 153025bded7cSSeth Forshee struct alps_data *priv = psmouse->private; 153125bded7cSSeth Forshee const struct alps_model_info *model = priv->i; 153225bded7cSSeth Forshee int ret = -1; 153325bded7cSSeth Forshee 153425bded7cSSeth Forshee switch (model->proto_version) { 153525bded7cSSeth Forshee case ALPS_PROTO_V1: 153625bded7cSSeth Forshee case ALPS_PROTO_V2: 153725bded7cSSeth Forshee ret = alps_hw_init_v1_v2(psmouse); 153825bded7cSSeth Forshee break; 153925bded7cSSeth Forshee case ALPS_PROTO_V3: 154025bded7cSSeth Forshee ret = alps_hw_init_v3(psmouse); 154125bded7cSSeth Forshee break; 154225bded7cSSeth Forshee case ALPS_PROTO_V4: 154325bded7cSSeth Forshee ret = alps_hw_init_v4(psmouse); 154425bded7cSSeth Forshee break; 154525bded7cSSeth Forshee } 154625bded7cSSeth Forshee 154725bded7cSSeth Forshee return ret; 154825bded7cSSeth Forshee } 154925bded7cSSeth Forshee 15501e0c5b12SDmitry Torokhov static int alps_reconnect(struct psmouse *psmouse) 15511e0c5b12SDmitry Torokhov { 155271bb21b6SMaxim Levitsky const struct alps_model_info *model; 155371bb21b6SMaxim Levitsky 15541e0c5b12SDmitry Torokhov psmouse_reset(psmouse); 15551e0c5b12SDmitry Torokhov 155671bb21b6SMaxim Levitsky model = alps_get_model(psmouse, NULL); 155771bb21b6SMaxim Levitsky if (!model) 15581e0c5b12SDmitry Torokhov return -1; 15591e0c5b12SDmitry Torokhov 156071bb21b6SMaxim Levitsky return alps_hw_init(psmouse); 15611da177e4SLinus Torvalds } 15621da177e4SLinus Torvalds 15631da177e4SLinus Torvalds static void alps_disconnect(struct psmouse *psmouse) 15641da177e4SLinus Torvalds { 15651da177e4SLinus Torvalds struct alps_data *priv = psmouse->private; 15662e5b636bSDmitry Torokhov 15671da177e4SLinus Torvalds psmouse_reset(psmouse); 15681d9f2626SSebastian Kapfer del_timer_sync(&priv->timer); 15692e5b636bSDmitry Torokhov input_unregister_device(priv->dev2); 15701da177e4SLinus Torvalds kfree(priv); 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds 15731da177e4SLinus Torvalds int alps_init(struct psmouse *psmouse) 15741da177e4SLinus Torvalds { 15751da177e4SLinus Torvalds struct alps_data *priv; 157671bb21b6SMaxim Levitsky const struct alps_model_info *model; 15772e5b636bSDmitry Torokhov struct input_dev *dev1 = psmouse->dev, *dev2; 15781da177e4SLinus Torvalds int version; 15791da177e4SLinus Torvalds 1580f42649e8SDmitry Torokhov priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); 15812e5b636bSDmitry Torokhov dev2 = input_allocate_device(); 15822e5b636bSDmitry Torokhov if (!priv || !dev2) 15831da177e4SLinus Torvalds goto init_fail; 15842e5b636bSDmitry Torokhov 15852e5b636bSDmitry Torokhov priv->dev2 = dev2; 15861d9f2626SSebastian Kapfer setup_timer(&priv->timer, alps_flush_packet, (unsigned long)psmouse); 15871d9f2626SSebastian Kapfer 15881e0c5b12SDmitry Torokhov psmouse->private = priv; 15891da177e4SLinus Torvalds 159025bded7cSSeth Forshee psmouse_reset(psmouse); 159125bded7cSSeth Forshee 159271bb21b6SMaxim Levitsky model = alps_get_model(psmouse, &version); 159371bb21b6SMaxim Levitsky if (!model) 159471bb21b6SMaxim Levitsky goto init_fail; 159571bb21b6SMaxim Levitsky 159671bb21b6SMaxim Levitsky priv->i = model; 159771bb21b6SMaxim Levitsky 159871bb21b6SMaxim Levitsky if (alps_hw_init(psmouse)) 15991da177e4SLinus Torvalds goto init_fail; 16001da177e4SLinus Torvalds 16017105d2eaSDmitry Torokhov /* 16027105d2eaSDmitry Torokhov * Undo part of setup done for us by psmouse core since touchpad 16037105d2eaSDmitry Torokhov * is not a relative device. 16047105d2eaSDmitry Torokhov */ 16057105d2eaSDmitry Torokhov __clear_bit(EV_REL, dev1->evbit); 16067105d2eaSDmitry Torokhov __clear_bit(REL_X, dev1->relbit); 16077105d2eaSDmitry Torokhov __clear_bit(REL_Y, dev1->relbit); 16087105d2eaSDmitry Torokhov 16097105d2eaSDmitry Torokhov /* 16107105d2eaSDmitry Torokhov * Now set up our capabilities. 16117105d2eaSDmitry Torokhov */ 16127b19ada2SJiri Slaby dev1->evbit[BIT_WORD(EV_KEY)] |= BIT_MASK(EV_KEY); 16137b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH); 16147b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_TOOL_FINGER)] |= BIT_MASK(BTN_TOOL_FINGER); 161571bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_LEFT)] |= 161671bb21b6SMaxim Levitsky BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT); 16171da177e4SLinus Torvalds 16187b19ada2SJiri Slaby dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); 161925bded7cSSeth Forshee 162025bded7cSSeth Forshee switch (model->proto_version) { 162125bded7cSSeth Forshee case ALPS_PROTO_V1: 162225bded7cSSeth Forshee case ALPS_PROTO_V2: 16232e5b636bSDmitry Torokhov input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); 16242e5b636bSDmitry Torokhov input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); 162525bded7cSSeth Forshee break; 162625bded7cSSeth Forshee case ALPS_PROTO_V3: 16273b7e09faSGeorge Pantalos case ALPS_PROTO_V4: 162801ce661fSSeth Forshee set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); 162901ce661fSSeth Forshee input_mt_init_slots(dev1, 2); 163001ce661fSSeth Forshee input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); 163101ce661fSSeth Forshee input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); 163201ce661fSSeth Forshee 163301ce661fSSeth Forshee set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); 163401ce661fSSeth Forshee set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); 163501ce661fSSeth Forshee set_bit(BTN_TOOL_QUADTAP, dev1->keybit); 16363b7e09faSGeorge Pantalos 163701ce661fSSeth Forshee input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); 163801ce661fSSeth Forshee input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); 163925bded7cSSeth Forshee break; 164025bded7cSSeth Forshee } 164125bded7cSSeth Forshee 16422e5b636bSDmitry Torokhov input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); 16431da177e4SLinus Torvalds 164471bb21b6SMaxim Levitsky if (model->flags & ALPS_WHEEL) { 16457b19ada2SJiri Slaby dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); 16467b19ada2SJiri Slaby dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds 164971bb21b6SMaxim Levitsky if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { 16507b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); 16517b19ada2SJiri Slaby dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); 16521da177e4SLinus Torvalds } 16531da177e4SLinus Torvalds 165471bb21b6SMaxim Levitsky if (model->flags & ALPS_FOUR_BUTTONS) { 165571bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); 165671bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); 165771bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); 165871bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); 165971bb21b6SMaxim Levitsky } else { 166071bb21b6SMaxim Levitsky dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); 166171bb21b6SMaxim Levitsky } 166271bb21b6SMaxim Levitsky 166308ffce45SDmitry Torokhov snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); 16642e5b636bSDmitry Torokhov dev2->phys = priv->phys; 166571bb21b6SMaxim Levitsky dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; 16662e5b636bSDmitry Torokhov dev2->id.bustype = BUS_I8042; 16672e5b636bSDmitry Torokhov dev2->id.vendor = 0x0002; 16682e5b636bSDmitry Torokhov dev2->id.product = PSMOUSE_ALPS; 16692e5b636bSDmitry Torokhov dev2->id.version = 0x0000; 16701db3a345SDmitry Torokhov dev2->dev.parent = &psmouse->ps2dev.serio->dev; 16711da177e4SLinus Torvalds 16727b19ada2SJiri Slaby dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); 167371bb21b6SMaxim Levitsky dev2->relbit[BIT_WORD(REL_X)] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); 167471bb21b6SMaxim Levitsky dev2->keybit[BIT_WORD(BTN_LEFT)] = 167571bb21b6SMaxim Levitsky BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); 16761da177e4SLinus Torvalds 1677f42649e8SDmitry Torokhov if (input_register_device(priv->dev2)) 1678f42649e8SDmitry Torokhov goto init_fail; 16791da177e4SLinus Torvalds 16801da177e4SLinus Torvalds psmouse->protocol_handler = alps_process_byte; 1681f0d5c6f4SDmitry Torokhov psmouse->poll = alps_poll; 16821da177e4SLinus Torvalds psmouse->disconnect = alps_disconnect; 16831da177e4SLinus Torvalds psmouse->reconnect = alps_reconnect; 168425bded7cSSeth Forshee psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; 16851da177e4SLinus Torvalds 1686f0d5c6f4SDmitry Torokhov /* We are having trouble resyncing ALPS touchpads so disable it for now */ 1687f0d5c6f4SDmitry Torokhov psmouse->resync_time = 0; 1688f0d5c6f4SDmitry Torokhov 16891da177e4SLinus Torvalds return 0; 16901da177e4SLinus Torvalds 16911da177e4SLinus Torvalds init_fail: 1692f42649e8SDmitry Torokhov psmouse_reset(psmouse); 16932e5b636bSDmitry Torokhov input_free_device(dev2); 16941da177e4SLinus Torvalds kfree(priv); 16951e0c5b12SDmitry Torokhov psmouse->private = NULL; 16961da177e4SLinus Torvalds return -1; 16971da177e4SLinus Torvalds } 16981da177e4SLinus Torvalds 1699b7802c5cSDmitry Torokhov int alps_detect(struct psmouse *psmouse, bool set_properties) 17001da177e4SLinus Torvalds { 17011da177e4SLinus Torvalds int version; 1702e38de678SHelge Deller const struct alps_model_info *model; 17031da177e4SLinus Torvalds 1704f42649e8SDmitry Torokhov model = alps_get_model(psmouse, &version); 1705f42649e8SDmitry Torokhov if (!model) 17061da177e4SLinus Torvalds return -1; 17071da177e4SLinus Torvalds 17081da177e4SLinus Torvalds if (set_properties) { 17091da177e4SLinus Torvalds psmouse->vendor = "ALPS"; 1710968ac842SDmitry Torokhov psmouse->name = model->flags & ALPS_DUALPOINT ? 1711968ac842SDmitry Torokhov "DualPoint TouchPad" : "GlidePoint"; 17121da177e4SLinus Torvalds psmouse->model = version; 17131da177e4SLinus Torvalds } 17141da177e4SLinus Torvalds return 0; 17151da177e4SLinus Torvalds } 17161da177e4SLinus Torvalds 1717