staticunsignedint psmouse_resetafter = 5;
module_param_named(resetafter, psmouse_resetafter, uint, 0644);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");
staticunsignedint psmouse_resync_time;
module_param_named(resync_time, psmouse_resync_time, uint, 0644);
MODULE_PARM_DESC(resync_time, "How long can mouse stay idle before forcing resync (in seconds, 0 = never).");
/* * psmouse_mutex protects all operations changing state of mouse * (connecting, disconnecting, changing rate or resolution via * sysfs). We could use a per-device semaphore but since there * rarely more than one PS/2 mouse connected and since semaphore * is taken in "slow" paths it is not worth it.
*/ static DEFINE_MUTEX(psmouse_mutex);
/* * psmouse_process_byte() analyzes the PS/2 data stream and reports * relevant events to the input module once full packet has arrived.
*/
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
{ struct input_dev *dev = psmouse->dev;
u8 *packet = psmouse->packet; int wheel;
if (psmouse->pktcnt < psmouse->pktsize) return PSMOUSE_GOOD_DATA;
/* Full packet accumulated, process it */
switch (psmouse->protocol->type) { case PSMOUSE_IMPS: /* IntelliMouse has scroll wheel */
input_report_rel(dev, REL_WHEEL, -(s8) packet[3]); break;
case PSMOUSE_IMEX: /* Scroll wheel and buttons on IntelliMouse Explorer */ switch (packet[3] & 0xC0) { case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_WHEEL,
-sign_extend32(packet[3], 5)); break; case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
input_report_rel(dev, REL_HWHEEL,
-sign_extend32(packet[3], 5)); break; case 0x00: case 0xC0:
wheel = sign_extend32(packet[3], 3);
/* * Some A4Tech mice have two scroll wheels, with first * one reporting +/-1 in the lower nibble, and second * one reporting +/-2.
*/ if (psmouse_a4tech_2wheels && abs(wheel) > 1)
input_report_rel(dev, REL_HWHEEL, wheel / 2); else
input_report_rel(dev, REL_WHEEL, -wheel);
case PSMOUSE_GENPS: /* Report scroll buttons on NetMice */
input_report_rel(dev, REL_WHEEL, -(s8) packet[3]);
/* Extra buttons on Genius NewNet 3D */
input_report_key(dev, BTN_SIDE, packet[0] & BIT(6));
input_report_key(dev, BTN_EXTRA, packet[0] & BIT(7)); break;
case PSMOUSE_THINKPS: /* Extra button on ThinkingMouse */
input_report_key(dev, BTN_EXTRA, packet[0] & BIT(3));
/* * Without this bit of weirdness moving up gives wildly * high Y changes.
*/
packet[1] |= (packet[0] & 0x40) << 1; break;
case PSMOUSE_CORTRON: /* * Cortron PS2 Trackball reports SIDE button in the * 4th bit of the first byte.
*/
input_report_key(dev, BTN_SIDE, packet[0] & BIT(3));
packet[0] |= BIT(3); break;
/* * __psmouse_set_state() sets new psmouse state and resets all flags.
*/ staticinlinevoid __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
psmouse->state = new_state;
psmouse->pktcnt = psmouse->out_of_sync_cnt = 0;
psmouse->ps2dev.flags = 0;
psmouse->last = jiffies;
}
/* * psmouse_set_state() sets new psmouse state and resets all flags and * counters while holding serio lock so fighting with interrupt handler * is not a concern.
*/ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
serio_pause_rx(psmouse->ps2dev.serio);
__psmouse_set_state(psmouse, new_state);
serio_continue_rx(psmouse->ps2dev.serio);
}
/* * psmouse_handle_byte() processes one byte of the input data stream * by calling corresponding protocol handler.
*/ staticint psmouse_handle_byte(struct psmouse *psmouse)
{
psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
switch (rc) { case PSMOUSE_BAD_DATA: if (psmouse->state == PSMOUSE_ACTIVATED) {
psmouse_warn(psmouse, "%s at %s lost sync at byte %d\n",
psmouse->name, psmouse->phys,
psmouse->pktcnt); if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
psmouse_notice(psmouse, "issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio); return -EIO;
}
}
psmouse->pktcnt = 0; break;
case PSMOUSE_FULL_PACKET:
psmouse->pktcnt = 0; if (psmouse->out_of_sync_cnt) {
psmouse->out_of_sync_cnt = 0;
psmouse_notice(psmouse, "%s at %s - driver resynced.\n",
psmouse->name, psmouse->phys);
} break;
/* Check if this is a new device announcement (0xAA 0x00) */ if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { if (psmouse->pktcnt == 1) {
psmouse->last = jiffies; return;
}
/* Not a new device, try processing first byte normally */
psmouse->pktcnt = 1; if (psmouse_handle_byte(psmouse)) return;
psmouse->packet[psmouse->pktcnt++] = data;
}
/* * See if we need to force resync because mouse was idle for * too long.
*/ if (psmouse->state == PSMOUSE_ACTIVATED &&
psmouse->pktcnt == 1 && psmouse->resync_time &&
time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
psmouse->badbyte = psmouse->packet[0];
__psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
psmouse_queue_work(psmouse, &psmouse->resync_work, 0); return;
}
/* * Bare PS/2 protocol "detection". Always succeeds.
*/ staticint ps2bare_detect(struct psmouse *psmouse, bool set_properties)
{ if (set_properties) { if (!psmouse->vendor)
psmouse->vendor = "Generic"; if (!psmouse->name)
psmouse->name = "Mouse";
/* * We have no way of figuring true number of buttons so let's * assume that the device has 3.
*/
input_set_capability(psmouse->dev, EV_KEY, BTN_MIDDLE);
}
return 0;
}
/* * Cortron PS/2 protocol detection. There's no special way to detect it, so it * must be forced by sysfs protocol writing.
*/ staticint cortron_detect(struct psmouse *psmouse, bool set_properties)
{ if (set_properties) {
psmouse->vendor = "Cortron";
psmouse->name = "PS/2 Trackball";
for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) {
p = &psmouse_protocols[i];
if ((strlen(p->name) == len && !strncmp(p->name, name, len)) ||
(strlen(p->alias) == len && !strncmp(p->alias, name, len))) return &psmouse_protocols[i];
}
return NULL;
}
/* * Apply default settings to the psmouse structure. Most of them will * be overridden by individual protocol initialization routines.
*/ staticvoid psmouse_apply_defaults(struct psmouse *psmouse)
{ struct input_dev *input_dev = psmouse->dev;
proto = __psmouse_protocol_by_type(type); if (!proto) returnfalse;
if (!psmouse_do_detect(proto->detect, psmouse, proto->try_passthru,
set_properties)) returnfalse;
if (set_properties && proto->init && init_allowed) { if (proto->init(psmouse) != 0) { /* * We detected device, but init failed. Adjust * max_proto so we only try standard protocols.
*/ if (*max_proto > PSMOUSE_IMEX)
*max_proto = PSMOUSE_IMEX;
returnfalse;
}
}
returntrue;
}
/* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have.
*/ staticint psmouse_extensions(struct psmouse *psmouse, unsignedint max_proto, bool set_properties)
{ bool synaptics_hardware = false; int ret;
/* * Always check for focaltech, this is safe as it uses pnp-id * matching.
*/ if (psmouse_do_detect(focaltech_detect,
psmouse, false, set_properties)) { if (max_proto > PSMOUSE_IMEX &&
IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
(!set_properties || focaltech_init(psmouse) == 0)) { return PSMOUSE_FOCALTECH;
} /* * Restrict psmouse_max_proto so that psmouse_initialize() * does not try to reset rate and resolution, because even * that upsets the device. * This also causes us to basically fall through to basic * protocol detection, where we fully reset the mouse, * and set it up as bare PS/2 protocol device.
*/
psmouse_max_proto = max_proto = PSMOUSE_PS2;
}
/* * We always check for LifeBook because it does not disturb mouse * (it only checks DMI information).
*/ if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
set_properties, max_proto > PSMOUSE_IMEX)) return PSMOUSE_LIFEBOOK;
if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
set_properties, max_proto > PSMOUSE_IMEX)) return PSMOUSE_VMMOUSE;
/* * Try Kensington ThinkingMouse (we try first, because Synaptics * probe upsets the ThinkingMouse).
*/ if (max_proto > PSMOUSE_IMEX &&
psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
set_properties, true)) { return PSMOUSE_THINKPS;
}
/* * Try Synaptics TouchPad. Note that probing is done even if * Synaptics protocol support is disabled in config - we need to * know if it is Synaptics so we can reset it properly after * probing for IntelliMouse.
*/ if (max_proto > PSMOUSE_PS2 &&
psmouse_do_detect(synaptics_detect,
psmouse, false, set_properties)) {
synaptics_hardware = true;
if (max_proto > PSMOUSE_IMEX) { /* * Try activating protocol, but check if support is * enabled first, since we try detecting Synaptics * even when protocol is disabled.
*/ if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) { if (!set_properties) return PSMOUSE_SYNAPTICS;
ret = synaptics_init(psmouse); if (ret >= 0) return ret;
}
/* * Some Synaptics touchpads can emulate extended * protocols (like IMPS/2). Unfortunately * Logitech/Genius probes confuse some firmware * versions so we'll have to skip them.
*/
max_proto = PSMOUSE_IMEX;
}
/* * Make sure that touchpad is in relative mode, gestures * (taps) are enabled.
*/
synaptics_reset(psmouse);
}
/* * Try Cypress Trackpad. We must try it before Finger Sensing Pad * because Finger Sensing Pad probe upsets some modules of Cypress * Trackpads.
*/ if (max_proto > PSMOUSE_IMEX &&
psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
set_properties, true)) { return PSMOUSE_CYPRESS;
}
/* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX &&
psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
&max_proto, set_properties, false)) { if (!set_properties) return PSMOUSE_ELANTECH;
ret = elantech_init(psmouse); if (ret >= 0) return ret;
}
if (max_proto > PSMOUSE_IMEX) { if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
&max_proto, set_properties, true)) return PSMOUSE_GENPS;
if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
&max_proto, set_properties, true)) return PSMOUSE_PS2PP;
if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
&max_proto, set_properties, true)) return PSMOUSE_TRACKPOINT;
if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
&max_proto, set_properties, true)) return PSMOUSE_TOUCHKIT_PS2;
}
/* * Try Finger Sensing Pad. We do it here because its probe upsets * Trackpoint devices (causing TP_READ_ID command to time out).
*/ if (max_proto > PSMOUSE_IMEX &&
psmouse_try_protocol(psmouse, PSMOUSE_FSP,
&max_proto, set_properties, true)) { return PSMOUSE_FSP;
}
/* * Reset to defaults in case the device got confused by extended * protocol probes. Note that we follow up with full reset because * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
*/
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
psmouse_reset(psmouse);
/* * Okay, all failed, we have a standard mouse here. The number of * the buttons is still a question, though. We assume 3.
*/
psmouse_try_protocol(psmouse, PSMOUSE_PS2,
&max_proto, set_properties, true);
if (synaptics_hardware) { /* * We detected Synaptics hardware but it did not respond to * IMPS/2 probes. We need to reset the touchpad because if * there is a track point on the pass through port it could * get disabled while probing for protocol extensions.
*/
psmouse_reset(psmouse);
}
return PSMOUSE_PS2;
}
/* * psmouse_probe() probes for a PS/2 mouse.
*/ staticint psmouse_probe(struct psmouse *psmouse)
{ struct ps2dev *ps2dev = &psmouse->ps2dev;
u8 param[2]; int error;
/* * First, we check if it's a mouse. It should send 0x00 or 0x03 in * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer. * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and * subsequent ID queries, probably due to a firmware bug.
*/
param[0] = 0xa5;
error = ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (error) return error;
/* * Then we reset and disable the mouse so that it doesn't generate * events.
*/
error = ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); if (error)
psmouse_warn(psmouse, "Failed to reset mouse on %s: %d\n",
ps2dev->serio->phys, error);
return 0;
}
/* * psmouse_initialize() initializes the mouse to a sane state.
*/ staticvoid psmouse_initialize(struct psmouse *psmouse)
{ /* * We set the mouse report rate, resolution and scaling.
*/ if (psmouse_max_proto != PSMOUSE_PS2) {
psmouse->set_rate(psmouse, psmouse->rate);
psmouse->set_resolution(psmouse, psmouse->resolution);
psmouse->set_scale(psmouse, PSMOUSE_SCALE11);
}
}
/* * psmouse_activate() enables the mouse so that we get motion reports from it.
*/ int psmouse_activate(struct psmouse *psmouse)
{ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
psmouse_warn(psmouse, "Failed to enable mouse on %s\n",
psmouse->ps2dev.serio->phys); return -1;
}
/* * psmouse_deactivate() puts the mouse into poll mode so that we don't get * motion reports from it unless we explicitly request it.
*/ int psmouse_deactivate(struct psmouse *psmouse)
{ int error;
error = ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE); if (error) {
psmouse_warn(psmouse, "Failed to deactivate mouse on %s: %d\n",
psmouse->ps2dev.serio->phys, error); return error;
}
/* * Some mice don't ACK commands sent while they are in the middle of * transmitting motion packet. To avoid delay we use ps2_sendbyte() * instead of ps2_command() which would wait for 200ms for an ACK * that may never come. * As an additional quirk ALPS touchpads may not only forget to ACK * disable command but will stop reporting taps, so if we see that * mouse at least once ACKs disable we will do full reconnect if ACK * is missing.
*/
psmouse->num_resyncs++;
if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) { if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command)
failed = true;
} else
psmouse->acks_disable_command = true;
/* * Poll the mouse. If it was reset the packet will be shorter than * psmouse->pktsize and ps2_command will fail. We do not expect and * do not handle scenario when mouse "upgrades" its protocol while * disconnected since it would require additional delay. If we ever * see a mouse that does it we'll adjust the code.
*/ if (!failed) { if (psmouse->poll(psmouse))
failed = true; else {
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); for (i = 0; i < psmouse->pktsize; i++) {
psmouse->pktcnt++;
rc = psmouse->protocol_handler(psmouse); if (rc != PSMOUSE_GOOD_DATA) break;
} if (rc != PSMOUSE_FULL_PACKET)
failed = true;
psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
}
}
/* * Now try to enable mouse. We try to do that even if poll failed * and also repeat our attempts 5 times, otherwise we may be left * out with disabled mouse.
*/ for (i = 0; i < 5; i++) { if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
enabled = true; break;
}
msleep(200);
}
if (!enabled) {
psmouse_warn(psmouse, "failed to re-enable mouse on %s\n",
psmouse->ps2dev.serio->phys);
failed = true;
}
/* * Disable stream mode so cleanup routine can proceed undisturbed.
*/ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
psmouse_warn(psmouse, "Failed to disable mouse on %s\n",
psmouse->ps2dev.serio->phys);
if (psmouse->cleanup)
psmouse->cleanup(psmouse);
/* * Reset the mouse to defaults (bare PS/2 protocol).
*/
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
/* * Some boxes, such as HP nx7400, get terribly confused if mouse * is not fully enabled before suspending/shutting down.
*/
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
if (parent) { if (parent->pt_deactivate)
parent->pt_deactivate(parent);
/* * If mouse's packet size is 3 there is no point in polling the * device in hopes to detect protocol reset - we won't get less * than 3 bytes response anyhow.
*/ if (psmouse->pktsize == 3)
psmouse->resync_time = 0;
/* * Some smart KVMs fake response to POLL command returning just * 3 bytes and messing up our resync logic, so if initial poll * fails we won't try polling the device anymore. Hopefully * such KVM will maintain initially selected protocol.
*/ if (psmouse->resync_time && psmouse->poll(psmouse))
psmouse->resync_time = 0;
/* * psmouse_connect() is a callback from the serio module when * an unhandled serio port is found.
*/ staticint psmouse_connect(struct serio *serio, struct serio_driver *drv)
{ struct psmouse *psmouse, *parent = NULL; struct input_dev *input_dev; int retval = 0, error = -ENOMEM;
mutex_lock(&psmouse_mutex);
/* * If this is a pass-through port deactivate parent so the device * connected to this port can be successfully identified
*/ if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
parent = psmouse_from_serio(serio->parent);
psmouse_deactivate(parent);
}
if (!psmouse->protocol->smbus_companion) {
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
error = input_register_device(input_dev); if (error) goto err_protocol_disconnect;
} else { /* Smbus companion will be reporting events, not us. */
input_free_device(input_dev);
psmouse->dev = input_dev = NULL;
}
if (parent && parent->pt_activate)
parent->pt_activate(parent);
/* * PS/2 devices having SMBus companions should stay disabled * on PS/2 side, in order to have SMBus part operable.
*/ if (!psmouse->protocol->smbus_companion)
psmouse_activate(psmouse);
out: /* If this is a pass-through port the parent needs to be re-activated */ if (parent)
psmouse_activate(parent);
if (reconnect_handler) { if (reconnect_handler(psmouse)) goto out;
} else {
psmouse_reset(psmouse);
if (psmouse_probe(psmouse) < 0) goto out;
type = psmouse_extensions(psmouse, psmouse_max_proto, false); if (psmouse->protocol->type != type) goto out;
}
/* * OK, the device type (and capabilities) match the old one, * we can continue using it, complete initialization
*/ if (!psmouse->protocol->smbus_companion) {
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
}
if (parent && parent->pt_activate)
parent->pt_activate(parent);
/* * PS/2 devices having SMBus companions should stay disabled * on PS/2 side, in order to have SMBus part operable.
*/ if (!psmouse->protocol->smbus_companion)
psmouse_activate(psmouse);
rc = 0;
out: /* If this is a pass-through port the parent waits to be activated */ if (parent)
psmouse_activate(parent);
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.