Commit c774326a authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: psmouse - introduce notion of SMBus companions

Prepare PS/2 mouse drivers to work with devices that are accessible both
via PS/2 and SMBus, which provides higher bandwidth, and thus suits better
for modern multi-touch devices.

We expect that SMBus drivers will take control over the device, so when
we detect SMBus "protocol" we forego registering input device, or enabling
PS/2 device reports (as it usually makes device unresponsive to access over
SMBus).
Reviewed-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 085fa80d
...@@ -1424,9 +1424,8 @@ static void psmouse_cleanup(struct serio *serio) ...@@ -1424,9 +1424,8 @@ static void psmouse_cleanup(struct serio *serio)
*/ */
static void psmouse_disconnect(struct serio *serio) static void psmouse_disconnect(struct serio *serio)
{ {
struct psmouse *psmouse, *parent = NULL; struct psmouse *psmouse = serio_get_drvdata(serio);
struct psmouse *parent = NULL;
psmouse = serio_get_drvdata(serio);
sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
...@@ -1454,7 +1453,10 @@ static void psmouse_disconnect(struct serio *serio) ...@@ -1454,7 +1453,10 @@ static void psmouse_disconnect(struct serio *serio)
serio_close(serio); serio_close(serio);
serio_set_drvdata(serio, NULL); serio_set_drvdata(serio, NULL);
input_unregister_device(psmouse->dev);
if (psmouse->dev)
input_unregister_device(psmouse->dev);
kfree(psmouse); kfree(psmouse);
if (parent) if (parent)
...@@ -1575,12 +1577,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) ...@@ -1575,12 +1577,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
psmouse_switch_protocol(psmouse, NULL); psmouse_switch_protocol(psmouse, NULL);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); if (!psmouse->protocol->smbus_companion) {
psmouse_initialize(psmouse); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse);
error = input_register_device(psmouse->dev); error = input_register_device(input_dev);
if (error) if (error)
goto err_protocol_disconnect; 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) if (parent && parent->pt_activate)
parent->pt_activate(parent); parent->pt_activate(parent);
...@@ -1589,7 +1597,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) ...@@ -1589,7 +1597,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
if (error) if (error)
goto err_pt_deactivate; 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: out:
/* If this is a pass-through port the parent needs to be re-activated */ /* If this is a pass-through port the parent needs to be re-activated */
...@@ -1602,8 +1615,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) ...@@ -1602,8 +1615,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
err_pt_deactivate: err_pt_deactivate:
if (parent && parent->pt_deactivate) if (parent && parent->pt_deactivate)
parent->pt_deactivate(parent); parent->pt_deactivate(parent);
input_unregister_device(psmouse->dev); if (input_dev) {
input_dev = NULL; /* so we don't try to free it below */ input_unregister_device(input_dev);
input_dev = NULL; /* so we don't try to free it below */
}
err_protocol_disconnect: err_protocol_disconnect:
if (psmouse->disconnect) if (psmouse->disconnect)
psmouse->disconnect(psmouse); psmouse->disconnect(psmouse);
...@@ -1665,14 +1680,21 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect) ...@@ -1665,14 +1680,21 @@ static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
* OK, the device type (and capabilities) match the old one, * OK, the device type (and capabilities) match the old one,
* we can continue using it, complete initialization * we can continue using it, complete initialization
*/ */
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); if (!psmouse->protocol->smbus_companion) {
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
psmouse_initialize(psmouse); psmouse_initialize(psmouse);
}
if (parent && parent->pt_activate) if (parent && parent->pt_activate)
parent->pt_activate(parent); 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; rc = 0;
out: out:
...@@ -1732,9 +1754,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de ...@@ -1732,9 +1754,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de
{ {
struct serio *serio = to_serio_port(dev); struct serio *serio = to_serio_port(dev);
struct psmouse_attribute *attr = to_psmouse_attr(devattr); 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); return attr->show(psmouse, attr->data, buf);
} }
...@@ -1753,6 +1777,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev ...@@ -1753,6 +1777,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
psmouse = serio_get_drvdata(serio); psmouse = serio_get_drvdata(serio);
if (psmouse->protocol->smbus_companion &&
devattr != &psmouse_attr_protocol.dattr) {
retval = -ENOENT;
goto out_unlock;
}
if (attr->protect) { if (attr->protect) {
if (psmouse->state == PSMOUSE_IGNORE) { if (psmouse->state == PSMOUSE_IGNORE) {
retval = -ENODEV; retval = -ENODEV;
...@@ -1764,13 +1794,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev ...@@ -1764,13 +1794,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
psmouse_deactivate(parent); psmouse_deactivate(parent);
} }
psmouse_deactivate(psmouse); if (!psmouse->protocol->smbus_companion)
psmouse_deactivate(psmouse);
} }
retval = attr->set(psmouse, attr->data, buf, count); retval = attr->set(psmouse, attr->data, buf, count);
if (attr->protect) { if (attr->protect) {
if (retval != -ENODEV) if (retval != -ENODEV && !psmouse->protocol->smbus_companion)
psmouse_activate(psmouse); psmouse_activate(psmouse);
if (parent) if (parent)
...@@ -1879,23 +1910,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co ...@@ -1879,23 +1910,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
psmouse_initialize(psmouse); psmouse_initialize(psmouse);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
error = input_register_device(psmouse->dev); if (psmouse->protocol->smbus_companion) {
if (error) { input_free_device(psmouse->dev);
if (psmouse->disconnect) psmouse->dev = NULL;
psmouse->disconnect(psmouse); } else {
error = input_register_device(psmouse->dev);
if (error) {
if (psmouse->disconnect)
psmouse->disconnect(psmouse);
psmouse_set_state(psmouse, PSMOUSE_IGNORE); psmouse_set_state(psmouse, PSMOUSE_IGNORE);
input_free_device(new_dev); input_free_device(new_dev);
psmouse->dev = old_dev; psmouse->dev = old_dev;
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING); psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
psmouse_switch_protocol(psmouse, old_proto); psmouse_switch_protocol(psmouse, old_proto);
psmouse_initialize(psmouse); psmouse_initialize(psmouse);
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); 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) if (parent && parent->pt_activate)
parent->pt_activate(parent); parent->pt_activate(parent);
......
...@@ -76,6 +76,7 @@ struct psmouse_protocol { ...@@ -76,6 +76,7 @@ struct psmouse_protocol {
bool maxproto; bool maxproto;
bool ignore_parity; /* Protocol should ignore parity errors from KBC */ bool ignore_parity; /* Protocol should ignore parity errors from KBC */
bool try_passthru; /* Try protocol also on passthrough ports */ bool try_passthru; /* Try protocol also on passthrough ports */
bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */
const char *name; const char *name;
const char *alias; const char *alias;
int (*detect)(struct psmouse *, bool); int (*detect)(struct psmouse *, bool);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment