Hi JJ On Mon, 2011-08-29 at 16:28 +0800, JJ Ding wrote: > v4 hardware is a true multitouch capable touchpad (up to 5 fingers). > The packet format is quite complex, please see protocol document for > reference. > > Signed-off-by: JJ Ding <jj_ding@xxxxxxxxxx> > --- > Documentation/input/elantech.txt | 170 ++++++++++++++++++++++++++ > drivers/input/mouse/elantech.c | 247 ++++++++++++++++++++++++++++++++++---- > drivers/input/mouse/elantech.h | 29 ++++- > 3 files changed, 420 insertions(+), 26 deletions(-) > > diff --git a/Documentation/input/elantech.txt b/Documentation/input/elantech.txt > index cee08ee..f63115a 100644 > --- a/Documentation/input/elantech.txt > +++ b/Documentation/input/elantech.txt > @@ -32,6 +32,12 @@ Contents > 6.2 Native absolute mode 6 byte packet format > 6.2.1 One/Three finger touch > 6.2.2 Two finger touch > + 7. Hardware version 4 > + 7.1 Registers > + 7.2 Native absolute mode 6 byte packet format > + 7.2.1 Status packet > + 7.2.2 Head packet > + 7.2.3 Motion packet > > > > @@ -573,3 +579,167 @@ The packet format is exactly the same for two finger touch, except the hardware > sends two 6 byte packets. The first packet contains data for the first finger, > the second packet has data for the second finger. So for two finger touch a > total of 12 bytes are sent. > + > +///////////////////////////////////////////////////////////////////////////// > + > +7. Hardware version 4 > + ================== > + > +7.1 Registers > + ~~~~~~~~~ > +* reg_07 > + > + bit 7 6 5 4 3 2 1 0 > + 0 0 0 0 0 0 0 A > + > + A: 1 = enable absolute tracking > + > +7.2 Native absolute mode 6 byte packet format > + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > +v4 hardware is a true multitouch touchpad, capable of tracking up to 5 fingers. > +Unfortunately, due to PS/2's limited bandwidth, its packet format is rather > +complex. > + > +Whenever the numbers or identities of the fingers changes, the hardware sends a > +status packet to indicate how many and which fingers is on touchpad, followed by > +head packets or motion packets. A head packet contains data of finger id, finger > +position (absolute x, y values), width, and presure. A motion packet contains > +two fingers' position delta. > + > +For example, when status packet tells there are 2 fingers on touchpad, then we > +can expect two following head packets. If the finger status doesn't change, > +the following packets would be motion packets, only sending delta of finger > +position, until we receive a status packet. > + > +One exception is one finger touch. when a status packet tells us there is only > +one finger, the hardware would just send head packets afterwards. > + > +7.2.1 Status packet > + ~~~~~~~~~~~~~ > + > +byte 0: > + > + bit 7 6 5 4 3 2 1 0 > + . . . . 0 1 R L > + > + L, R = 1 when Left, Right mouse button pressed > + > +byte 1: > + > + bit 7 6 5 4 3 2 1 0 > + . . . ft4 ft3 ft2 ft1 ft0 > + > + ft4 ft3 ft2 ft1 ft0 ftn = 1 when finger n is on touchpad > + > +byte 2: not used > + > +byte 3: > + > + bit 7 6 5 4 3 2 1 0 > + . . . 1 0 0 0 0 > + > + constant bits > + > +byte 4: > + > + bit 7 6 5 4 3 2 1 0 > + p . . . . . . . > + > + p = 1 for palm > + > +byte 5: not used > + > +7.2.2 Head packet > + ~~~~~~~~~~~ > + > +byte 0: > + > + bit 7 6 5 4 3 2 1 0 > + w3 w2 w1 w0 0 1 R L > + > + L, R = 1 when Left, Right mouse button pressed > + w3..w0 = finger width (spans how many trace lines) > + > +byte 1: > + > + bit 7 6 5 4 3 2 1 0 > + p7 p6 p5 p4 x11 x10 x9 x8 > + > +byte 2: > + > + bit 7 6 5 4 3 2 1 0 > + x7 x6 x5 x4 x3 x2 x1 x0 > + > + x11..x0 = absolute x value (horizontal) > + > +byte 3: > + > + bit 7 6 5 4 3 2 1 0 > + id2 id1 id0 1 0 0 0 1 > + > + id2..id0 = finger id > + > +byte 4: > + > + bit 7 6 5 4 3 2 1 0 > + p3 p1 p2 p0 y11 y10 y9 y8 > + > + p7..p0 = pressure > + > +byte 5: > + > + bit 7 6 5 4 3 2 1 0 > + y7 y6 y5 y4 y3 y2 y1 y0 > + > + y11..y0 = absolute y value (vertical) > + > +7.2.3 Motion packet > + ~~~~~~~~~~~~~ > + > +byte 0: > + > + bit 7 6 5 4 3 2 1 0 > + id2 id1 id0 w 0 1 R L > + > + L, R = 1 when Left, Right mouse button pressed > + id2..id0 = finger id > + w = 1 when delta overflows (> 127 or < -128), in this case > + firmware sends us (delta x / 5) and (delta y / 5) > + > +byte 1: > + > + bit 7 6 5 4 3 2 1 0 > + x7 x6 x5 x4 x3 x2 x1 x0 > + > + x7..x0 = delta x (two's complement) > + > +byte 2: > + > + bit 7 6 5 4 3 2 1 0 > + y7 y6 y5 y4 y3 y2 y1 y0 > + > + y7..y0 = delta y (two's complement) > + > +byte 3: > + > + bit 7 6 5 4 3 2 1 0 > + id2 id1 id0 1 0 0 1 0 > + > + id2..id0 = finger id > + > +byte 4: > + > + bit 7 6 5 4 3 2 1 0 > + x7 x6 x5 x4 x3 x2 x1 x0 > + > + x7..x0 = delta x (two's complement) > + > +byte 5: > + > + bit 7 6 5 4 3 2 1 0 > + y7 y6 y5 y4 y3 y2 y1 y0 > + > + y7..y0 = delta y (two's complement) > + > + byte 0 ~ 2 for one finger > + byte 3 ~ 5 for another > diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c > index c4ceefd..0d3936d 100644 > --- a/drivers/input/mouse/elantech.c > +++ b/drivers/input/mouse/elantech.c > @@ -84,12 +84,6 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > unsigned char param[3]; > int rc = 0; > > - if (reg < 0x10 || reg > 0x26) > - return -1; > - > - if (reg > 0x11 && reg < 0x20) > - return -1; > - > switch (etd->hw_version) { > case 1: > if (psmouse_sliced_command(psmouse, ETP_REGISTER_READ) || > @@ -109,7 +103,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > } > break; > > - case 3: > + case 3 ... 4: > if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || > elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > @@ -122,8 +116,10 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg, > > if (rc) > pr_err("failed to read register 0x%02x.\n", reg); > - else > + else if (etd->hw_version != 4) > *val = param[0]; > + else > + *val = param[1]; > > return rc; > } > @@ -137,12 +133,6 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > struct elantech_data *etd = psmouse->private; > int rc = 0; > > - if (reg < 0x10 || reg > 0x26) > - return -1; > - > - if (reg > 0x11 && reg < 0x20) > - return -1; > - > switch (etd->hw_version) { > case 1: > if (psmouse_sliced_command(psmouse, ETP_REGISTER_WRITE) || > @@ -176,6 +166,20 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg, > rc = -1; > } > break; > + > + case 4: > + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || > + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > + elantech_ps2_command(psmouse, NULL, reg) || > + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || > + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || > + elantech_ps2_command(psmouse, NULL, val) || > + elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { > + rc = -1; > + } > + break; > } > > if (rc) > @@ -409,12 +413,12 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse, > * byte 1: . . . . ax11 ax10 ax9 ax8 > * byte 2: ax7 ax6 ax5 ax4 ax3 ax2 ax1 ax0 > */ > - etd->prev_x = ((packet[1] & 0x0f) << 8) | packet[2]; > + etd->mt[0].x = ((packet[1] & 0x0f) << 8) | packet[2]; > /* > * byte 4: . . . . ay11 ay10 ay9 ay8 > * byte 5: ay7 ay6 ay5 ay4 ay3 ay2 ay1 ay0 > */ > - etd->prev_y = etd->y_max - > + etd->mt[0].y = etd->y_max - > (((packet[4] & 0x0f) << 8) | packet[5]); > /* > * wait for next packet > @@ -423,8 +427,8 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse, > } > > /* packet_type == PACKET_V3_TAIL */ > - x1 = etd->prev_x; > - y1 = etd->prev_y; > + x1 = etd->mt[0].x; > + y1 = etd->mt[0].y; > x2 = ((packet[1] & 0x0f) << 8) | packet[2]; > y2 = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); > break; > @@ -450,6 +454,129 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse, > input_sync(dev); > } > > +static void elantech_mt_sync(struct psmouse *psmouse) > +{ > + struct input_dev *dev = psmouse->dev; > + unsigned char *packet = psmouse->packet; > + > + input_report_key(dev, BTN_LEFT, packet[0] & 0x01); > + input_report_key(dev, BTN_RIGHT, packet[0] & 0x02); > + input_mt_report_pointer_emulation(dev, true); > + input_sync(dev); > +} > + > +static void process_packet_status(struct psmouse *psmouse) > +{ > + struct input_dev *dev = psmouse->dev; > + unsigned char *packet = psmouse->packet; > + unsigned fingers; > + int i; > + > + /* notify finger state change */ > + fingers = packet[1] & 0x1f; > + for (i = 0; i < ETP_MAX_FINGERS; i++) { > + if ((fingers & (1 << i)) == 0) { > + input_mt_slot(dev, i); > + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); > + } > + } > + > + elantech_mt_sync(psmouse); > +} > + > +static void process_packet_head(struct psmouse *psmouse) > +{ > + struct input_dev *dev = psmouse->dev; > + struct elantech_data *etd = psmouse->private; > + unsigned char *packet = psmouse->packet; > + int id = ((packet[3] & 0xe0) >> 5) - 1; > + int pres, traces; > + > + if (id < 0) > + return; > + > + etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2]; > + etd->mt[id].y = etd->y_max - (((packet[4] & 0x0f) << 8) | packet[5]); > + pres = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4); > + traces = (packet[0] & 0xf0) >> 4; > + > + input_mt_slot(dev, id); > + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); > + > + input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); > + input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); > + input_report_abs(dev, ABS_MT_PRESSURE, pres); > + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, traces * etd->width); > + /* report this for backwards compatibility */ > + input_report_abs(dev, ABS_TOOL_WIDTH, traces); > + > + elantech_mt_sync(psmouse); > +} > + > +static void process_packet_motion(struct psmouse *psmouse) > +{ > + struct input_dev *dev = psmouse->dev; > + struct elantech_data *etd = psmouse->private; > + unsigned char *packet = psmouse->packet; > + int weight, delta_x1 = 0, delta_y1 = 0, delta_x2 = 0, delta_y2 = 0; > + int id, sid; > + > + id = ((packet[0] & 0xe0) >> 5) - 1; > + if (id < 0) > + return; > + > + sid = ((packet[3] & 0xe0) >> 5) - 1; > + weight = (packet[0] & 0x10) ? ETP_WEIGHT_VALUE : 1; > + /* > + * Motion packets give us the delta of x, y values of specific fingers, > + * but in two's complement. Let the compiler do the conversion for us. > + * Also _enlarge_ the numbers to int, in case of overflow. > + */ > + delta_x1 = (signed char)packet[1]; > + delta_y1 = (signed char)packet[2]; > + delta_x2 = (signed char)packet[4]; > + delta_y2 = (signed char)packet[5]; > + > + etd->mt[id].x += delta_x1 * weight; > + etd->mt[id].y -= delta_y1 * weight; > + input_mt_slot(dev, id); > + input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); > + input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); > + > + if (sid >= 0) { > + etd->mt[sid].x += delta_x2 * weight; > + etd->mt[sid].y -= delta_y2 * weight; > + input_mt_slot(dev, sid); > + input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[sid].x); > + input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[sid].y); > + } > + > + elantech_mt_sync(psmouse); > +} > + > +static void elantech_report_absolute_v4(struct psmouse *psmouse, > + int packet_type) > +{ > + switch (packet_type) { > + case PACKET_V4_STATUS: > + process_packet_status(psmouse); > + break; > + > + case PACKET_V4_HEAD: > + process_packet_head(psmouse); > + break; > + > + case PACKET_V4_MOTION: > + process_packet_motion(psmouse); > + break; > + > + case PACKET_UNKNOWN: elantech_packet_dump(psmouse->packet, psmosue->pktsize); You could add this function here. We will get detail data for debug when data type is PACKET_UNKNOWN. > + default: > + /* impossible to get here */ > + break; > + } > +} > + > static int elantech_packet_check_v1(struct psmouse *psmouse) > { > struct elantech_data *etd = psmouse->private; > @@ -504,7 +631,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse) > > /* > * We check the constant bits to determine what packet type we get, > - * so packet checking is mandatory for v3 hardware. > + * so packet checking is mandatory for v3 and later hardware. > */ > static int elantech_packet_check_v3(struct psmouse *psmouse) > { > @@ -527,6 +654,25 @@ static int elantech_packet_check_v3(struct psmouse *psmouse) > return PACKET_UNKNOWN; > } > > +static int elantech_packet_check_v4(struct psmouse *psmouse) > +{ > + unsigned char *packet = psmouse->packet; > + > + if ((packet[0] & 0x0c) == 0x04 && > + (packet[3] & 0x1f) == 0x11) > + return PACKET_V4_HEAD; > + > + if ((packet[0] & 0x0c) == 0x04 && > + (packet[3] & 0x1f) == 0x12) > + return PACKET_V4_MOTION; > + > + if ((packet[0] & 0x0c) == 0x04 && > + (packet[3] & 0x1f) == 0x10) > + return PACKET_V4_STATUS; > + > + return PACKET_UNKNOWN; > +} > + > /* > * Process byte stream from mouse and handle complete packets > */ > @@ -567,6 +713,14 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse) > > elantech_report_absolute_v3(psmouse, packet_type); > break; > + > + case 4: > + packet_type = elantech_packet_check_v4(psmouse); > + if (packet_type == PACKET_UNKNOWN) > + return PSMOUSE_BAD_DATA; > + > + elantech_report_absolute_v4(psmouse, packet_type); > + break; > } > > return PSMOUSE_FULL_PACKET; > @@ -610,6 +764,13 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > rc = -1; > > break; > + > + case 4: > + etd->reg_07 = 0x01; > + if (elantech_write_reg(psmouse, 0x07, etd->reg_07)) > + rc = -1; > + > + goto skip_readback_reg_10; /* v4 has no reg 0x10 to read */ > } > > if (rc == 0) { > @@ -637,6 +798,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > } > } > > + skip_readback_reg_10: > if (rc) > pr_err("failed to initialise registers.\n"); > > @@ -645,10 +807,11 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse) > > static int set_range(struct psmouse *psmouse, unsigned int *x_min, > unsigned int *y_min, unsigned int *x_max, > - unsigned int *y_max) > + unsigned int *y_max, unsigned int *width) > { > struct elantech_data *etd = psmouse->private; > unsigned char param[3]; > + unsigned char traces = 0; > int i; > > switch (etd->hw_version) { > @@ -677,12 +840,16 @@ static int set_range(struct psmouse *psmouse, unsigned int *x_min, > } > break; > > + case 4: > + traces = etd->capabilities[1]; > + /* pass through */ > case 3: > if (synaptics_send_cmd(psmouse, ETP_FW_ID_QUERY, param)) > return -1; > > *x_max = (0x0f & param[0]) << 8 | param[1]; > *y_max = (0xf0 & param[0]) << 4 | param[2]; > + *width = *x_max / (traces - 1); > break; > } > > @@ -696,9 +863,9 @@ static int elantech_set_input_params(struct psmouse *psmouse) > { > struct input_dev *dev = psmouse->dev; > struct elantech_data *etd = psmouse->private; > - unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0; > + unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; > > - if (set_range(psmouse, &x_min, &y_min, &x_max, &y_max)) > + if (set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) > return -1; > > __set_bit(EV_KEY, dev->evbit); > @@ -742,9 +909,37 @@ static int elantech_set_input_params(struct psmouse *psmouse) > input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); > input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); > break; > + > + case 4: > + __set_bit(BTN_TOOL_QUADTAP, dev->keybit); > + /* For X to recognize me as touchpad. */ > + input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0); > + input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0); > + /* > + * range of pressure and width is the same as v2, > + * report ABS_PRESSURE, ABS_TOOL_WIDTH for compatibility. > + */ > + input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2, > + ETP_PMAX_V2, 0, 0); > + input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2, > + ETP_WMAX_V2, 0, 0); > + /* Multitouch capable pad, up to 5 fingers. */ > + input_mt_init_slots(dev, ETP_MAX_FINGERS); > + input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0); > + input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0); > + input_set_abs_params(dev, ABS_MT_PRESSURE, ETP_PMIN_V2, > + ETP_PMAX_V2, 0, 0); > + /* > + * The firmware reports how many trace lines the finger spans, > + * convert to surface unit as Protocol-B requires. > + */ > + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, > + ETP_WMAX_V2 * width, 0, 0); > + break; > } > > etd->y_max = y_max; > + etd->width = width; > > return 0; > } > @@ -816,6 +1011,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse, > elantech_show_int_attr, \ > elantech_set_int_attr) > > +ELANTECH_INT_ATTR(reg_07, 0x07); > ELANTECH_INT_ATTR(reg_10, 0x10); > ELANTECH_INT_ATTR(reg_11, 0x11); > ELANTECH_INT_ATTR(reg_20, 0x20); > @@ -829,6 +1025,7 @@ ELANTECH_INT_ATTR(debug, 0); > ELANTECH_INT_ATTR(paritycheck, 0); > > static struct attribute *elantech_attrs[] = { > + &psmouse_attr_reg_07.dattr.attr, > &psmouse_attr_reg_10.dattr.attr, > &psmouse_attr_reg_11.dattr.attr, > &psmouse_attr_reg_20.dattr.attr, > @@ -957,12 +1154,16 @@ static int elantech_reconnect(struct psmouse *psmouse) > */ > static int elantech_set_properties(struct elantech_data *etd) > { > + int ver = (etd->fw_version & 0x0f0000) >> 16; > + > if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600) > etd->hw_version = 1; > else if (etd->fw_version < 0x150600) > etd->hw_version = 2; > - else if ((etd->fw_version & 0x0f0000) >> 16 == 5) > + else if (ver == 5) > etd->hw_version = 3; > + else if (ver == 6) > + etd->hw_version = 4; > else > return -1; > > diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h > index 236c33c..7ecaef0 100644 > --- a/drivers/input/mouse/elantech.h > +++ b/drivers/input/mouse/elantech.h > @@ -82,14 +82,37 @@ > #define ETP_WMAX_V2 15 > > /* > - * v3 hardware has 2 kinds of packet types. > + * v3 hardware has 2 kinds of packet types, > + * v4 hardware has 3. > */ > #define PACKET_UNKNOWN 0x01 > #define PACKET_DEBOUNCE 0x02 > #define PACKET_V3_HEAD 0x03 > #define PACKET_V3_TAIL 0x04 > +#define PACKET_V4_HEAD 0x05 > +#define PACKET_V4_MOTION 0x06 > +#define PACKET_V4_STATUS 0x07 > + > +/* > + * track up to 5 fingers for v4 hardware > + */ > +#define ETP_MAX_FINGERS 5 > + > +/* > + * weight value for v4 hardware > + */ > +#define ETP_WEIGHT_VALUE 5 > + > +/* > + * The base position for one finger, v4 hardware > + */ > +struct finger_pos { > + unsigned int x; > + unsigned int y; > +}; > > struct elantech_data { > + unsigned char reg_07; > unsigned char reg_10; > unsigned char reg_11; > unsigned char reg_20; > @@ -108,8 +131,8 @@ struct elantech_data { > unsigned int fw_version; > unsigned int single_finger_reports; > unsigned int y_max; > - unsigned int prev_x; > - unsigned int prev_y; > + unsigned int width; > + struct finger_pos mt[ETP_MAX_FINGERS]; > unsigned char parity[256]; > }; > -- 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