diff options
Diffstat (limited to 'drivers/input/mouse/psmouse-base.c')
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 216 |
1 files changed, 148 insertions, 68 deletions
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index a598b7223cef..f73b47b8c578 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -116,17 +116,6 @@ static DEFINE_MUTEX(psmouse_mutex); static struct workqueue_struct *kpsmoused_wq; -struct psmouse_protocol { - enum psmouse_type type; - bool maxproto; - bool ignore_parity; /* Protocol should ignore parity errors from KBC */ - bool try_passthru; /* Try protocol also on passthrough ports */ - const char *name; - const char *alias; - int (*detect)(struct psmouse *, bool); - int (*init)(struct psmouse *); -}; - static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons) { input_report_key(dev, BTN_LEFT, buttons & BIT(0)); @@ -148,7 +137,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse) /* Full packet accumulated, process it */ - switch (psmouse->type) { + switch (psmouse->protocol->type) { case PSMOUSE_IMPS: /* IntelliMouse has scroll wheel */ input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]); @@ -325,7 +314,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, goto out; if (unlikely((flags & SERIO_TIMEOUT) || - ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) { + ((flags & SERIO_PARITY) && + !psmouse->protocol->ignore_parity))) { if (psmouse->state == PSMOUSE_ACTIVATED) psmouse_warn(psmouse, @@ -372,7 +362,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, } if (psmouse->packet[1] == PSMOUSE_RET_ID || - (psmouse->type == PSMOUSE_HGPK && + (psmouse->protocol->type == PSMOUSE_HGPK && psmouse->packet[1] == PSMOUSE_RET_BAT)) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); serio_reconnect(serio); @@ -783,7 +773,7 @@ static const struct psmouse_protocol psmouse_protocols[] = { .name = "SynPS/2", .alias = "synaptics", .detect = synaptics_detect, - .init = synaptics_init, + .init = synaptics_init_absolute, }, { .type = PSMOUSE_SYNAPTICS_RELATIVE, @@ -793,6 +783,16 @@ static const struct psmouse_protocol psmouse_protocols[] = { .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, @@ -959,6 +959,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) __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; @@ -966,6 +968,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse) 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; @@ -1018,6 +1021,7 @@ 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 @@ -1080,9 +1084,14 @@ static int psmouse_extensions(struct psmouse *psmouse, * enabled first, since we try detecting Synaptics * even when protocol is disabled. */ - if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) && - (!set_properties || synaptics_init(psmouse) == 0)) { - return PSMOUSE_SYNAPTICS; + 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; } /* @@ -1431,9 +1440,8 @@ static void psmouse_cleanup(struct serio *serio) */ static void psmouse_disconnect(struct serio *serio) { - struct psmouse *psmouse, *parent = NULL; - - psmouse = serio_get_drvdata(serio); + struct psmouse *psmouse = serio_get_drvdata(serio); + struct psmouse *parent = NULL; sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); @@ -1461,7 +1469,10 @@ static void psmouse_disconnect(struct serio *serio) serio_close(serio); serio_set_drvdata(serio, NULL); - input_unregister_device(psmouse->dev); + + if (psmouse->dev) + input_unregister_device(psmouse->dev); + kfree(psmouse); if (parent) @@ -1475,6 +1486,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, { 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; @@ -1487,15 +1499,13 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, if (proto->init && proto->init(psmouse) < 0) return -1; - psmouse->type = proto->type; selected_proto = proto; } else { - psmouse->type = psmouse_extensions(psmouse, - psmouse_max_proto, true); - selected_proto = psmouse_protocol_by_type(psmouse->type); + type = psmouse_extensions(psmouse, psmouse_max_proto, true); + selected_proto = psmouse_protocol_by_type(type); } - psmouse->ignore_parity = selected_proto->ignore_parity; + psmouse->protocol = selected_proto; /* * If mouse's packet size is 3 there is no point in polling the @@ -1521,7 +1531,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, input_dev->phys = psmouse->phys; input_dev->id.bustype = BUS_I8042; input_dev->id.vendor = 0x0002; - input_dev->id.product = psmouse->type; + input_dev->id.product = psmouse->protocol->type; input_dev->id.version = psmouse->model; return 0; @@ -1583,12 +1593,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) psmouse_switch_protocol(psmouse, NULL); - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - psmouse_initialize(psmouse); + if (!psmouse->protocol->smbus_companion) { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); - error = input_register_device(psmouse->dev); - if (error) - goto err_protocol_disconnect; + 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); @@ -1597,7 +1613,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) if (error) goto err_pt_deactivate; - psmouse_activate(psmouse); + /* + * 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 */ @@ -1610,8 +1631,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) err_pt_deactivate: if (parent && parent->pt_deactivate) parent->pt_deactivate(parent); - input_unregister_device(psmouse->dev); - input_dev = NULL; /* so we don't try to free it below */ + 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); @@ -1628,15 +1651,26 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) goto out; } -static int psmouse_reconnect(struct serio *serio) +static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) { struct psmouse *psmouse = serio_get_drvdata(serio); struct psmouse *parent = NULL; - unsigned char type; + 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); @@ -1644,8 +1678,8 @@ static int psmouse_reconnect(struct serio *serio) psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); - if (psmouse->reconnect) { - if (psmouse->reconnect(psmouse)) + if (reconnect_handler) { + if (reconnect_handler(psmouse)) goto out; } else { psmouse_reset(psmouse); @@ -1654,7 +1688,7 @@ static int psmouse_reconnect(struct serio *serio) goto out; type = psmouse_extensions(psmouse, psmouse_max_proto, false); - if (psmouse->type != type) + if (psmouse->protocol->type != type) goto out; } @@ -1662,14 +1696,21 @@ static int psmouse_reconnect(struct serio *serio) * OK, the device type (and capabilities) match the old one, * we can continue using it, complete initialization */ - psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - - psmouse_initialize(psmouse); + if (!psmouse->protocol->smbus_companion) { + psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); + psmouse_initialize(psmouse); + } if (parent && parent->pt_activate) parent->pt_activate(parent); - psmouse_activate(psmouse); + /* + * 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: @@ -1677,10 +1718,21 @@ out: if (parent) psmouse_activate(parent); +out_unlock: mutex_unlock(&psmouse_mutex); return rc; } +static int psmouse_reconnect(struct serio *serio) +{ + return __psmouse_reconnect(serio, false); +} + +static int psmouse_fast_reconnect(struct serio *serio) +{ + return __psmouse_reconnect(serio, true); +} + static struct serio_device_id psmouse_serio_ids[] = { { .type = SERIO_8042, @@ -1708,6 +1760,7 @@ static struct serio_driver psmouse_drv = { .interrupt = psmouse_interrupt, .connect = psmouse_connect, .reconnect = psmouse_reconnect, + .fast_reconnect = psmouse_fast_reconnect, .disconnect = psmouse_disconnect, .cleanup = psmouse_cleanup, }; @@ -1717,9 +1770,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de { struct serio *serio = to_serio_port(dev); struct psmouse_attribute *attr = to_psmouse_attr(devattr); - struct psmouse *psmouse; + struct psmouse *psmouse = serio_get_drvdata(serio); - psmouse = serio_get_drvdata(serio); + if (psmouse->protocol->smbus_companion && + devattr != &psmouse_attr_protocol.dattr) + return -ENOENT; return attr->show(psmouse, attr->data, buf); } @@ -1738,6 +1793,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev 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; @@ -1749,13 +1810,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev psmouse_deactivate(parent); } - psmouse_deactivate(psmouse); + if (!psmouse->protocol->smbus_companion) + psmouse_deactivate(psmouse); } retval = attr->set(psmouse, attr->data, buf, count); if (attr->protect) { - if (retval != -ENODEV) + if (retval != -ENODEV && !psmouse->protocol->smbus_companion) psmouse_activate(psmouse); if (parent) @@ -1792,7 +1854,7 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf) { - return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name); + return sprintf(buf, "%s\n", psmouse->protocol->name); } static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count) @@ -1808,7 +1870,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co if (!proto) return -EINVAL; - if (psmouse->type == proto->type) + if (psmouse->protocol == proto) return count; new_dev = input_allocate_device(); @@ -1832,7 +1894,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co return -ENODEV; } - if (psmouse->type == proto->type) { + if (psmouse->protocol == proto) { input_free_device(new_dev); return count; /* switched by other thread */ } @@ -1845,7 +1907,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co } old_dev = psmouse->dev; - old_proto = psmouse_protocol_by_type(psmouse->type); + old_proto = psmouse->protocol; if (psmouse->disconnect) psmouse->disconnect(psmouse); @@ -1864,23 +1926,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co psmouse_initialize(psmouse); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); - error = input_register_device(psmouse->dev); - if (error) { - if (psmouse->disconnect) - psmouse->disconnect(psmouse); + 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); + 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; + return error; + } } - input_unregister_device(old_dev); + if (old_dev) + input_unregister_device(old_dev); if (parent && parent->pt_activate) parent->pt_activate(parent); @@ -1947,16 +2015,27 @@ static int __init psmouse_init(void) 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"); - return -ENOMEM; + err = -ENOMEM; + goto err_smbus_exit; } err = serio_register_driver(&psmouse_drv); if (err) - destroy_workqueue(kpsmoused_wq); + goto err_destroy_wq; + + return 0; +err_destroy_wq: + destroy_workqueue(kpsmoused_wq); +err_smbus_exit: + psmouse_smbus_module_exit(); return err; } @@ -1964,6 +2043,7 @@ static void __exit psmouse_exit(void) { serio_unregister_driver(&psmouse_drv); destroy_workqueue(kpsmoused_wq); + psmouse_smbus_module_exit(); } module_init(psmouse_init); |