/* * V3 and later support this fast command
*/ staticint elantech_send_cmd(struct psmouse *psmouse, unsignedchar c, unsignedchar *param)
{ struct ps2dev *ps2dev = &psmouse->ps2dev;
/* * A retrying version of ps2_command
*/ staticint elantech_ps2_command(struct psmouse *psmouse, unsignedchar *param, int command)
{ struct ps2dev *ps2dev = &psmouse->ps2dev; struct elantech_data *etd = psmouse->private; int rc; int tries = ETP_PS2_COMMAND_TRIES;
do {
rc = ps2_command(ps2dev, param, command); if (rc == 0) break;
tries--;
elantech_debug("retrying ps2 command 0x%02x (%d).\n",
command, tries);
msleep(ETP_PS2_COMMAND_DELAY);
} while (tries > 0);
if (rc)
psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command);
return rc;
}
/* * Send an Elantech style special command to read 3 bytes from a register
*/ staticint elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param)
{ 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, param, PSMOUSE_CMD_GETINFO)) {
psmouse_err(psmouse, "failed to read register %#02x\n", reg); return -EIO;
}
return 0;
}
/* * Send an Elantech style special command to write a register with a parameter
*/ staticint elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param)
{ 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, param[0]) ||
elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) ||
elantech_ps2_command(psmouse, NULL, param[1]) ||
elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) {
psmouse_err(psmouse, "failed to write register %#02x with value %#02x%#02x\n",
reg, param[0], param[1]); return -EIO;
}
return 0;
}
/* * Send an Elantech style special command to read a value from a register
*/ staticint elantech_read_reg(struct psmouse *psmouse, unsignedchar reg, unsignedchar *val)
{ struct elantech_data *etd = psmouse->private; unsignedchar param[3]; int rc = 0;
if (rc)
psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg); elseif (etd->info.hw_version != 4)
*val = param[0]; else
*val = param[1];
return rc;
}
/* * Send an Elantech style special command to write a register with a value
*/ staticint elantech_write_reg(struct psmouse *psmouse, unsignedchar reg, unsignedchar val)
{ struct elantech_data *etd = psmouse->private; int rc = 0;
staticvoid elantech_report_trackpoint(struct psmouse *psmouse, int packet_type)
{ /* * byte 0: 0 0 sx sy 0 M R L * byte 1:~sx 0 0 0 0 0 0 0 * byte 2:~sy 0 0 0 0 0 0 0 * byte 3: 0 0 ~sy ~sx 0 1 1 0 * byte 4: x7 x6 x5 x4 x3 x2 x1 x0 * byte 5: y7 y6 y5 y4 y3 y2 y1 y0 * * x and y are written in two's complement spread * over 9 bits with sx/sy the relative top bit and * x7..x0 and y7..y0 the lower bits. * The sign of y is opposite to what the input driver * expects for a relative movement
*/
/* For clickpads map both buttons to BTN_LEFT */ if (elantech_is_buttonpad(&etd->info))
input_report_key(dev, BTN_LEFT, packet[0] & 0x03); else
psmouse_report_standard_buttons(dev, packet[0]);
/* For clickpads map both buttons to BTN_LEFT */ if (elantech_is_buttonpad(&etd->info))
input_report_key(dev, BTN_LEFT, packet[0] & 0x03); else
psmouse_report_standard_buttons(dev, packet[0]);
id = ((packet[0] & 0xe0) >> 5) - 1; if (id < 0 || id >= ETP_MAX_FINGERS) 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 = (signedchar)packet[1];
delta_y1 = (signedchar)packet[2];
delta_x2 = (signedchar)packet[4];
delta_y2 = (signedchar)packet[5];
staticint elantech_debounce_check_v2(struct psmouse *psmouse)
{ /* * When we encounter packet that matches this exactly, it means the * hardware is in debounce status. Just ignore the whole packet.
*/ staticconst u8 debounce_packet[] = {
0x84, 0xff, 0xff, 0x02, 0xff, 0xff
}; unsignedchar *packet = psmouse->packet;
/* * V2 hardware has two flavors. Older ones that do not report pressure, * and newer ones that reports pressure and width. With newer ones, all * packets (1, 2, 3 finger touch) have the same constant bits. With * older ones, 1/3 finger touch packets and 2 finger touch packets * have different constant bits. * With all three cases, if the constant bits are not exactly what I * expected, I consider them invalid.
*/ if (etd->info.reports_pressure) return (packet[0] & 0x0c) == 0x04 &&
(packet[3] & 0x0f) == 0x02;
/* * We check the constant bits to determine what packet type we get, * so packet checking is mandatory for v3 and later hardware.
*/ staticint elantech_packet_check_v3(struct psmouse *psmouse)
{ struct elantech_data *etd = psmouse->private; staticconst u8 debounce_packet[] = {
0xc4, 0xff, 0xff, 0x02, 0xff, 0xff
}; unsignedchar *packet = psmouse->packet;
/* * check debounce first, it has the same signature in byte 0 * and byte 3 as PACKET_V3_HEAD.
*/ if (!memcmp(packet, debounce_packet, sizeof(debounce_packet))) return PACKET_DEBOUNCE;
/* * If the hardware flag 'crc_enabled' is set the packets have * different signatures.
*/ if (etd->info.crc_enabled) { if ((packet[3] & 0x09) == 0x08) return PACKET_V3_HEAD;
if (etd->tp_dev && (packet[3] & 0x0f) == 0x06) return PACKET_TRACKPOINT;
/* This represents the version of IC body. */
ic_version = (etd->info.fw_version & 0x0f0000) >> 16;
/* * Sanity check based on the constant bits of a packet. * The constant bits change depending on the value of * the hardware flag 'crc_enabled' and the version of * the IC body, but are the same for every packet, * regardless of the type.
*/ if (etd->info.crc_enabled)
sanity_check = ((packet[3] & 0x08) == 0x00); elseif (ic_version == 7 && etd->info.samples[1] == 0x2A)
sanity_check = ((packet[3] & 0x1c) == 0x10); else
sanity_check = ((packet[0] & 0x08) == 0x00 &&
(packet[3] & 0x1c) == 0x10);
if (!sanity_check) return PACKET_UNKNOWN;
switch (packet_type) { case 0: return PACKET_V4_STATUS;
case 1: return PACKET_V4_HEAD;
case 2: return PACKET_V4_MOTION;
}
return PACKET_UNKNOWN;
}
/* * Process byte stream from mouse and handle complete packets
*/ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
{ struct elantech_data *etd = psmouse->private; int packet_type;
if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA;
if (etd->info.debug > 1)
elantech_packet_dump(psmouse);
switch (etd->info.hw_version) { case 1: if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse)) return PSMOUSE_BAD_DATA;
elantech_report_absolute_v1(psmouse); break;
case 2: /* ignore debounce */ if (elantech_debounce_check_v2(psmouse)) return PSMOUSE_FULL_PACKET;
if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse)) return PSMOUSE_BAD_DATA;
elantech_report_absolute_v2(psmouse); break;
case 3:
packet_type = elantech_packet_check_v3(psmouse); switch (packet_type) { case PACKET_UNKNOWN: return PSMOUSE_BAD_DATA;
case PACKET_DEBOUNCE: /* ignore debounce */ break;
case PACKET_TRACKPOINT:
elantech_report_trackpoint(psmouse, packet_type); break;
/* * This writes the reg_07 value again to the hardware at the end of every * set_rate call because the register loses its value. reg_07 allows setting * absolute mode on v4 hardware
*/ staticvoid elantech_set_rate_restore_reg_07(struct psmouse *psmouse, unsignedint rate)
{ struct elantech_data *etd = psmouse->private;
case 3: if (etd->info.set_hw_resolution)
etd->reg_10 = 0x0b; else
etd->reg_10 = 0x01;
if (elantech_write_reg(psmouse, 0x10, etd->reg_10))
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) { /* * Read back reg 0x10. For hardware version 1 we must make * sure the absolute mode bit is set. For hardware version 2 * the touchpad is probably initializing and not ready until * we read back the value we just wrote.
*/ do {
rc = elantech_read_reg(psmouse, 0x10, &val); if (rc == 0) break;
tries--;
elantech_debug("retrying read (%d).\n", tries);
msleep(ETP_READ_BACK_DELAY);
} while (tries > 0);
if (rc) {
psmouse_err(psmouse, "failed to read back register 0x10.\n");
} elseif (etd->info.hw_version == 1 &&
!(val & ETP_R10_ABSOLUTE_MODE)) {
psmouse_err(psmouse, "touchpad refuses to switch to absolute mode.\n");
rc = -1;
}
}
skip_readback_reg_10: if (rc)
psmouse_err(psmouse, "failed to initialise registers.\n");
return rc;
}
/* * (value from firmware) * 10 + 790 = dpi * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
*/ staticunsignedint elantech_convert_res(unsignedint val)
{ return (val * 10 + 790) * 10 / 254;
}
/* * Write a register value by writing a sysfs entry
*/ static ssize_t elantech_set_int_attr(struct psmouse *psmouse, void *data, constchar *buf, size_t count)
{ struct elantech_data *etd = psmouse->private; struct elantech_attr_data *attr = data; unsignedchar *reg = (unsignedchar *) etd + attr->field_offset; unsignedchar value; int err;
err = kstrtou8(buf, 16, &value); if (err) return err;
/* Do we need to preserve some bits for version 2 hardware too? */ if (etd->info.hw_version == 1) { if (attr->reg == 0x10) /* Force absolute mode always on */
value |= ETP_R10_ABSOLUTE_MODE; elseif (attr->reg == 0x11) /* Force 4 byte mode always on */
value |= ETP_R11_4_BYTE_MODE;
}
/* * Some hw_version >= 4 models have a revision higher then 20. Meaning * that param[2] may be 10 or 20, skip the rates check for these.
*/ if ((param[0] & 0x0f) >= 0x06 && (param[1] & 0xaf) == 0x0f &&
param[2] < 40) returntrue;
for (i = 0; i < ARRAY_SIZE(rates); i++) if (param[2] == rates[i]) returnfalse;
returntrue;
}
/* * Use magic knock to detect Elantech touchpad
*/ int elantech_detect(struct psmouse *psmouse, bool set_properties)
{ struct ps2dev *ps2dev = &psmouse->ps2dev; unsignedchar param[3];
/* * Report this in case there are Elantech models that use a different * set of magic numbers
*/ if (param[0] != 0x3c || param[1] != 0x03 ||
(param[2] != 0xc8 && param[2] != 0x00)) {
psmouse_dbg(psmouse, "unexpected magic knock result 0x%02x, 0x%02x, 0x%02x.\n",
param[0], param[1], param[2]); return -1;
}
/* * Query touchpad's firmware version and see if it reports known * value to avoid mis-detection. Logitech mice are known to respond * to Elantech magic knock and there might be more.
*/ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
psmouse_dbg(psmouse, "failed to query firmware version.\n"); return -1;
}
psmouse_dbg(psmouse, "Elantech version query result 0x%02x, 0x%02x, 0x%02x.\n",
param[0], param[1], param[2]);
if (!elantech_is_signature_valid(param)) {
psmouse_dbg(psmouse, "Probably not a real Elantech touchpad. Aborting.\n"); return -1;
}
if (set_properties) {
psmouse->vendor = "Elantech";
psmouse->name = "Touchpad";
}
return 0;
}
/* * Clean up sysfs entries when disconnecting
*/ staticvoid elantech_disconnect(struct psmouse *psmouse)
{ struct elantech_data *etd = psmouse->private;
/* * We might have left a breadcrumb when trying to * set up SMbus companion.
*/
psmouse_smbus_cleanup(psmouse);
if (etd->tp_dev)
input_unregister_device(etd->tp_dev);
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group);
kfree(psmouse->private);
psmouse->private = NULL;
}
/* * Some hw_version 4 models fail to properly activate absolute mode on * resume without going through disable/enable cycle.
*/ staticconststruct dmi_system_id elantech_needs_reenable[] = { #ifdefined(CONFIG_DMI) && defined(CONFIG_X86)
{ /* Lenovo N24 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "81AF"),
},
}, #endif
{ }
};
/* * Put the touchpad back into absolute mode when reconnecting
*/ staticint elantech_reconnect(struct psmouse *psmouse)
{ int err;
psmouse_reset(psmouse);
if (elantech_detect(psmouse, 0)) return -1;
if (dmi_check_system(elantech_needs_reenable)) {
err = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE); if (err)
psmouse_warn(psmouse, "failed to deactivate mouse on %s: %d\n",
psmouse->ps2dev.serio->phys, err);
err = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE); if (err)
psmouse_warn(psmouse, "failed to reactivate mouse on %s: %d\n",
psmouse->ps2dev.serio->phys, err);
}
if (elantech_set_absolute_mode(psmouse)) {
psmouse_err(psmouse, "failed to put touchpad back into absolute mode.\n"); return -1;
}
return 0;
}
/* * Some hw_version 4 models do not work with crc_disabled
*/ staticconststruct dmi_system_id elantech_dmi_force_crc_enabled[] = { #ifdefined(CONFIG_DMI) && defined(CONFIG_X86)
{ /* Fujitsu H730 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
},
},
{ /* Fujitsu H760 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
},
},
{ /* Fujitsu LIFEBOOK E544 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"),
},
},
{ /* Fujitsu LIFEBOOK E546 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E546"),
},
},
{ /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"),
},
},
{ /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
},
},
{ /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
},
},
{ /* Fujitsu LIFEBOOK E557 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E557"),
},
},
{ /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
},
}, #endif
{ }
};
/* * Some hw_version 3 models go into error state when we try to set * bit 3 and/or bit 1 of r10.
*/ staticconststruct dmi_system_id no_hw_res_dmi_table[] = { #ifdefined(CONFIG_DMI) && defined(CONFIG_X86)
{ /* Gigabyte U2442 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
DMI_MATCH(DMI_PRODUCT_NAME, "U2442"),
},
}, #endif
{ }
};
/* * Change Report id 0x5E to 0x5F.
*/ staticint elantech_change_report_id(struct psmouse *psmouse)
{ /* * NOTE: the code is expecting to receive param[] as an array of 3 * items (see __ps2_command()), even if in this case only 2 are * actually needed. Make sure the array size is 3 to avoid potential * stack out-of-bound accesses.
*/ unsignedchar param[3] = { 0x10, 0x03 };
if (elantech_write_reg_params(psmouse, 0x7, param) ||
elantech_read_reg_params(psmouse, 0x7, param) ||
param[0] != 0x10 || param[1] != 0x03) {
psmouse_err(psmouse, "Unable to change report ID to 0x5f.\n"); return -EIO;
}
return 0;
} /* * determine hardware version and set some properties according to it.
*/ staticint elantech_set_properties(struct elantech_device_info *info)
{ /* This represents the version of IC body. */
info->ic_version = (info->fw_version & 0x0f0000) >> 16;
/* Early version of Elan touchpads doesn't obey the rule. */ if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
info->hw_version = 1; else { switch (info->ic_version) { case 2: case 4:
info->hw_version = 2; break; case 5:
info->hw_version = 3; break; case 6 ... 15:
info->hw_version = 4; break; default: return -1;
}
}
/* Get information pattern for hw_version 4 */
info->pattern = 0x00; if (info->ic_version == 0x0f && (info->fw_version & 0xff) <= 0x02)
info->pattern = info->fw_version & 0xff;
/* decide which send_cmd we're gonna use early */
info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
synaptics_send_cmd;
/* Turn on packet checking by default */
info->paritycheck = 1;
/* * This firmware suffers from misreporting coordinates when * a touch action starts causing the mouse cursor or scrolled page * to jump. Enable a workaround.
*/
info->jumpy_cursor =
(info->fw_version == 0x020022 || info->fw_version == 0x020600);
if (info->hw_version > 1) { /* For now show extra debug information */
info->debug = 1;
if (info->fw_version >= 0x020800)
info->reports_pressure = true;
}
/* * The signatures of v3 and v4 packets change depending on the * value of this hardware flag.
*/
info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 ||
dmi_check_system(elantech_dmi_force_crc_enabled);
/* Enable real hardware resolution on hw_version 3 ? */
info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
/* * Do the version query again so we can store the result
*/ if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
psmouse_err(psmouse, "failed to query firmware version.\n"); return -EINVAL;
}
info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
if (elantech_set_properties(info)) {
psmouse_err(psmouse, "unknown hardware version, aborting...\n"); return -EINVAL;
}
psmouse_info(psmouse, "assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
info->hw_version, param[0], param[1], param[2]);
if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
info->capabilities)) {
psmouse_err(psmouse, "failed to query capabilities.\n"); return -EINVAL;
}
psmouse_info(psmouse, "Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
info->capabilities[0], info->capabilities[1],
info->capabilities[2]);
if (info->hw_version != 1) { if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
psmouse_err(psmouse, "failed to query sample data\n"); return -EINVAL;
}
psmouse_info(psmouse, "Elan sample query result %02x, %02x, %02x\n",
info->samples[0],
info->samples[1],
info->samples[2]);
}
if (info->pattern > 0x00 && info->ic_version == 0xf) { if (info->send_cmd(psmouse, ETP_ICBODY_QUERY, ic_body)) {
psmouse_err(psmouse, "failed to query ic body\n"); return -EINVAL;
}
info->ic_version = be16_to_cpup((__be16 *)ic_body);
psmouse_info(psmouse, "Elan ic body: %#04x, current fw version: %#02x\n",
info->ic_version, ic_body[2]);
}
if (info->samples[1] == 0x74 && info->hw_version == 0x03) { /* * This module has a bug which makes absolute mode * unusable, so let's abort so we'll be using standard * PS/2 protocol.
*/
psmouse_info(psmouse, "absolute mode broken, forcing standard PS/2 protocol\n"); return -ENODEV;
}
/* The MSB indicates the presence of the trackpoint */
info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
if (info->has_trackpoint && info->ic_version == 0x0011 &&
(info->product_id == 0x08 || info->product_id == 0x09 ||
info->product_id == 0x0d || info->product_id == 0x0e)) { /* * This module has a bug which makes trackpoint in SMBus * mode return invalid data unless trackpoint is switched * from using 0x5e reports to 0x5f. If we are not able to * make the switch, let's abort initialization so we'll be * using standard PS/2 protocol.
*/ if (elantech_change_report_id(psmouse)) {
psmouse_info(psmouse, "Trackpoint report is broken, forcing standard PS/2 protocol\n"); return -ENODEV;
}
}
info->x_res = 31;
info->y_res = 31; if (info->hw_version == 4) { if (elantech_get_resolution_v4(psmouse,
&info->x_res,
&info->y_res,
&info->bus)) {
psmouse_warn(psmouse, "failed to query resolution data.\n");
}
}
/* query range information */ switch (info->hw_version) { case 1:
info->x_min = ETP_XMIN_V1;
info->y_min = ETP_YMIN_V1;
info->x_max = ETP_XMAX_V1;
info->y_max = ETP_YMAX_V1; break;
case 2: if (info->fw_version == 0x020800 ||
info->fw_version == 0x020b00 ||
info->fw_version == 0x020030) {
info->x_min = ETP_XMIN_V2;
info->y_min = ETP_YMIN_V2;
info->x_max = ETP_XMAX_V2;
info->y_max = ETP_YMAX_V2;
} else { int i; int fixed_dpi;
/* column number of traces */
info->x_traces = traces;
/* row number of traces */
traces = info->capabilities[2]; if ((traces >= 2) && (traces <= info->y_max))
info->y_traces = traces;
break;
}
/* check for the middle button: DMI matching or new v4 firmwares */
info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) ||
(ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) &&
!elantech_is_buttonpad(info));
return 0;
}
#ifdefined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
/* * The newest Elantech device can use a secondary bus (over SMBus) which * provides a better bandwidth and allow a better control of the touchpads. * This is used to decide if we need to use this bus or not.
*/ enum {
ELANTECH_SMBUS_NOT_SET = -1,
ELANTECH_SMBUS_OFF,
ELANTECH_SMBUS_ON,
};
staticint elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
module_param_named(elantech_smbus, elantech_smbus, int, 0644);
MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
staticconstchar * const i2c_blacklist_pnp_ids[] = { /* * These are known to not be working properly as bits are missing * in elan_i2c.
*/
NULL
};
/* * elantech_setup_smbus - called once the PS/2 devices are enumerated * and decides to instantiate a SMBus InterTouch device.
*/ staticint elantech_setup_smbus(struct psmouse *psmouse, struct elantech_device_info *info, bool leave_breadcrumbs)
{ int error;
if (elantech_smbus == ELANTECH_SMBUS_OFF) return -ENXIO;
if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) { /* * New ICs are enabled by default, unless mentioned in * i2c_blacklist_pnp_ids. * Old ICs are up to the user to decide.
*/ if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) ||
psmouse_matches_pnp_id(psmouse, i2c_blacklist_pnp_ids)) return -ENXIO;
}
psmouse_info(psmouse, "Trying to set up SMBus access\n");
error = elantech_create_smbus(psmouse, info, leave_breadcrumbs); if (error) { if (error == -EAGAIN)
psmouse_info(psmouse, "SMbus companion is not ready yet\n"); else
psmouse_err(psmouse, "unable to create intertouch device\n");
switch (info->bus) { case ETP_BUS_PS2_ONLY: /* expected case */ break; case ETP_BUS_SMB_ALERT_ONLY: case ETP_BUS_PS2_SMB_ALERT:
psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n"); break; case ETP_BUS_SMB_HST_NTFY_ONLY: case ETP_BUS_PS2_SMB_HST_NTFY: returntrue; default:
psmouse_dbg(psmouse, "Ignoring SMBus bus provider %d.\n",
info->bus);
}
returnfalse;
}
int elantech_init_smbus(struct psmouse *psmouse)
{ struct elantech_device_info info; int error;
psmouse_reset(psmouse);
error = elantech_query_info(psmouse, &info); if (error) goto init_fail;
int elantech_init(struct psmouse *psmouse)
{ struct elantech_device_info info; int error;
psmouse_reset(psmouse);
error = elantech_query_info(psmouse, &info); if (error) goto init_fail;
#ifdefined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
if (elantech_use_host_notify(psmouse, &info)) { if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
!IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
psmouse_warn(psmouse, "The touchpad can support a better bus than the too old PS/2 protocol. " "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
}
error = elantech_setup_smbus(psmouse, &info, true); if (!error) return PSMOUSE_ELANTECH_SMBUS;
}
#endif/* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
error = elantech_setup_ps2(psmouse, &info); if (error < 0) { /* * Not using any flavor of Elantech support, so clean up * SMbus breadcrumbs, if any.
*/
psmouse_smbus_cleanup(psmouse); goto init_fail;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.