Hello, by the E7 and EC identification, that's what I have on Toshiba Portege Z30-A-12N, too (E7 = 0x73, 0x03, 0x0a, EC = 0x88, 0xb3, 0x22). I was in process of creation of my own driver; you've overtaken me... :-) Thanks, Regards, vencik ______________________________________________________________ > Od: Qiting Chen <elaineee66@xxxxxxxxx> > Komu: <dmitry.torokhov@xxxxxxxxx>, <cernekee@xxxxxxxxx>, <dturvene@xxxxxxxxxxxx> > Datum: 19.03.2014 09:57 > Předmět: [PATCH] input: add support for ALPS v7 protocol device > > CC: linux-input@xxxxxxxxxxxxxxx, ndevos@xxxxxxxxxx, jclift@xxxxxxxxxx--cc, "Qiting Chen" <qiting.chen@xxxxxxxxxxx> >Here is a patch of supporting ALPS v7 protocol device. >ALPS v7 protocol device is a clickpad that is currently used on >Lenovo S430/S435/S530, Lenovo Z410/Z510, HP Odie, HP Revolve 810 G1, >as well as other machines with ALPS Touchpad of following infomation: > Device ID = 0x73, 0x03, 0x0a > Firmware ID = 0x88, 0xb*, 0x** > >A v7 protocol support patch is first relesed 2 months ago: >http://www.spinics.net/lists/linux-input/msg29084.html >After that some feedbacks were received from end user. Now this patch fixed the bugs >reported by them: >1) Fix cursor jump when doing a right click drag >2) Fix cursor jitter when button clicking > >Signed-off-by: Qiting Chen <qiting.chen@xxxxxxxxxxx> >--- > drivers/input/mouse/alps.c | 560 ++++++++++++++++++++++++++++++++++++++++++--- > drivers/input/mouse/alps.h | 132 +++++++++-- > 2 files changed, 641 insertions(+), 51 deletions(-) > >diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c >index fb15c64..383281f 100644 >--- a/drivers/input/mouse/alps.c >+++ b/drivers/input/mouse/alps.c >@@ -32,6 +32,13 @@ > #define ALPS_REG_BASE_RUSHMORE 0xc2c0 > #define ALPS_REG_BASE_PINNACLE 0x0000 > >+#define LEFT_BUTTON_BIT 0x01 >+#define RIGHT_BUTTON_BIT 0x02 >+ >+#define V7_LARGE_MOVEMENT 130 >+#define V7_DEAD_ZONE_OFFSET_X 72 >+#define V7_DEAD_ZONE_OFFSET_Y 72 >+ > static const struct alps_nibble_commands alps_v3_nibble_commands[] = { > { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ > { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ >@@ -99,6 +106,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { > #define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ > #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with > 6-byte ALPS packet */ >+#define ALPS_BTNLESS 0x100 /* ALPS ClickPad flag */ > > static const struct alps_model_info alps_model_data[] = { > { { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */ >@@ -140,6 +148,20 @@ static void alps_set_abs_params_mt(struct alps_data *priv, > * isn't valid per PS/2 spec. > */ > >+static unsigned int alps_pt_distance(struct alps_abs_data *pt0, >+ struct alps_abs_data *pt1) >+{ >+ int vect_x, vect_y; >+ >+ if (!pt0 || !pt1) >+ return 0; >+ >+ vect_x = pt0->x - pt1->x; >+ vect_y = pt0->y - pt1->y; >+ >+ return int_sqrt(vect_x * vect_x + vect_y * vect_y); >+} >+ > /* Packet formats are described in Documentation/input/alps.txt */ > > static bool alps_is_valid_first_byte(struct alps_data *priv, >@@ -320,8 +342,8 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv, > end_bit = y_msb - 1; > box_middle_y = (priv->y_max * (start_bit + end_bit)) / > (2 * (priv->y_bits - 1)); >- *x1 = fields->x; >- *y1 = fields->y; >+ *x1 = fields->pt.x; >+ *y1 = fields->pt.y; > *x2 = 2 * box_middle_x - *x1; > *y2 = 2 * box_middle_y - *y1; > } >@@ -461,6 +483,38 @@ static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers, > alps_set_slot(dev, 1, num_fingers == 2, x2, y2); > } > >+static void alps_report_coord_and_btn(struct psmouse *psmouse, >+ struct alps_fields *f) >+{ >+ struct input_dev *dev; >+ >+ if (!psmouse || !f) >+ return; >+ >+ dev = psmouse->dev; >+ >+ if (f->fingers) { >+ input_report_key(dev, BTN_TOUCH, 1); >+ alps_report_semi_mt_data(dev, f->fingers, >+ f->pt_img[0].x, f->pt_img[0].y, >+ f->pt_img[1].x, f->pt_img[1].y); >+ input_mt_report_finger_count(dev, f->fingers); >+ >+ input_report_abs(dev, ABS_X, f->pt_img[0].x); >+ input_report_abs(dev, ABS_Y, f->pt_img[0].y); >+ input_report_abs(dev, ABS_PRESSURE, f->pt_img[0].z); >+ } else { >+ input_report_key(dev, BTN_TOUCH, 0); >+ input_mt_report_finger_count(dev, 0); >+ input_report_abs(dev, ABS_PRESSURE, 0); >+ } >+ >+ input_report_key(dev, BTN_LEFT, f->btn.left); >+ input_report_key(dev, BTN_RIGHT, f->btn.right); >+ >+ input_sync(dev); >+} >+ > static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) > { > struct alps_data *priv = psmouse->private; >@@ -523,13 +577,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) > > static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) > { >- f->left = !!(p[3] & 0x01); >- f->right = !!(p[3] & 0x02); >- f->middle = !!(p[3] & 0x04); >+ f->btn.left = !!(p[3] & 0x01); >+ f->btn.right = !!(p[3] & 0x02); >+ f->btn.middle = !!(p[3] & 0x04); > >- f->ts_left = !!(p[3] & 0x10); >- f->ts_right = !!(p[3] & 0x20); >- f->ts_middle = !!(p[3] & 0x40); >+ f->btn.ts_left = !!(p[3] & 0x10); >+ f->btn.ts_right = !!(p[3] & 0x20); >+ f->btn.ts_middle = !!(p[3] & 0x40); > } > > static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, >@@ -546,10 +600,10 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p, > ((p[2] & 0x7f) << 1) | > (p[4] & 0x01); > >- f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | >+ f->pt.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | > ((p[0] & 0x30) >> 4); >- f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); >- f->z = p[5] & 0x7f; >+ f->pt.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); >+ f->pt.z = p[5] & 0x7f; > > alps_decode_buttons_v3(f, p); > } >@@ -573,9 +627,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p, > f->is_mp = !!(p[0] & 0x20); > > if (!f->is_mp) { >- f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); >- f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); >- f->z = (p[0] & 4) ? 0 : p[5] & 0x7f; >+ f->pt.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7)); >+ f->pt.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3)); >+ f->pt.z = (p[0] & 4) ? 0 : p[5] & 0x7f; > alps_decode_buttons_v3(f, p); > } else { > f->fingers = ((p[0] & 0x6) >> 1 | >@@ -687,7 +741,7 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) > * with x, y, and z all zero, so these seem to be flukes. > * Ignore them. > */ >- if (f.x && f.y && !f.z) >+ if (f.pt.x && f.pt.y && !f.pt.z) > return; > > /* >@@ -695,12 +749,12 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) > * to rely on ST data. > */ > if (!fingers) { >- x1 = f.x; >- y1 = f.y; >- fingers = f.z > 0 ? 1 : 0; >+ x1 = f.pt.x; >+ y1 = f.pt.y; >+ fingers = f.pt.z > 0 ? 1 : 0; > } > >- if (f.z >= 64) >+ if (f.pt.z >= 64) > input_report_key(dev, BTN_TOUCH, 1); > else > input_report_key(dev, BTN_TOUCH, 0); >@@ -709,22 +763,22 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) > > input_mt_report_finger_count(dev, fingers); > >- input_report_key(dev, BTN_LEFT, f.left); >- input_report_key(dev, BTN_RIGHT, f.right); >- input_report_key(dev, BTN_MIDDLE, f.middle); >+ input_report_key(dev, BTN_LEFT, f.btn.left); >+ input_report_key(dev, BTN_RIGHT, f.btn.right); >+ input_report_key(dev, BTN_MIDDLE, f.btn.middle); > >- if (f.z > 0) { >- input_report_abs(dev, ABS_X, f.x); >- input_report_abs(dev, ABS_Y, f.y); >+ if (f.pt.z > 0) { >+ input_report_abs(dev, ABS_X, f.pt.x); >+ input_report_abs(dev, ABS_Y, f.pt.y); > } >- input_report_abs(dev, ABS_PRESSURE, f.z); >+ input_report_abs(dev, ABS_PRESSURE, f.pt.z); > > input_sync(dev); > > if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { >- input_report_key(dev2, BTN_LEFT, f.ts_left); >- input_report_key(dev2, BTN_RIGHT, f.ts_right); >- input_report_key(dev2, BTN_MIDDLE, f.ts_middle); >+ input_report_key(dev2, BTN_LEFT, f.btn.ts_left); >+ input_report_key(dev2, BTN_RIGHT, f.btn.ts_right); >+ input_report_key(dev2, BTN_MIDDLE, f.btn.ts_middle); > input_sync(dev2); > } > } >@@ -916,6 +970,364 @@ static void alps_process_packet_v4(struct psmouse *psmouse) > input_sync(dev); > } > >+static bool alps_is_valid_package_v7(struct psmouse *psmouse) >+{ >+ if ((psmouse->pktcnt == 3) && ((psmouse->packet[2] & 0x40) != 0x40)) >+ return false; >+ if ((psmouse->pktcnt == 4) && ((psmouse->packet[3] & 0x48) != 0x48)) >+ return false; >+ if ((psmouse->pktcnt == 6) && ((psmouse->packet[5] & 0x40) != 0x0)) >+ return false; >+ return true; >+} >+ >+static int alps_drop_unsupported_packet_v7(struct psmouse *psmouse) >+{ >+ struct alps_data *priv = psmouse->private; >+ int drop = 1; >+ >+ if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW || >+ priv->r.v7.pkt_id == V7_PACKET_ID_TWO || >+ priv->r.v7.pkt_id == V7_PACKET_ID_MULTI || >+ priv->r.v7.pkt_id == V7_PACKET_ID_IDLE) >+ drop = 0; >+ >+ return drop; >+} >+ >+static unsigned char alps_get_packet_id_v7(char *byte) >+{ >+ unsigned char packet_id; >+ >+ if (byte[4] & 0x40) >+ packet_id = V7_PACKET_ID_TWO; >+ else if (byte[4] & 0x01) >+ packet_id = V7_PACKET_ID_MULTI; >+ else if ((byte[0] & 0x10) && !(byte[4] & 0x43)) >+ packet_id = V7_PACKET_ID_NEW; >+ else >+ packet_id = V7_PACKET_ID_IDLE; >+ >+ return packet_id; >+} >+ >+static void alps_get_finger_coordinate_v7(struct alps_abs_data *pt, >+ unsigned char *pkt, >+ unsigned char pkt_id) >+{ >+ if ((pkt_id == V7_PACKET_ID_TWO) || >+ (pkt_id == V7_PACKET_ID_MULTI) || >+ (pkt_id == V7_PACKET_ID_NEW)) { >+ pt[0].x = ((pkt[2] & 0x80) << 4); >+ pt[0].x |= ((pkt[2] & 0x3F) << 5); >+ pt[0].x |= ((pkt[3] & 0x30) >> 1); >+ pt[0].x |= (pkt[3] & 0x07); >+ pt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07); >+ >+ pt[1].x = ((pkt[3] & 0x80) << 4); >+ pt[1].x |= ((pkt[4] & 0x80) << 3); >+ pt[1].x |= ((pkt[4] & 0x3F) << 4); >+ pt[1].y = ((pkt[5] & 0x80) << 3); >+ pt[1].y |= ((pkt[5] & 0x3F) << 4); >+ >+ if (pkt_id == V7_PACKET_ID_TWO) { >+ pt[1].x &= ~0x000F; >+ pt[1].y |= 0x000F; >+ } else if (pkt_id == V7_PACKET_ID_MULTI) { >+ pt[1].x &= ~0x003F; >+ pt[1].y &= ~0x0020; >+ pt[1].y |= ((pkt[4] & 0x02) << 4); >+ pt[1].y |= 0x001F; >+ } else if (pkt_id == V7_PACKET_ID_NEW) { >+ pt[1].x &= ~0x003F; >+ pt[1].x |= (pkt[0] & 0x20); >+ pt[1].y |= 0x000F; >+ } >+ >+ pt[0].y = 0x7FF - pt[0].y; >+ pt[1].y = 0x7FF - pt[1].y; >+ >+ pt[0].z = (pt[0].x && pt[0].y) ? 62 : 0; >+ pt[1].z = (pt[1].x && pt[1].y) ? 62 : 0; >+ } >+} >+ >+static void alps_decode_packet_v7(struct alps_fields *f, >+ unsigned char *p, >+ struct psmouse *psmouse) >+{ >+ struct alps_data *priv = psmouse->private; >+ static struct v7_raw prev_r; >+ >+ priv->r.v7.pkt_id = alps_get_packet_id_v7(p); >+ >+ alps_get_finger_coordinate_v7(f->pt_img, p, priv->r.v7.pkt_id); >+ >+ priv->r.v7.rest_left = 0; >+ priv->r.v7.rest_right = 0; >+ priv->r.v7.additional_fingers = 0; >+ priv->phy_btn = 0; >+ >+ if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO || >+ priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) { >+ priv->r.v7.rest_left = (p[0] & 0x10) >> 4; >+ priv->r.v7.rest_right = (p[0] & 0x20) >> 5; >+ } >+ >+ if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) >+ priv->r.v7.additional_fingers = p[5] & 0x03; >+ >+ priv->phy_btn = (p[0] & 0x80) >> 7; >+ >+ if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO) { >+ if (f->pt_img[0].z != 0 && f->pt_img[1].z != 0) >+ priv->r.v7.raw_fn = 2; >+ else >+ priv->r.v7.raw_fn = 1; >+ } else if (priv->r.v7.pkt_id == V7_PACKET_ID_MULTI) >+ priv->r.v7.raw_fn = 3 + priv->r.v7.additional_fingers; >+ else if (priv->r.v7.pkt_id == V7_PACKET_ID_IDLE) >+ priv->r.v7.raw_fn = 0; >+ else if (priv->r.v7.pkt_id == V7_PACKET_ID_NEW) >+ priv->r.v7.raw_fn = prev_r.raw_fn; >+ >+ /* It is a trick to bypass firmware bug of older version >+ that 'New' Packet is missed when finger number changed. >+ We fake a 'New' Packet in such cases.*/ >+ if (priv->r.v7.pkt_id == V7_PACKET_ID_TWO || >+ priv->r.v7.pkt_id == V7_PACKET_ID_MULTI || >+ priv->r.v7.pkt_id == V7_PACKET_ID_IDLE) { >+ if (priv->r.v7.raw_fn != prev_r.raw_fn) >+ priv->r.v7.pkt_id = V7_PACKET_ID_NEW; >+ } >+ >+ memcpy(&prev_r, &priv->r.v7, sizeof(struct v7_raw)); >+} >+ >+static void alps_set_each_pt_attr_v7(struct psmouse *psmouse, >+ struct alps_abs_data *pt, >+ struct alps_bl_pt_attr *pt_attr) >+{ >+ struct alps_data *priv = psmouse->private; >+ unsigned int dist; >+ >+ if (!pt_attr->is_init_pt_got && pt->z != 0) { >+ pt_attr->is_init_pt_got = 1; >+ pt_attr->is_counted = 0; >+ memcpy(&pt_attr->init_pt, pt, sizeof(pt_attr->init_pt)); >+ } >+ >+ if (pt->z != 0) { >+ if (pt->y < priv->resting_zone_y_min) { >+ /* A finger is recognized as a non-resting finger >+ if it's position is outside the resting finger zone.*/ >+ pt_attr->zone = ZONE_NORMAL; >+ pt_attr->is_counted = 1; >+ } else { >+ /* A finger is recognized as a resting finger if it's >+ position is inside the resting finger zone and there's >+ no large movement from it's touch down position.*/ >+ pt_attr->zone = ZONE_RESTING; >+ >+ if (pt->x > priv->x_max / 2) >+ pt_attr->zone |= ZONE_RIGHT_BTN; >+ else >+ pt_attr->zone |= ZONE_LEFT_BTN; >+ >+ /* A resting finger will turn to be a non-resting >+ finger if it has made large movement from it's touch >+ down position. A non-resting finger will never turn >+ to a resting finger before it leaves the touchpad >+ surface.*/ >+ if (pt_attr->is_init_pt_got) { >+ dist = alps_pt_distance(pt, &pt_attr->init_pt); >+ >+ if (dist > V7_LARGE_MOVEMENT) >+ pt_attr->is_counted = 1; >+ } >+ } >+ } >+} >+ >+static void alps_set_pt_attr_v7(struct psmouse *psmouse, >+ struct alps_fields *f) >+{ >+ struct alps_data *priv = psmouse->private; >+ int i; >+ >+ switch (priv->r.v7.pkt_id) { >+ case V7_PACKET_ID_TWO: >+ case V7_PACKET_ID_MULTI: >+ for (i = 0; i < V7_IMG_PT_NUM; i++) { >+ alps_set_each_pt_attr_v7(psmouse, >+ &f->pt_img[i], >+ &priv->pt_attr[i]); >+ } >+ break; >+ default: >+ /*All finger attributes are cleared when packet ID is >+ 'IDLE', 'New'or other unknown IDs. An 'IDLE' packet >+ indicates that there's no finger and no button activity. >+ A 'NEW' packet indicates the finger position in packet >+ is not continues from previous packet. Such as the >+ condition there's finger placed or lifted. In these cases, >+ finger attributes will be reset.*/ >+ memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * 2); >+ break; >+ } >+} >+ >+static void alps_cal_output_finger_num_v7(struct psmouse *psmouse, >+ struct alps_fields *f) >+{ >+ struct alps_data *priv = psmouse->private; >+ unsigned int fn = 0; >+ int i; >+ >+ switch (priv->r.v7.pkt_id) { >+ case V7_PACKET_ID_IDLE: >+ case V7_PACKET_ID_NEW: >+ /*No finger is reported when packet ID is 'IDLE' or 'New'. >+ An 'IDLE' packet indicates that there's no finger on touchpad. >+ A 'NEW' packet indicates there's finger placed or lifted. >+ Finger position of 'New' packet is not continues from the >+ previous packet.*/ >+ fn = 0; >+ break; >+ case V7_PACKET_ID_TWO: >+ if (f->pt_img[0].z == 0) { >+ /*The first finger slot is zero when a non-resting >+ finger lifted and remaining only one resting finger >+ on touchpad. Hardware report the remaining resting >+ finger in second slot. This resting is ignored*/ >+ fn = 0; >+ } else if (f->pt_img[1].z == 0) { >+ /* The second finger slot is zero if there's >+ only one finger*/ >+ fn = 1; >+ } else { >+ /*All non-resting fingers will be counted to report*/ >+ fn = 0; >+ for (i = 0; i < V7_IMG_PT_NUM; i++) { >+ if (priv->pt_attr[i].is_counted) >+ fn++; >+ } >+ >+ /*In the case that both fingers are >+ resting fingers, report the first one*/ >+ if (!priv->pt_attr[0].is_counted && >+ !priv->pt_attr[1].is_counted) { >+ fn = 1; >+ } >+ } >+ break; >+ case V7_PACKET_ID_MULTI: >+ /*A packet ID 'MULTI' indicats that at least 3 non-resting >+ finger exist.*/ >+ fn = 3 + priv->r.v7.additional_fingers; >+ break; >+ } >+ >+ f->fingers = fn; >+} >+ >+static void alps_button_dead_zone_filter(struct psmouse *psmouse, >+ struct alps_fields *f, >+ struct alps_fields *prev_f) >+{ >+ struct alps_data *priv = psmouse->private; >+ int dx, dy; >+ >+ if (priv->prev_phy_btn == 0 && priv->phy_btn != 0) { >+ memcpy(&priv->pt_attr[0].init_dead_pt, >+ &f->pt_img[0], >+ sizeof(struct alps_abs_data)); >+ } >+ >+ if (priv->pt_attr[0].init_dead_pt.x != 0 && >+ priv->pt_attr[0].init_dead_pt.x != 0) { >+ dx = f->pt_img[0].x - priv->pt_attr[0].init_dead_pt.x; >+ dy = f->pt_img[0].y - priv->pt_attr[0].init_dead_pt.y; >+ if ((abs(dx) > V7_DEAD_ZONE_OFFSET_X) || >+ (abs(dy) > V7_DEAD_ZONE_OFFSET_Y)) { >+ memset(&priv->pt_attr[0].init_dead_pt, 0, >+ sizeof(struct alps_abs_data)); >+ priv->btn_delay_cnt = 0; >+ } else { >+ memcpy(&f->pt_img[0], >+ &prev_f->pt_img[0], >+ sizeof(struct alps_abs_data)); >+ if (priv->prev_phy_btn == 0 && priv->phy_btn != 0) >+ priv->btn_delay_cnt = 2; >+ } >+ } >+ >+ if (priv->btn_delay_cnt > 0) { >+ f->btn.left = 0; >+ f->btn.right = 0; >+ priv->btn_delay_cnt--; >+ } >+} >+ >+static void alps_assign_buttons_v7(struct psmouse *psmouse, >+ struct alps_fields *f, >+ struct alps_fields *prev_f) >+{ >+ struct alps_data *priv = psmouse->private; >+ >+ if (priv->phy_btn) { >+ if (!priv->prev_phy_btn) { >+ /* Report a right click as long as there's finger on >+ right button zone. Othrewise, report a left click.*/ >+ if (priv->r.v7.rest_right || >+ priv->pt_attr[0].zone & ZONE_RIGHT_BTN || >+ priv->pt_attr[1].zone & ZONE_RIGHT_BTN) { >+ f->btn.right = 1; >+ priv->pressed_btn_bits |= RIGHT_BUTTON_BIT; >+ } else { >+ f->btn.left = 1; >+ priv->pressed_btn_bits |= LEFT_BUTTON_BIT; >+ } >+ } else { >+ if (priv->pressed_btn_bits & RIGHT_BUTTON_BIT) >+ f->btn.right = 1; >+ if (priv->pressed_btn_bits & LEFT_BUTTON_BIT) >+ f->btn.left = 1; >+ } >+ } else { >+ priv->pressed_btn_bits = 0; >+ f->btn.right = 0; >+ f->btn.left = 0; >+ } >+ >+ alps_button_dead_zone_filter(psmouse, f, prev_f); >+ >+ priv->prev_phy_btn = priv->phy_btn; >+} >+ >+static void alps_process_packet_v7(struct psmouse *psmouse) >+{ >+ struct alps_data *priv = psmouse->private; >+ struct alps_fields f = {0}; >+ static struct alps_fields prev_f; >+ unsigned char *packet = psmouse->packet; >+ >+ priv->decode_fields(&f, packet, psmouse); >+ >+ if (alps_drop_unsupported_packet_v7(psmouse)) >+ return; >+ >+ alps_set_pt_attr_v7(psmouse, &f); >+ >+ alps_cal_output_finger_num_v7(psmouse, &f); >+ >+ alps_assign_buttons_v7(psmouse, &f, &prev_f); >+ >+ alps_report_coord_and_btn(psmouse, &f); >+ >+ memcpy(&prev_f, &f, sizeof(struct alps_fields)); >+} >+ > static void alps_report_bare_ps2_packet(struct psmouse *psmouse, > unsigned char packet[], > bool report_buttons) >@@ -1080,6 +1492,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) > return PSMOUSE_BAD_DATA; > } > >+ if ((priv->proto_version == ALPS_PROTO_V7 && >+ !alps_is_valid_package_v7(psmouse))) { >+ psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", >+ psmouse->pktcnt - 1, >+ psmouse->packet[psmouse->pktcnt - 1]); >+ return PSMOUSE_BAD_DATA; >+ } >+ > if (psmouse->pktcnt == psmouse->pktsize) { > priv->process_packet(psmouse); > return PSMOUSE_FULL_PACKET; >@@ -1192,6 +1612,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, > return 0; > } > >+static int alps_check_valid_firmware_id(unsigned char id[]) >+{ >+ int valid = 1; >+ >+ if (id[0] == 0x73) >+ valid = 1; >+ else if (id[0] == 0x88) { >+ if ((id[1] == 0x07) || >+ (id[1] == 0x08) || >+ ((id[1] & 0xf0) == 0xB0)) >+ valid = 1; >+ } >+ >+ return valid; >+} >+ > static int alps_enter_command_mode(struct psmouse *psmouse) > { > unsigned char param[4]; >@@ -1201,8 +1637,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse) > return -1; > } > >- if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) && >- param[0] != 0x73) { >+ if (!alps_check_valid_firmware_id(param)) { > psmouse_dbg(psmouse, > "unknown response while entering command mode\n"); > return -1; >@@ -1704,6 +2139,36 @@ error: > return ret; > } > >+static int alps_hw_init_v7(struct psmouse *psmouse) >+{ >+ struct ps2dev *ps2dev = &psmouse->ps2dev; >+ int reg_val, ret = -1; >+ >+ if (alps_enter_command_mode(psmouse)) >+ goto error; >+ >+ reg_val = alps_command_mode_read_reg(psmouse, 0xc2d9); >+ if (reg_val == -1) >+ goto error; >+ >+ if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) >+ goto error; >+ >+ reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); >+ if (reg_val == -1) >+ goto error; >+ >+ if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) >+ goto error; >+ >+ alps_exit_command_mode(psmouse); >+ return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); >+ >+error: >+ alps_exit_command_mode(psmouse); >+ return ret; >+} >+ > /* Must be in command mode when calling this function */ > static int alps_absolute_mode_v4(struct psmouse *psmouse) > { >@@ -1875,6 +2340,7 @@ static void alps_set_defaults(struct alps_data *priv) > priv->set_abs_params = alps_set_abs_params_st; > priv->x_max = 1023; > priv->y_max = 767; >+ priv->slot_number = 1; > break; > case ALPS_PROTO_V3: > priv->hw_init = alps_hw_init_v3; >@@ -1883,6 +2349,7 @@ static void alps_set_defaults(struct alps_data *priv) > priv->decode_fields = alps_decode_pinnacle; > priv->nibble_commands = alps_v3_nibble_commands; > priv->addr_command = PSMOUSE_CMD_RESET_WRAP; >+ priv->slot_number = 2; > break; > case ALPS_PROTO_V4: > priv->hw_init = alps_hw_init_v4; >@@ -1890,6 +2357,7 @@ static void alps_set_defaults(struct alps_data *priv) > priv->set_abs_params = alps_set_abs_params_mt; > priv->nibble_commands = alps_v4_nibble_commands; > priv->addr_command = PSMOUSE_CMD_DISABLE; >+ priv->slot_number = 2; > break; > case ALPS_PROTO_V5: > priv->hw_init = alps_hw_init_dolphin_v1; >@@ -1905,6 +2373,7 @@ static void alps_set_defaults(struct alps_data *priv) > priv->y_max = 660; > priv->x_bits = 23; > priv->y_bits = 12; >+ priv->slot_number = 2; > break; > case ALPS_PROTO_V6: > priv->hw_init = alps_hw_init_v6; >@@ -1913,6 +2382,28 @@ static void alps_set_defaults(struct alps_data *priv) > priv->nibble_commands = alps_v6_nibble_commands; > priv->x_max = 2047; > priv->y_max = 1535; >+ priv->slot_number = 2; >+ break; >+ case ALPS_PROTO_V7: >+ priv->hw_init = alps_hw_init_v7; >+ priv->process_packet = alps_process_packet_v7; >+ priv->decode_fields = alps_decode_packet_v7; >+ priv->set_abs_params = alps_set_abs_params_mt; >+ priv->nibble_commands = alps_v3_nibble_commands; >+ priv->addr_command = PSMOUSE_CMD_RESET_WRAP; >+ priv->x_max = 0xfff; >+ priv->y_max = 0x7ff; >+ priv->resting_zone_y_min = 0x654; >+ priv->byte0 = 0x48; >+ priv->mask0 = 0x48; >+ priv->flags = 0; >+ priv->slot_number = 2; >+ >+ priv->phy_btn = 0; >+ priv->prev_phy_btn = 0; >+ priv->btn_delay_cnt = 0; >+ priv->pressed_btn_bits = 0; >+ memset(priv->pt_attr, 0, sizeof(priv->pt_attr[0]) * 2); > break; > } > } >@@ -1982,6 +2473,11 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) > return -EIO; > else > return 0; >+ } else if (ec[0] == 0x88 && (ec[1] & 0xf0) == 0xB0) { >+ priv->proto_version = ALPS_PROTO_V7; >+ alps_set_defaults(priv); >+ >+ return 0; > } else if (ec[0] == 0x88 && ec[1] == 0x08) { > priv->proto_version = ALPS_PROTO_V3; > alps_set_defaults(priv); >@@ -2045,7 +2541,7 @@ static void alps_set_abs_params_mt(struct alps_data *priv, > struct input_dev *dev1) > { > set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); >- input_mt_init_slots(dev1, 2, 0); >+ input_mt_init_slots(dev1, priv->slot_number, 0); > input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); > input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); > >diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h >index 03f88b6..dedbd27 100644 >--- a/drivers/input/mouse/alps.h >+++ b/drivers/input/mouse/alps.h >@@ -18,11 +18,36 @@ > #define ALPS_PROTO_V4 4 > #define ALPS_PROTO_V5 5 > #define ALPS_PROTO_V6 6 >+#define ALPS_PROTO_V7 7 >+ >+#define MAX_IMG_PT_NUM 5 >+#define V7_IMG_PT_NUM 2 >+ >+#define ZONE_NORMAL 0x01 >+#define ZONE_RESTING 0x02 >+#define ZONE_LEFT_BTN 0x04 >+#define ZONE_RIGHT_BTN 0x08 > > #define DOLPHIN_COUNT_PER_ELECTRODE 64 > #define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */ > #define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */ > >+/* >+ * enum V7_PACKET_ID - defines the packet type for V7 >+ * V7_PACKET_ID_IDLE: There's no finger and no button activity. >+ * V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad >+ * or there's button activities. >+ * V7_PACKET_ID_MULTI: There are at least three non-resting fingers. >+ * V7_PACKET_ID_NEW: The finger position in slot is not continues from >+ * previous packet. >+*/ >+enum V7_PACKET_ID { >+ V7_PACKET_ID_IDLE, >+ V7_PACKET_ID_TWO, >+ V7_PACKET_ID_MULTI, >+ V7_PACKET_ID_NEW, >+}; >+ > /** > * struct alps_model_info - touchpad ID table > * @signature: E7 response string to match. >@@ -66,15 +91,7 @@ struct alps_nibble_commands { > }; > > /** >- * struct alps_fields - decoded version of the report packet >- * @x_map: Bitmap of active X positions for MT. >- * @y_map: Bitmap of active Y positions for MT. >- * @fingers: Number of fingers for MT. >- * @x: X position for ST. >- * @y: Y position for ST. >- * @z: Z position for ST. >- * @first_mp: Packet is the first of a multi-packet report. >- * @is_mp: Packet is part of a multi-packet report. >+ * struct alps_btn - decoded version of the button status > * @left: Left touchpad button is active. > * @right: Right touchpad button is active. > * @middle: Middle touchpad button is active. >@@ -82,16 +99,7 @@ struct alps_nibble_commands { > * @ts_right: Right trackstick button is active. > * @ts_middle: Middle trackstick button is active. > */ >-struct alps_fields { >- unsigned int x_map; >- unsigned int y_map; >- unsigned int fingers; >- unsigned int x; >- unsigned int y; >- unsigned int z; >- unsigned int first_mp:1; >- unsigned int is_mp:1; >- >+struct alps_btn { > unsigned int left:1; > unsigned int right:1; > unsigned int middle:1; >@@ -102,6 +110,73 @@ struct alps_fields { > }; > > /** >+ * struct alps_btn - decoded version of the X Y Z postion for ST. >+ * @x: X position for ST. >+ * @y: Y position for ST. >+ * @z: Z position for ST. >+ */ >+struct alps_abs_data { >+ unsigned int x; >+ unsigned int y; >+ unsigned int z; >+}; >+ >+/** >+ * struct alps_fields - decoded version of the report packet >+ * @fingers: Number of fingers for MT. >+ * @pt: X Y Z postion for ST. >+ * @pt: X Y Z postion for image MT. >+ * @x_map: Bitmap of active X positions for MT. >+ * @y_map: Bitmap of active Y positions for MT. >+ * @first_mp: Packet is the first of a multi-packet report. >+ * @is_mp: Packet is part of a multi-packet report. >+ * @btn: Button activity status >+ */ >+struct alps_fields { >+ unsigned int fingers; >+ struct alps_abs_data pt; >+ struct alps_abs_data pt_img[MAX_IMG_PT_NUM]; >+ unsigned int x_map; >+ unsigned int y_map; >+ unsigned int first_mp:1; >+ unsigned int is_mp:1; >+ struct alps_btn btn; >+}; >+ >+/** >+ * struct v7_raw - data decoded from raw packet for V7. >+ * @pkt_id: An id that specifies the type of packet. >+ * @additional_fingers: Number of additional finger that is neighter included >+ * in pt slot nor reflected in rest_left and rest_right flag of data packet. >+ * @rest_left: There are fingers on left resting zone. >+ * @rest_right: There are fingers on right resting zone. >+ * @raw_fn: The number of finger on touchpad. >+ */ >+struct v7_raw { >+ unsigned char pkt_id; >+ unsigned int additional_fingers; >+ unsigned char rest_left; >+ unsigned char rest_right; >+ unsigned char raw_fn; >+}; >+ >+/** >+ * struct alps_bl_pt_attr - generic attributes of touch points for buttonless device >+ * @zone: The part of touchpad that the touch point locates >+ * @is_counted: The touch point is not a resting finger. >+ * @is_init_pt_got: The touch down point is got. >+ * @init_pt: The X Y Z position of the touch down point. >+ * @init_dead_pt: The touch down point of a finger used by dead zone process. >+ */ >+struct alps_bl_pt_attr { >+ unsigned char zone; >+ unsigned char is_counted; >+ unsigned char is_init_pt_got; >+ struct alps_abs_data init_pt; >+ struct alps_abs_data init_dead_pt; >+}; >+ >+/** > * struct alps_data - private data structure for the ALPS driver > * @dev2: "Relative" device used to report trackstick or mouse activity. > * @phys: Physical path for the relative device. >@@ -116,8 +191,10 @@ struct alps_fields { > * @flags: Additional device capabilities (passthrough port, trackstick, etc.). > * @x_max: Largest possible X position value. > * @y_max: Largest possible Y position value. >+ * @resting_zone_y_min: Smallest Y postion value of the bottom resting zone. > * @x_bits: Number of X bits in the MT bitmap. > * @y_bits: Number of Y bits in the MT bitmap. >+ * @img_fingers: Number of image fingers. > * @hw_init: Protocol-specific hardware init function. > * @process_packet: Protocol-specific function to process a report packet. > * @decode_fields: Protocol-specific function to read packet bitfields. >@@ -132,6 +209,11 @@ struct alps_fields { > * @fingers: Number of fingers from last MT report. > * @quirks: Bitmap of ALPS_QUIRK_*. > * @timer: Timer for flushing out the final report packet in the stream. >+ * @v7: Data decoded from raw packet for V7 >+ * @phy_btn: Physical button is active. >+ * @prev_phy_btn: Physical button of previous packet is active. >+ * @pressed_btn_bits: Pressed positon of button zone >+ * @pt_attr: Generic attributes of touch points for buttonless device. > */ > struct alps_data { > struct input_dev *dev2; >@@ -145,8 +227,10 @@ struct alps_data { > unsigned char flags; > int x_max; > int y_max; >+ int resting_zone_y_min; > int x_bits; > int y_bits; >+ unsigned char slot_number; > > int (*hw_init)(struct psmouse *psmouse); > void (*process_packet)(struct psmouse *psmouse); >@@ -161,6 +245,16 @@ struct alps_data { > int fingers; > u8 quirks; > struct timer_list timer; >+ >+ /* these are used for buttonless touchpad*/ >+ union { >+ struct v7_raw v7; >+ } r; >+ unsigned char phy_btn; >+ unsigned char prev_phy_btn; >+ unsigned char btn_delay_cnt; >+ unsigned char pressed_btn_bits; >+ struct alps_bl_pt_attr pt_attr[MAX_IMG_PT_NUM]; > }; > > #define ALPS_QUIRK_TRACKSTICK_BUTTONS 1 /* trakcstick buttons in trackstick packet */ >-- >1.8.3.2 > >-- >To unsubscribe from this list: send the line "unsubscribe linux-input" in >the body of a message to majordomo@xxxxxxxxxxxxxxx >More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html