cregit-Linux how code gets into the kernel

Release 4.16 drivers/input/mouse/psmouse-base.c

/*
 * PS/2 mouse driver
 *
 * Copyright (c) 1999-2002 Vojtech Pavlik
 * Copyright (c) 2003-2004 Dmitry Torokhov
 */

/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 */


#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt

#define psmouse_fmt(fmt)	fmt

#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
#include <linux/libps2.h>
#include <linux/mutex.h>

#include "psmouse.h"
#include "synaptics.h"
#include "logips2pp.h"
#include "alps.h"
#include "hgpk.h"
#include "lifebook.h"
#include "trackpoint.h"
#include "touchkit_ps2.h"
#include "elantech.h"
#include "sentelic.h"
#include "cypress_ps2.h"
#include "focaltech.h"
#include "vmmouse.h"
#include "byd.h"


#define DRIVER_DESC	"PS/2 mouse driver"

MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");


static unsigned int psmouse_max_proto = PSMOUSE_AUTO;
static int psmouse_set_maxproto(const char *val, const struct kernel_param *);
static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp);

static const struct kernel_param_ops param_ops_proto_abbrev = {
	.set = psmouse_set_maxproto,
	.get = psmouse_get_maxproto,
};

#define param_check_proto_abbrev(name, p)	__param_check(name, p, unsigned int)
module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644);
MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches.");


static unsigned int psmouse_resolution = 200;
module_param_named(resolution, psmouse_resolution, uint, 0644);
MODULE_PARM_DESC(resolution, "Resolution, in dpi.");


static unsigned int psmouse_rate = 100;
module_param_named(rate, psmouse_rate, uint, 0644);
MODULE_PARM_DESC(rate, "Report rate, in reports per second.");


static bool psmouse_smartscroll = true;
module_param_named(smartscroll, psmouse_smartscroll, bool, 0644);
MODULE_PARM_DESC(smartscroll, "Logitech Smartscroll autorepeat, 1 = enabled (default), 0 = disabled.");


static unsigned int psmouse_resetafter = 5;
module_param_named(resetafter, psmouse_resetafter, uint, 0644);
MODULE_PARM_DESC(resetafter, "Reset device after so many bad packets (0 = never).");


static unsigned int 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_DEFINE_ATTR(protocol, S_IWUSR | S_IRUGO,
			NULL,
			psmouse_attr_show_protocol, psmouse_attr_set_protocol);
PSMOUSE_DEFINE_ATTR(rate, S_IWUSR | S_IRUGO,
			(void *) offsetof(struct psmouse, rate),
			psmouse_show_int_attr, psmouse_attr_set_rate);
PSMOUSE_DEFINE_ATTR(resolution, S_IWUSR | S_IRUGO,
			(void *) offsetof(struct psmouse, resolution),
			psmouse_show_int_attr, psmouse_attr_set_resolution);
PSMOUSE_DEFINE_ATTR(resetafter, S_IWUSR | S_IRUGO,
			(void *) offsetof(struct psmouse, resetafter),
			psmouse_show_int_attr, psmouse_set_int_attr);
PSMOUSE_DEFINE_ATTR(resync_time, S_IWUSR | S_IRUGO,
			(void *) offsetof(struct psmouse, resync_time),
			psmouse_show_int_attr, psmouse_set_int_attr);


static struct attribute *psmouse_attributes[] = {
	&psmouse_attr_protocol.dattr.attr,
	&psmouse_attr_rate.dattr.attr,
	&psmouse_attr_resolution.dattr.attr,
	&psmouse_attr_resetafter.dattr.attr,
	&psmouse_attr_resync_time.dattr.attr,
	NULL
};


static const struct attribute_group psmouse_attribute_group = {
	.attrs	= psmouse_attributes,
};

/*
 * 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);


static struct workqueue_struct *kpsmoused_wq;


static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) { input_report_key(dev, BTN_LEFT, buttons & BIT(0)); input_report_key(dev, BTN_MIDDLE, buttons & BIT(2)); input_report_key(dev, BTN_RIGHT, buttons & BIT(1)); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Tissoires56100.00%1100.00%
Total56100.00%1100.00%

/* * 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; unsigned char *packet = psmouse->packet; 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, -(signed char) 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, (int) (packet[3] & 32) - (int) (packet[3] & 31)); break; case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */ input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31)); break; case 0x00: case 0xC0: input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7)); input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1); input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1); break; } break; case PSMOUSE_GENPS: /* Report scroll buttons on NetMice */ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); /* Extra buttons on Genius NewNet 3D */ input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1); input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1); break; case PSMOUSE_THINKPS: /* Extra button on ThinkingMouse */ input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1); /* * 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] >> 3) & 1); packet[0] |= 0x08; break; default: break; } /* Generic PS/2 Mouse */ psmouse_report_standard_buttons(dev, packet[0] | psmouse->extra_buttons); input_report_rel(dev, REL_X, packet[1] ? (int) packet[1] - (int) ((packet[0] << 4) & 0x100) : 0); input_report_rel(dev, REL_Y, packet[2] ? (int) ((packet[0] << 3) & 0x100) - (int) packet[2] : 0); input_sync(dev); return PSMOUSE_FULL_PACKET; }

Contributors

PersonTokensPropCommitsCommitProp
Vojtech Pavlik24352.15%538.46%
Pozsar Balazs9019.31%17.69%
Dmitry Torokhov6614.16%430.77%
Peter Maydell357.51%17.69%
Aristeu Rozanski275.79%17.69%
Benjamin Tissoires51.07%17.69%
Total466100.00%13100.00%


void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work, unsigned long delay) { queue_delayed_work(kpsmoused_wq, work, delay); }

Contributors

PersonTokensPropCommitsCommitProp
Andres Salomon28100.00%1100.00%
Total28100.00%1100.00%

/* * __psmouse_set_state() sets new psmouse state and resets all flags. */
static inline void __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; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4086.96%266.67%
Vojtech Pavlik613.04%133.33%
Total46100.00%3100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov3282.05%250.00%
Vojtech Pavlik717.95%250.00%
Total39100.00%4100.00%

/* * psmouse_handle_byte() processes one byte of the input data stream * by calling corresponding protocol handler. */
static int 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 -1; } } 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; case PSMOUSE_GOOD_DATA: break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov14796.71%675.00%
Peter Osterlund31.97%112.50%
Vojtech Pavlik21.32%112.50%
Total152100.00%8100.00%


static void psmouse_handle_oob_data(struct psmouse *psmouse, u8 data) { switch (psmouse->oob_data_type) { case PSMOUSE_OOB_NONE: psmouse->oob_data_type = data; break; case PSMOUSE_OOB_EXTRA_BTNS: psmouse_report_standard_buttons(psmouse->dev, data); input_sync(psmouse->dev); psmouse->extra_buttons = data; psmouse->oob_data_type = PSMOUSE_OOB_NONE; break; default: psmouse_warn(psmouse, "unknown OOB_DATA type: 0x%02x\n", psmouse->oob_data_type); psmouse->oob_data_type = PSMOUSE_OOB_NONE; break; } }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Tissoires83100.00%1100.00%
Total83100.00%1100.00%

/* * psmouse_interrupt() handles incoming characters, either passing them * for normal processing or gathering them as command response. */
static irqreturn_t psmouse_interrupt(struct serio *serio, unsigned char data, unsigned int flags) { struct psmouse *psmouse = serio_get_drvdata(serio); if (psmouse->state == PSMOUSE_IGNORE) goto out; if (unlikely((flags & SERIO_TIMEOUT) || ((flags & SERIO_PARITY) && !psmouse->protocol->ignore_parity))) { if (psmouse->state == PSMOUSE_ACTIVATED) psmouse_warn(psmouse, "bad data from KBC -%s%s\n", flags & SERIO_TIMEOUT ? " timeout" : "", flags & SERIO_PARITY ? " bad parity" : ""); ps2_cmd_aborted(&psmouse->ps2dev); goto out; } if (flags & SERIO_OOB_DATA) { psmouse_handle_oob_data(psmouse, data); goto out; } if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_ACK)) if (ps2_handle_ack(&psmouse->ps2dev, data)) goto out; if (unlikely(psmouse->ps2dev.flags & PS2_FLAG_CMD)) if (ps2_handle_response(&psmouse->ps2dev, data)) goto out; if (psmouse->state <= PSMOUSE_RESYNCING) goto out; if (psmouse->state == PSMOUSE_ACTIVATED && psmouse->pktcnt && time_after(jiffies, psmouse->last + HZ/2)) { psmouse_info(psmouse, "%s at %s lost synchronization, throwing %d bytes away.\n", psmouse->name, psmouse->phys, psmouse->pktcnt); psmouse->badbyte = psmouse->packet[0]; __psmouse_set_state(psmouse, PSMOUSE_RESYNCING); psmouse_queue_work(psmouse, &psmouse->resync_work, 0); goto out; } psmouse->packet[psmouse->pktcnt++] = data; /* 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; goto out; } if (psmouse->packet[1] == PSMOUSE_RET_ID || (psmouse->protocol->type == PSMOUSE_HGPK && psmouse->packet[1] == PSMOUSE_RET_BAT)) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); serio_reconnect(serio); goto out; } /* Not a new device, try processing first byte normally */ psmouse->pktcnt = 1; if (psmouse_handle_byte(psmouse)) goto out; 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); goto out; } psmouse->last = jiffies; psmouse_handle_byte(psmouse); out: return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov29560.33%1458.33%
Vojtech Pavlik9619.63%416.67%
Peter Osterlund489.82%14.17%
Benjamin Tissoires183.68%14.17%
Zephaniah E. Hull173.48%14.17%
Andres Salomon81.64%14.17%
Andrew Morton61.23%14.17%
Linus Torvalds10.20%14.17%
Total489100.00%24100.00%

/* * psmouse_sliced_command() sends an extended PS/2 command to the mouse * using sliced syntax, understood by advanced devices, such as Logitech * or Synaptics touchpads. The command is encoded as: * 0xE6 0xE8 rr 0xE8 ss 0xE8 tt 0xE8 uu where (rr*64)+(ss*16)+(tt*4)+uu * is the command. */
int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command) { int i; if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) return -1; for (i = 6; i >= 0; i -= 2) { unsigned char d = (command >> i) & 3; if (ps2_command(&psmouse->ps2dev, &d, PSMOUSE_CMD_SETRES)) return -1; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov85100.00%2100.00%
Total85100.00%2100.00%

/* * psmouse_reset() resets the mouse into power-on state. */
int psmouse_reset(struct psmouse *psmouse) { unsigned char param[2]; if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_RESET_BAT)) return -1; if (param[0] != PSMOUSE_RET_BAT && param[1] != PSMOUSE_RET_ID) return -1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov58100.00%2100.00%
Total58100.00%2100.00%

/* * Here we set the mouse resolution. */
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution) { static const unsigned char params[] = { 0, 1, 2, 2, 3 }; unsigned char p; if (resolution == 0 || resolution > 200) resolution = 200; p = params[resolution / 50]; ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES); psmouse->resolution = 25 << p; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov81100.00%1100.00%
Total81100.00%1100.00%

/* * Here we set the mouse report rate. */
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate) { static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 }; unsigned char r; int i = 0; while (rates[i] > rate) i++; r = rates[i]; ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE); psmouse->rate = r; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov87100.00%1100.00%
Total87100.00%1100.00%

/* * Here we set the mouse scaling. */
static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale) { ps2_command(&psmouse->ps2dev, NULL, scale == PSMOUSE_SCALE21 ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11); }

Contributors

PersonTokensPropCommitsCommitProp
Mathias Gottschlag33100.00%1100.00%
Total33100.00%1100.00%

/* * psmouse_poll() - default poll handler. Everyone except for ALPS uses it. */
static int psmouse_poll(struct psmouse *psmouse) { return ps2_command(&psmouse->ps2dev, psmouse->packet, PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov34100.00%1100.00%
Total34100.00%1100.00%


static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) { int i; for (i = 0; ids[i]; i++) if (!strcasecmp(id, ids[i])) return true; return false; }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede3259.26%150.00%
Dmitry Torokhov2240.74%150.00%
Total54100.00%2100.00%

/* * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids. */
bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) { struct serio *serio = psmouse->ps2dev.serio; char *p, *fw_id_copy, *save_ptr; bool found = false; if (strncmp(serio->firmware_id, "PNP: ", 5)) return false; fw_id_copy = kstrndup(&serio->firmware_id[5], sizeof(serio->firmware_id) - 5, GFP_KERNEL); if (!fw_id_copy) return false; save_ptr = fw_id_copy; while ((p = strsep(&fw_id_copy, " ")) != NULL) { if (psmouse_check_pnp_id(p, ids)) { found = true; break; } } kfree(save_ptr); return found; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov12087.59%150.00%
Hans de Goede1712.41%150.00%
Total137100.00%2100.00%

/* * Genius NetMouse magic init. */
static int genius_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; param[0] = 3; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11); ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO); if (param[0] != 0x00 || param[1] != 0x33 || param[2] != 0x55) return -1; if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_EXTRA, psmouse->dev->keybit); __set_bit(BTN_SIDE, psmouse->dev->keybit); __set_bit(REL_WHEEL, psmouse->dev->relbit); psmouse->vendor = "Genius"; psmouse->name = "Mouse"; psmouse->pktsize = 4; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov10859.67%777.78%
Vojtech Pavlik7340.33%222.22%
Total181100.00%9100.00%

/* * IntelliMouse magic init. */
static int intellimouse_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 100; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 80; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 3) return -1; if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(REL_WHEEL, psmouse->dev->relbit); if (!psmouse->vendor) psmouse->vendor = "Generic"; if (!psmouse->name) psmouse->name = "Wheel Mouse"; psmouse->pktsize = 4; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov12173.78%555.56%
Vojtech Pavlik4326.22%444.44%
Total164100.00%9100.00%

/* * Try IntelliMouse/Explorer magic init. */
static int im_explorer_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; intellimouse_detect(psmouse, 0); param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 80; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 4) return -1; /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */ param[0] = 200; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 80; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 40; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(REL_WHEEL, psmouse->dev->relbit); __set_bit(REL_HWHEEL, psmouse->dev->relbit); __set_bit(BTN_SIDE, psmouse->dev->keybit); __set_bit(BTN_EXTRA, psmouse->dev->keybit); if (!psmouse->vendor) psmouse->vendor = "Generic"; if (!psmouse->name) psmouse->name = "Explorer Mouse"; psmouse->pktsize = 4; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov12449.01%550.00%
Vojtech Pavlik7027.67%440.00%
Pozsar Balazs5923.32%110.00%
Total253100.00%10100.00%

/* * Kensington ThinkingMouse / ExpertMouse magic init. */
static int thinking_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 }; int i; param[0] = 10; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); param[0] = 0; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES); for (i = 0; i < ARRAY_SIZE(seq); i++) { param[0] = seq[i]; ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE); } ps2_command(ps2dev, param, PSMOUSE_CMD_GETID); if (param[0] != 2) return -1; if (set_properties) { __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_EXTRA, psmouse->dev->keybit); psmouse->vendor = "Kensington"; psmouse->name = "ThinkingMouse"; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Peter Maydell10352.82%114.29%
Dmitry Torokhov7236.92%571.43%
Helge Deller2010.26%114.29%
Total195100.00%7100.00%

/* * Bare PS/2 protocol "detection". Always succeeds. */
static int 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. */ __set_bit(BTN_MIDDLE, psmouse->dev->keybit); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov61100.00%5100.00%
Total61100.00%5100.00%

/* * Cortron PS/2 protocol detection. There's no special way to detect it, so it * must be forced by sysfs protocol writing. */
static int cortron_detect(struct psmouse *psmouse, bool set_properties) { if (set_properties) { psmouse->vendor = "Cortron"; psmouse->name = "PS/2 Trackball"; __set_bit(BTN_MIDDLE, psmouse->dev->keybit); __set_bit(BTN_SIDE, psmouse->dev->keybit); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aristeu Rozanski4477.19%133.33%
Dmitry Torokhov1322.81%266.67%
Total57100.00%3100.00%

static const struct psmouse_protocol psmouse_protocols[] = { { .type = PSMOUSE_PS2, .name = "PS/2", .alias = "bare", .maxproto = true, .ignore_parity = true, .detect = ps2bare_detect, .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP { .type = PSMOUSE_PS2PP, .name = "PS2++", .alias = "logitech", .detect = ps2pp_detect, }, #endif { .type = PSMOUSE_THINKPS, .name = "ThinkPS/2", .alias = "thinkps", .detect = thinking_detect, }, #ifdef CONFIG_MOUSE_PS2_CYPRESS { .type = PSMOUSE_CYPRESS, .name = "CyPS/2", .alias = "cypress", .detect = cypress_detect, .init = cypress_init, }, #endif { .type = PSMOUSE_GENPS, .name = "GenPS/2", .alias = "genius", .detect = genius_detect, }, { .type = PSMOUSE_IMPS, .name = "ImPS/2", .alias = "imps", .maxproto = true, .ignore_parity = true, .detect = intellimouse_detect, .try_passthru = true, }, { .type = PSMOUSE_IMEX, .name = "ImExPS/2", .alias = "exps", .maxproto = true, .ignore_parity = true, .detect = im_explorer_detect, .try_passthru = true, }, #ifdef CONFIG_MOUSE_PS2_SYNAPTICS { .type = PSMOUSE_SYNAPTICS, .name = "SynPS/2", .alias = "synaptics", .detect = synaptics_detect, .init = synaptics_init_absolute, }, { .type = PSMOUSE_SYNAPTICS_RELATIVE, .name = "SynRelPS/2", .alias = "synaptics-relative", .detect = synaptics_detect, .init = synaptics_init_relative, }, #endif #ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS { .type = PSMOUSE_SYNAPTICS_SMBUS, .name = "SynSMBus", .alias = "synaptics-smbus", .detect = synaptics_detect, .init = synaptics_init_smbus, .smbus_companion = true, }, #endif #ifdef CONFIG_MOUSE_PS2_ALPS { .type = PSMOUSE_ALPS, .name = "AlpsPS/2", .alias = "alps", .detect = alps_detect, .init = alps_init, }, #endif #ifdef CONFIG_MOUSE_PS2_LIFEBOOK { .type = PSMOUSE_LIFEBOOK, .name = "LBPS/2", .alias = "lifebook", .detect = lifebook_detect, .init = lifebook_init, }, #endif #ifdef CONFIG_MOUSE_PS2_TRACKPOINT { .type = PSMOUSE_TRACKPOINT, .name = "TPPS/2", .alias = "trackpoint", .detect = trackpoint_detect, .try_passthru = true, }, #endif #ifdef CONFIG_MOUSE_PS2_TOUCHKIT { .type = PSMOUSE_TOUCHKIT_PS2, .name = "touchkitPS/2", .alias = "touchkit", .detect = touchkit_ps2_detect, }, #endif #ifdef CONFIG_MOUSE_PS2_OLPC { .type = PSMOUSE_HGPK, .name = "OLPC HGPK", .alias = "hgpk", .detect = hgpk_detect, }, #endif #ifdef CONFIG_MOUSE_PS2_ELANTECH { .type = PSMOUSE_ELANTECH, .name = "ETPS/2", .alias = "elantech", .detect = elantech_detect, .init = elantech_init, }, #endif #ifdef CONFIG_MOUSE_PS2_SENTELIC { .type = PSMOUSE_FSP, .name = "FSPPS/2", .alias = "fsp", .detect = fsp_detect, .init = fsp_init, }, #endif { .type = PSMOUSE_CORTRON, .name = "CortronPS/2", .alias = "cortps", .detect = cortron_detect, }, #ifdef CONFIG_MOUSE_PS2_FOCALTECH { .type = PSMOUSE_FOCALTECH, .name = "FocalTechPS/2", .alias = "focaltech", .detect = focaltech_detect, .init = focaltech_init, }, #endif #ifdef CONFIG_MOUSE_PS2_VMMOUSE { .type = PSMOUSE_VMMOUSE, .name = VMMOUSE_PSNAME, .alias = "vmmouse", .detect = vmmouse_detect, .init = vmmouse_init, }, #endif #ifdef CONFIG_MOUSE_PS2_BYD { .type = PSMOUSE_BYD, .name = "BYDPS/2", .alias = "byd", .detect = byd_detect, .init = byd_init, }, #endif { .type = PSMOUSE_AUTO, .name = "auto", .alias = "any", .maxproto = true, }, };
static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type) { int i; for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) if (psmouse_protocols[i].type == type) return &psmouse_protocols[i]; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov5298.11%375.00%
Stephen Evanchik11.89%125.00%
Total53100.00%4100.00%


static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type) { const struct psmouse_protocol *proto; proto = __psmouse_protocol_by_type(type); if (proto) return proto; WARN_ON(1); return &psmouse_protocols[0]; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov45100.00%2100.00%
Total45100.00%2100.00%


static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len) { const struct psmouse_protocol *p; int i; 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; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov10797.27%150.00%
Stephen Evanchik32.73%150.00%
Total110100.00%2100.00%

/* * Apply default settings to the psmouse structure. Most of them will * be overridden by individual protocol initialization routines. */
static void psmouse_apply_defaults(struct psmouse *psmouse) { struct input_dev *input_dev = psmouse->dev; memset(input_dev->evbit, 0, sizeof(input_dev->evbit)); memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); memset(input_dev->relbit, 0, sizeof(input_dev->relbit)); memset(input_dev->absbit, 0, sizeof(input_dev->absbit)); memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit)); __set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_REL, input_dev->evbit); __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_RIGHT, input_dev->keybit); __set_bit(REL_X, input_dev->relbit); __set_bit(REL_Y, input_dev->relbit); __set_bit(INPUT_PROP_POINTER, input_dev->propbit); psmouse->protocol = &psmouse_protocols[0]; psmouse->set_rate = psmouse_set_rate; psmouse->set_resolution = psmouse_set_resolution; psmouse->set_scale = psmouse_set_scale; psmouse->poll = psmouse_poll; psmouse->protocol_handler = psmouse_process_byte; psmouse->pktsize = 3; psmouse->reconnect = NULL; psmouse->fast_reconnect = NULL; psmouse->disconnect = NULL; psmouse->cleanup = NULL; psmouse->pt_activate = NULL; psmouse->pt_deactivate = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov23395.10%342.86%
Andres Salomon41.63%114.29%
Arjan Opmeer31.22%114.29%
Stefan Lucke31.22%114.29%
Stephen Evanchik20.82%114.29%
Total245100.00%7100.00%


static bool psmouse_do_detect(int (*detect)(struct psmouse *, bool), struct psmouse *psmouse, bool allow_passthrough, bool set_properties) { if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU && !allow_passthrough) { return false; } if (set_properties) psmouse_apply_defaults(psmouse); return detect(psmouse, set_properties) == 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov71100.00%1100.00%
Total71100.00%1100.00%


static bool psmouse_try_protocol(struct psmouse *psmouse, enum psmouse_type type, unsigned int *max_proto, bool set_properties, bool init_allowed) { const struct psmouse_protocol *proto; proto = __psmouse_protocol_by_type(type); if (!proto) return false; if (!psmouse_do_detect(proto->detect, psmouse, proto->try_passthru, set_properties)) return false; 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; return false; } } return true; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov11098.21%480.00%
Tai-hwa Liang21.79%120.00%
Total112100.00%5100.00%

/* * psmouse_extensions() probes for any extensions to the basic PS/2 protocol * the mouse may have. */
static int psmouse_extensions(struct psmouse *psmouse, unsigned int 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 ALPS TouchPad */ if (max_proto > PSMOUSE_IMEX) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS, &max_proto, set_properties, true)) return PSMOUSE_ALPS; } /* Try OLPC HGPK touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto, set_properties, true)) { return PSMOUSE_HGPK; } /* Try Elantech touchpad */ if (max_proto > PSMOUSE_IMEX && psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH, &max_proto, set_properties, true)) { return PSMOUSE_ELANTECH; } 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); if (max_proto >= PSMOUSE_IMEX && psmouse_try_protocol(psmouse, PSMOUSE_IMEX, &max_proto, set_properties, true)) { return PSMOUSE_IMEX; } if (max_proto >= PSMOUSE_IMPS && psmouse_try_protocol(psmouse, PSMOUSE_IMPS, &max_proto, set_properties, true)) { return PSMOUSE_IMPS; } /* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov52494.58%457.14%
Benjamin Tissoires244.33%114.29%
Mathias Gottschlag50.90%114.29%
Stefan Lucke10.18%114.29%
Total554100.00%7100.00%

/* * psmouse_probe() probes for a PS/2 mouse. */
static int psmouse_probe(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[2]; /* * 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; if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID)) return -1; if (param[0] != 0x00 && param[0] != 0x03 && param[0] != 0x04 && param[0] != 0xff) return -1; /* * Then we reset and disable the mouse so that it doesn't generate * events. */ if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS)) psmouse_warn(psmouse, "Failed to reset mouse on %s\n", ps2dev->serio->phys); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov9785.84%555.56%
Vojtech Pavlik119.73%333.33%
Helge Deller54.42%111.11%
Total113100.00%9100.00%

/* * psmouse_initialize() initializes the mouse to a sane state. */
static void 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); } }

Contributors

PersonTokensPropCommitsCommitProp
Vojtech Pavlik2447.06%436.36%
Dmitry Torokhov1937.25%436.36%
Mathias Gottschlag47.84%19.09%
Linus Torvalds35.88%19.09%
Peter Osterlund11.96%19.09%
Total51100.00%11100.00%

/* * 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_set_state(psmouse, PSMOUSE_ACTIVATED); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4581.82%375.00%
Andres Salomon1018.18%125.00%
Total55100.00%4100.00%

/* * 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) { if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) { psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n", psmouse->ps2dev.serio->phys); return -1; } psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4581.82%480.00%
Andres Salomon1018.18%120.00%
Total55100.00%5100.00%

/* * psmouse_resync() attempts to re-validate current protocol. */
static void psmouse_resync(struct work_struct *work) { struct psmouse *parent = NULL, *psmouse = container_of(work, struct psmouse, resync_work.work); struct serio *serio = psmouse->ps2dev.serio; psmouse_ret_t rc = PSMOUSE_GOOD_DATA; bool failed = false, enabled = false; int i; mutex_lock(&psmouse_mutex); if (psmouse->state != PSMOUSE_RESYNCING) goto out; if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } /* * 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; } if (failed) { psmouse_set_state(psmouse, PSMOUSE_IGNORE); psmouse_info(psmouse, "resync failed, issuing reconnect request\n"); serio_reconnect(serio); } else psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); if (parent) psmouse_activate(parent); out: mutex_unlock(&psmouse_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov31587.99%861.54%
Vojtech Pavlik205.59%17.69%
David Howells154.19%17.69%
Ingo Molnar41.12%17.69%
Andres Salomon20.56%17.69%
Peter Osterlund20.56%17.69%
Total358100.00%13100.00%

/* * psmouse_cleanup() resets the mouse into power-on state. */
static void psmouse_cleanup(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *parent = NULL; mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); /* * 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); psmouse_activate(parent); } mutex_unlock(&psmouse_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov170100.00%6100.00%
Total170100.00%6100.00%

/* * psmouse_disconnect() closes and frees. */
static void psmouse_disconnect(struct serio *serio) { struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *parent = NULL; sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); mutex_lock(&psmouse_mutex); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); /* make sure we don't have a resync in progress */ mutex_unlock(&psmouse_mutex); flush_workqueue(kpsmoused_wq); mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } if (psmouse->disconnect) psmouse->disconnect(psmouse); if (parent && parent->pt_deactivate) parent->pt_deactivate(parent); psmouse_set_state(psmouse, PSMOUSE_IGNORE); serio_close(serio); serio_set_drvdata(serio, NULL); if (psmouse->dev) input_unregister_device(psmouse->dev); kfree(psmouse); if (parent) psmouse_activate(parent); mutex_unlock(&psmouse_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov16992.86%770.00%
Ingo Molnar84.40%110.00%
Vojtech Pavlik52.75%220.00%
Total182100.00%10100.00%


static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto) { const struct psmouse_protocol *selected_proto; struct input_dev *input_dev = psmouse->dev; enum psmouse_type type; input_dev->dev.parent = &psmouse->ps2dev.serio->dev; if (proto && (proto->detect || proto->init)) { psmouse_apply_defaults(psmouse); if (proto->detect && proto->detect(psmouse, true) < 0) return -1; if (proto->init && proto->init(psmouse) < 0) return -1; selected_proto = proto; } else { type = psmouse_extensions(psmouse, psmouse_max_proto, true); selected_proto = psmouse_protocol_by_type(type); } psmouse->protocol = selected_proto; /* * 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; snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s", selected_proto->name, psmouse->vendor, psmouse->name); input_dev->name = psmouse->devname; input_dev->phys = psmouse->phys; input_dev->id.bustype = BUS_I8042; input_dev->id.vendor = 0x0002; input_dev->id.product = psmouse->protocol->type; input_dev->id.version = psmouse->model; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov24693.54%1785.00%
Vojtech Pavlik114.18%15.00%
Peter Osterlund51.90%15.00%
Helge Deller10.38%15.00%
Total263100.00%20100.00%

/* * psmouse_connect() is a callback from the serio module when * an unhandled serio port is found. */
static int 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 = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } psmouse = kzalloc(sizeof(struct psmouse), GFP_KERNEL); input_dev = input_allocate_device(); if (!psmouse || !input_dev) goto err_free; ps2_init(&psmouse->ps2dev, serio); INIT_DELAYED_WORK(&psmouse->resync_work, psmouse_resync); psmouse->dev = input_dev; snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys); psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); serio_set_drvdata(serio, psmouse); error = serio_open(serio, drv); if (error) goto err_clear_drvdata; /* give PT device some time to settle down before probing */ if (serio->id.type == SERIO_PS_PSTHRU) usleep_range(10000, 15000); if (psmouse_probe(psmouse) < 0) { error = -ENODEV; goto err_close_serio; } psmouse->rate = psmouse_rate; psmouse->resolution = psmouse_resolution; psmouse->resetafter = psmouse_resetafter; psmouse->resync_time = parent ? 0 : psmouse_resync_time; psmouse->smartscroll = psmouse_smartscroll; psmouse_switch_protocol(psmouse, NULL); 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); error = sysfs_create_group(&serio->dev.kobj, &psmouse_attribute_group); if (error) goto err_pt_deactivate; /* * 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); mutex_unlock(&psmouse_mutex); return retval; err_pt_deactivate: if (parent && parent->pt_deactivate) parent->pt_deactivate(parent); if (input_dev) { input_unregister_device(input_dev); input_dev = NULL; /* so we don't try to free it below */ } err_protocol_disconnect: if (psmouse->disconnect) psmouse->disconnect(psmouse); psmouse_set_state(psmouse, PSMOUSE_IGNORE); err_close_serio: serio_close(serio); err_clear_drvdata: serio_set_drvdata(serio, NULL); err_free: input_free_device(input_dev); kfree(psmouse); retval = error; goto out; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov37378.03%2374.19%
Vojtech Pavlik6914.44%26.45%
Stefan Assmann183.77%13.23%
Andres Salomon102.09%26.45%
Peter Osterlund51.05%13.23%
Ingo Molnar20.42%13.23%
Pekka J Enberg10.21%13.23%
Total478100.00%31100.00%


static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) { struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *parent = NULL; int (*reconnect_handler)(struct psmouse *); enum psmouse_type type; int rc = -1; mutex_lock(&psmouse_mutex); if (fast_reconnect) { reconnect_handler = psmouse->fast_reconnect; if (!reconnect_handler) { rc = -ENOENT; goto out_unlock; } } else { reconnect_handler = psmouse->reconnect; } if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); 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); out_unlock: mutex_unlock(&psmouse_mutex); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov26497.06%1588.24%
Ingo Molnar41.47%15.88%
Vojtech Pavlik41.47%15.88%
Total272100.00%17100.00%


static int psmouse_reconnect(struct serio *serio) { return __psmouse_reconnect(serio, false); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov19100.00%1100.00%
Total19100.00%1100.00%


static int psmouse_fast_reconnect(struct serio *serio) { return __psmouse_reconnect(serio, true); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov19100.00%1100.00%
Total19100.00%1100.00%

static struct serio_device_id psmouse_serio_ids[] = { { .type = SERIO_8042, .proto = SERIO_ANY, .id = SERIO_ANY, .extra = SERIO_ANY, }, { .type = SERIO_PS_PSTHRU, .proto = SERIO_ANY, .id = SERIO_ANY, .extra = SERIO_ANY, }, { 0 } }; MODULE_DEVICE_TABLE(serio, psmouse_serio_ids); static struct serio_driver psmouse_drv = { .driver = { .name = "psmouse", }, .description = DRIVER_DESC, .id_table = psmouse_serio_ids, .interrupt = psmouse_interrupt, .connect = psmouse_connect, .reconnect = psmouse_reconnect, .fast_reconnect = psmouse_fast_reconnect, .disconnect = psmouse_disconnect, .cleanup = psmouse_cleanup, };
ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *devattr, char *buf) { struct serio *serio = to_serio_port(dev); struct psmouse_attribute *attr = to_psmouse_attr(devattr); struct psmouse *psmouse = serio_get_drvdata(serio); if (psmouse->protocol->smbus_companion && devattr != &psmouse_attr_protocol.dattr) return -ENOENT; return attr->show(psmouse, attr->data, buf); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov8198.78%480.00%
Eric W. Biedermann11.22%120.00%
Total82100.00%5100.00%


ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct serio *serio = to_serio_port(dev); struct psmouse_attribute *attr = to_psmouse_attr(devattr); struct psmouse *psmouse, *parent = NULL; int retval; retval = mutex_lock_interruptible(&psmouse_mutex); if (retval) goto out; psmouse = serio_get_drvdata(serio); if (psmouse->protocol->smbus_companion && devattr != &psmouse_attr_protocol.dattr) { retval = -ENOENT; goto out_unlock; } if (attr->protect) { if (psmouse->state == PSMOUSE_IGNORE) { retval = -ENODEV; goto out_unlock; } if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); psmouse_deactivate(parent); } if (!psmouse->protocol->smbus_companion) psmouse_deactivate(psmouse); } retval = attr->set(psmouse, attr->data, buf, count); if (attr->protect) { if (retval != -ENODEV && !psmouse->protocol->smbus_companion) psmouse_activate(psmouse); if (parent) psmouse_activate(parent); } out_unlock: mutex_unlock(&psmouse_mutex); out: return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov21589.96%562.50%
Andres Salomon166.69%112.50%
Ingo Molnar62.51%112.50%
Eric W. Biedermann20.84%112.50%
Total239100.00%8100.00%


static ssize_t psmouse_show_int_attr(struct psmouse *psmouse, void *offset, char *buf) { unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); return sprintf(buf, "%u\n", *field); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov5094.34%375.00%
Sergey Vlasov35.66%125.00%
Total53100.00%4100.00%


static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const char *buf, size_t count) { unsigned int *field = (unsigned int *)((char *)psmouse + (size_t)offset); unsigned int value; int err; err = kstrtouint(buf, 10, &value); if (err) return err; *field = value; return count; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov6075.00%125.00%
JJ Ding1012.50%125.00%
Sergey Vlasov78.75%125.00%
Joe Rouvier33.75%125.00%
Total80100.00%4100.00%


static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf) { return sprintf(buf, "%s\n", psmouse->protocol->name); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov33100.00%2100.00%
Total33100.00%2100.00%


static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count) { struct serio *serio = psmouse->ps2dev.serio; struct psmouse *parent = NULL; struct input_dev *old_dev, *new_dev; const struct psmouse_protocol *proto, *old_proto; int error; int retry = 0; proto = psmouse_protocol_by_name(buf, count); if (!proto) return -EINVAL; if (psmouse->protocol == proto) return count; new_dev = input_allocate_device(); if (!new_dev) return -ENOMEM; while (!list_empty(&serio->children)) { if (++retry > 3) { psmouse_warn(psmouse, "failed to destroy children ports, protocol change aborted.\n"); input_free_device(new_dev); return -EIO; } mutex_unlock(&psmouse_mutex); serio_unregister_child_port(serio); mutex_lock(&psmouse_mutex); if (serio->drv != &psmouse_drv) { input_free_device(new_dev); return -ENODEV; } if (psmouse->protocol == proto) { input_free_device(new_dev); return count; /* switched by other thread */ } } if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); if (parent->pt_deactivate) parent->pt_deactivate(parent); } old_dev = psmouse->dev; old_proto = psmouse->protocol; if (psmouse->disconnect) psmouse->disconnect(psmouse); psmouse_set_state(psmouse, PSMOUSE_IGNORE); psmouse->dev = new_dev; psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); if (psmouse_switch_protocol(psmouse, proto) < 0) { psmouse_reset(psmouse); /* default to PSMOUSE_PS2 */ psmouse_switch_protocol(psmouse, &psmouse_protocols[0]); } psmouse_initialize(psmouse); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); if (psmouse->protocol->smbus_companion) { input_free_device(psmouse->dev); psmouse->dev = NULL; } else { error = input_register_device(psmouse->dev); if (error) { if (psmouse->disconnect) psmouse->disconnect(psmouse); psmouse_set_state(psmouse, PSMOUSE_IGNORE); input_free_device(new_dev); psmouse->dev = old_dev; psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); psmouse_switch_protocol(psmouse, old_proto); psmouse_initialize(psmouse); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); return error; } } if (old_dev) input_unregister_device(old_dev); if (parent && parent->pt_activate) parent->pt_activate(parent); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov44597.59%1178.57%
Dmitry Baryshkov61.32%17.14%
Ingo Molnar40.88%17.14%
Helge Deller10.22%17.14%
Total456100.00%14100.00%


static ssize_t psmouse_attr_set_rate(struct psmouse *psmouse, void *data, const char *buf, size_t count) { unsigned int value; int err; err = kstrtouint(buf, 10, &value); if (err) return err; psmouse->set_rate(psmouse, value); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4675.41%250.00%
JJ Ding1219.67%125.00%
Joe Rouvier34.92%125.00%
Total61100.00%4100.00%


static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, const char *buf, size_t count) { unsigned int value; int err; err = kstrtouint(buf, 10, &value); if (err) return err; psmouse->set_resolution(psmouse, value); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4675.41%250.00%
JJ Ding1219.67%125.00%
Joe Rouvier34.92%125.00%
Total61100.00%4100.00%


static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp) { const struct psmouse_protocol *proto; if (!val) return -EINVAL; proto = psmouse_protocol_by_name(val, strlen(val)); if (!proto || !proto->maxproto) return -EINVAL; *((unsigned int *)kp->arg) = proto->type; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov6787.01%333.33%
Vojtech Pavlik67.79%222.22%
Helge Deller11.30%111.11%
Rusty Russell11.30%111.11%
Stephen Evanchik11.30%111.11%
Peter Osterlund11.30%111.11%
Total77100.00%9100.00%


static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp) { int type = *((unsigned int *)kp->arg); return sprintf(buffer, "%s", psmouse_protocol_by_type(type)->name); }

Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov4189.13%350.00%
Linus Torvalds36.52%116.67%
Rusty Russell12.17%116.67%
Takashi Iwai12.17%116.67%
Total46100.00%6100.00%


static int __init psmouse_init(void) { int err; lifebook_module_init(); synaptics_module_init(); hgpk_module_init(); err = psmouse_smbus_module_init(); if (err) return err; kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0); if (!kpsmoused_wq) { pr_err("failed to create kpsmoused workqueue\n"); err = -ENOMEM; goto err_smbus_exit; } err = serio_register_driver(&psmouse_drv); if (err) goto err_destroy_wq; return 0; err_destroy_wq: destroy_workqueue(kpsmoused_wq); err_smbus_exit: psmouse_smbus_module_exit(); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Tissoires3132.63%110.00%
Dmitry Torokhov2829.47%440.00%
Akinobu Mita1515.79%110.00%
Vojtech Pavlik1414.74%110.00%
Daniel Drake33.16%110.00%
Bhaktipriya Shridhar33.16%110.00%
Adrian Bunk11.05%110.00%
Total95100.00%10100.00%


static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); destroy_workqueue(kpsmoused_wq); psmouse_smbus_module_exit(); }

Contributors

PersonTokensPropCommitsCommitProp
Vojtech Pavlik1252.17%120.00%
Dmitry Torokhov730.43%240.00%
Benjamin Tissoires313.04%120.00%
Adrian Bunk14.35%120.00%
Total23100.00%5100.00%

module_init(psmouse_init); module_exit(psmouse_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Dmitry Torokhov658375.70%6446.38%
Vojtech Pavlik8219.44%1611.59%
Benjamin Tissoires2582.97%32.17%
Pozsar Balazs1491.71%10.72%
Peter Maydell1391.60%10.72%
Andres Salomon1051.21%64.35%
Peter Osterlund891.02%53.62%
Aristeu Rozanski720.83%10.72%
Hans de Goede530.61%21.45%
Mathias Gottschlag440.51%21.45%
Chris Diamand340.39%10.72%
Ingo Molnar340.39%10.72%
JJ Ding340.39%10.72%
Dudley Du300.34%10.72%
Helge Deller280.32%10.72%
Andrew Morton260.30%21.45%
Daniel Drake260.30%32.17%
Rusty Russell220.25%21.45%
Stefan Assmann180.21%10.72%
Zephaniah E. Hull170.20%10.72%
David Howells150.17%10.72%
Akinobu Mita150.17%10.72%
Sergey Vlasov100.11%10.72%
Stephen Evanchik100.11%10.72%
Joe Rouvier90.10%10.72%
Arjan Opmeer80.09%10.72%
Stefan Lucke70.08%10.72%
Linus Torvalds70.08%21.45%
Dmitry Baryshkov60.07%10.72%
Tai-hwa Liang50.06%10.72%
Thomas Hellstrom40.05%10.72%
Kenan Esau30.03%10.72%
Eric W. Biedermann30.03%10.72%
Bhaktipriya Shridhar30.03%10.72%
Adrian Bunk20.02%10.72%
Arvind Yadav10.01%10.72%
Alan Cox10.01%10.72%
Takashi Iwai10.01%10.72%
Pekka J Enberg10.01%10.72%
Shailendra Verma10.01%10.72%
Richard Pospesel10.01%10.72%
Luis R. Rodriguez10.01%10.72%
Total8696100.00%138100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.