* [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards
@ 2010-08-16 11:26 Dmitry Eremin-Solenikov
2010-08-16 11:26 ` [PATCH 1/2] serio: support multiple child devices per single parent Dmitry Eremin-Solenikov
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Dmitry Eremin-Solenikov @ 2010-08-16 11:26 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov
On tqm85xx boards (and several others) keyboard and mice are connected to CPU via
special controller multiplexing KBD and MS traffic into single UART.
Changes since v1:
* Rewrote serio to use iterative algorithms instead of recursion for serio
tree traversal
* Changed ps2mult to register as SERIO_8042, not it's own special type
* Small cleanup in ps2mult
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] serio: support multiple child devices per single parent
2010-08-16 11:26 [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
@ 2010-08-16 11:26 ` Dmitry Eremin-Solenikov
2010-09-15 5:03 ` Dmitry Torokhov
2010-08-16 11:26 ` [PATCH 2/2] serio: add support for PS2Mult multiplexer protocol Dmitry Eremin-Solenikov
2010-08-31 10:49 ` [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
2 siblings, 1 reply; 7+ messages in thread
From: Dmitry Eremin-Solenikov @ 2010-08-16 11:26 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov
Some (rare) serio devices need to have multiple serio children. One of
the examples is PS/2 multiplexer present on several TQC STKxxx boards,
which connect PS/2 keyboard and mouse to single tty port.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
drivers/input/mouse/psmouse-base.c | 2 +-
drivers/input/mouse/synaptics.c | 11 +++-
drivers/input/serio/serio.c | 114 +++++++++++++++++++-----------------
include/linux/serio.h | 5 +-
4 files changed, 73 insertions(+), 59 deletions(-)
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 979c502..0eeed6c 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -1582,7 +1582,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
if (!new_dev)
return -ENOMEM;
- while (serio->child) {
+ while (!list_empty(&serio->children)) {
if (++retry > 3) {
printk(KERN_WARNING
"psmouse: failed to destroy child port, "
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 705589d..9295ad0 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -315,7 +315,9 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
static void synaptics_pt_activate(struct psmouse *psmouse)
{
- struct serio *ptport = psmouse->ps2dev.serio->child;
+ struct serio *ptport =
+ list_first_entry(&psmouse->ps2dev.serio->children,
+ struct serio, child_list);
struct psmouse *child = serio_get_drvdata(ptport);
struct synaptics_data *priv = psmouse->private;
@@ -577,8 +579,11 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
- if (psmouse->ps2dev.serio->child)
- synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+ if (!list_empty(&psmouse->ps2dev.serio->children))
+ synaptics_pass_pt_packet(
+ list_first_entry(&psmouse->ps2dev.serio->children,
+ struct serio, child_list),
+ psmouse->packet);
} else
synaptics_process_packet(psmouse);
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index c3b626e..55c46e8 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -312,12 +312,9 @@ static void serio_handle_event(void)
* Remove all events that have been submitted for a given
* object, be it serio port or driver.
*/
-static void serio_remove_pending_events(void *object)
+static void __serio_remove_pending_events(void *object)
{
struct serio_event *event, *next;
- unsigned long flags;
-
- spin_lock_irqsave(&serio_event_lock, flags);
list_for_each_entry_safe(event, next, &serio_event_list, node) {
if (event->object == object) {
@@ -325,38 +322,41 @@ static void serio_remove_pending_events(void *object)
serio_free_event(event);
}
}
+}
+
+static void serio_remove_pending_events(void *object)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&serio_event_lock, flags);
+
+ __serio_remove_pending_events(object);
spin_unlock_irqrestore(&serio_event_lock, flags);
}
/*
* Destroy child serio port (if any) that has not been fully registered yet.
- *
- * Note that we rely on the fact that port can have only one child and therefore
- * only one child registration request can be pending. Additionally, children
- * are registered by driver's connect() handler so there can't be a grandchild
- * pending registration together with a child.
*/
-static struct serio *serio_get_pending_child(struct serio *parent)
+static void serio_drop_pending_children(struct serio *parent)
{
- struct serio_event *event;
- struct serio *serio, *child = NULL;
+ struct serio_event *event, *temp;
+ struct serio *serio;
unsigned long flags;
spin_lock_irqsave(&serio_event_lock, flags);
- list_for_each_entry(event, &serio_event_list, node) {
+ list_for_each_entry_safe(event, temp, &serio_event_list, node) {
if (event->type == SERIO_REGISTER_PORT) {
serio = event->object;
if (serio->parent == parent) {
- child = serio;
- break;
+ __serio_remove_pending_events(serio);
+ put_device(&serio->dev);
}
}
}
spin_unlock_irqrestore(&serio_event_lock, flags);
- return child;
}
static int serio_thread(void *nothing)
@@ -516,6 +516,9 @@ static void serio_init_port(struct serio *serio)
__module_get(THIS_MODULE);
INIT_LIST_HEAD(&serio->node);
+ INIT_LIST_HEAD(&serio->children);
+ INIT_LIST_HEAD(&serio->child_list);
+ INIT_LIST_HEAD(&serio->internal);
spin_lock_init(&serio->lock);
mutex_init(&serio->drv_mutex);
device_initialize(&serio->dev);
@@ -542,7 +545,7 @@ static void serio_add_port(struct serio *serio)
if (serio->parent) {
serio_pause_rx(serio->parent);
- serio->parent->child = serio;
+ list_add_tail(&serio->child_list, &serio->parent->children);
serio_continue_rx(serio->parent);
}
@@ -564,20 +567,14 @@ static void serio_add_port(struct serio *serio)
*/
static void serio_destroy_port(struct serio *serio)
{
- struct serio *child;
-
- child = serio_get_pending_child(serio);
- if (child) {
- serio_remove_pending_events(child);
- put_device(&child->dev);
- }
+ serio_drop_pending_children(serio);
if (serio->stop)
serio->stop(serio);
if (serio->parent) {
serio_pause_rx(serio->parent);
- serio->parent->child = NULL;
+ list_del(&serio->child_list);
serio_continue_rx(serio->parent);
serio->parent = NULL;
}
@@ -613,13 +610,19 @@ static int serio_reconnect_port(struct serio *serio)
*/
static void serio_reconnect_chain(struct serio *serio)
{
- do {
- if (serio_reconnect_port(serio)) {
- /* Ok, old children are now gone, we are done */
- break;
- }
- serio = serio->child;
- } while (serio);
+ struct serio *child;
+ LIST_HEAD(todo);
+
+ list_add_tail(&serio->internal, &todo);
+
+ while (!list_empty(&todo)) {
+ serio = list_first_entry(&todo, struct serio, internal);
+ list_del_init(&serio->internal);
+
+ if (!serio_reconnect_port(serio))
+ list_for_each_entry(child, &serio->children, child_list)
+ list_add_tail(&child->internal, &todo);
+ }
}
/*
@@ -628,29 +631,31 @@ static void serio_reconnect_chain(struct serio *serio)
*/
static void serio_disconnect_port(struct serio *serio)
{
- struct serio *s, *parent;
+ struct serio *s, *child;
+ LIST_HEAD(todo);
- if (serio->child) {
- /*
- * Children ports should be disconnected and destroyed
- * first, staring with the leaf one, since we don't want
- * to do recursion
- */
- for (s = serio; s->child; s = s->child)
- /* empty */;
+ list_add_tail(&serio->internal, &todo);
- do {
- parent = s->parent;
+ while (!list_empty(&todo)) {
+ s = list_first_entry(&todo, struct serio, internal);
+
+ if (!list_empty(&s->children)) {
+ list_for_each_entry(child, &s->children, child_list)
+ list_add(&child->internal, &todo);
+
+ /*
+ * We will return to this item later, when it will have
+ * no children.
+ */
+ } else {
+ list_del_init(&s->internal);
device_release_driver(&s->dev);
- serio_destroy_port(s);
- } while ((s = parent) != serio);
- }
- /*
- * Ok, no children left, now disconnect this port
- */
- device_release_driver(&serio->dev);
+ if (s != serio)
+ serio_destroy_port(s);
+ }
+ }
}
void serio_rescan(struct serio *serio)
@@ -689,14 +694,15 @@ void serio_unregister_port(struct serio *serio)
EXPORT_SYMBOL(serio_unregister_port);
/*
- * Safely unregisters child port if one is present.
+ * Safely unregisters child ports if any is present.
*/
void serio_unregister_child_port(struct serio *serio)
{
+ struct serio *s, *temp;
mutex_lock(&serio_mutex);
- if (serio->child) {
- serio_disconnect_port(serio->child);
- serio_destroy_port(serio->child);
+ list_for_each_entry_safe(s, temp, &serio->children, child_list) {
+ serio_disconnect_port(s);
+ serio_destroy_port(s);
}
mutex_unlock(&serio_mutex);
}
diff --git a/include/linux/serio.h b/include/linux/serio.h
index b555256..861a72a 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -41,7 +41,9 @@ struct serio {
int (*start)(struct serio *);
void (*stop)(struct serio *);
- struct serio *parent, *child;
+ struct serio *parent;
+ struct list_head child_list;
+ struct list_head children;
unsigned int depth; /* level of nesting in serio hierarchy */
struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */
@@ -50,6 +52,7 @@ struct serio {
struct device dev;
struct list_head node;
+ struct list_head internal; /* Used internally to avoid recursion */
};
#define to_serio_port(d) container_of(d, struct serio, dev)
--
1.7.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/2] serio: add support for PS2Mult multiplexer protocol
2010-08-16 11:26 [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
2010-08-16 11:26 ` [PATCH 1/2] serio: support multiple child devices per single parent Dmitry Eremin-Solenikov
@ 2010-08-16 11:26 ` Dmitry Eremin-Solenikov
2010-09-08 5:35 ` Dmitry Torokhov
2010-08-31 10:49 ` [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
2 siblings, 1 reply; 7+ messages in thread
From: Dmitry Eremin-Solenikov @ 2010-08-16 11:26 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov
PS2Mult is a simple serial protocol used for multiplexing several PS/2 streams
into one serial data stream. It's used e.g. on TQM85xx serie of boards.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
---
drivers/input/serio/Kconfig | 8 ++
drivers/input/serio/Makefile | 1 +
drivers/input/serio/ps2mult.c | 265 +++++++++++++++++++++++++++++++++++++++++
include/linux/serio.h | 2 +
4 files changed, 276 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/serio/ps2mult.c
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index 3bfe8fa..63f4658 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -226,4 +226,12 @@ config SERIO_AMS_DELTA
To compile this driver as a module, choose M here;
the module will be called ams_delta_serio.
+config SERIO_PS2MULT
+ tristate "TQC PS/2 multiplexer"
+ help
+ Say Y here if you have the PS/2 line multiplexer like present on TQC boads
+
+ To compile this driver as a module, choose M here: the
+ module will be called ps2mult.
+
endif
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 84c80bf..26714c5 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_SERIO_RAW) += serio_raw.o
obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o
obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o
obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
+obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
new file mode 100644
index 0000000..e9b7951
--- /dev/null
+++ b/drivers/input/serio/ps2mult.c
@@ -0,0 +1,265 @@
+/*
+ * TQC PS/2 Multiplexer driver
+ *
+ * Copyright (C) 2010 Dmitry Eremin-Solenikov
+ *
+ * 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.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
+MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
+MODULE_LICENSE("GPL");
+
+#define PS2MULT_KB_SELECTOR 0xA0
+#define PS2MULT_MS_SELECTOR 0xA1
+#define PS2MULT_ESCAPE 0x7D
+#define PS2MULT_BSYNC 0x7E
+#define PS2MULT_SESSION_START 0x55
+#define PS2MULT_SESSION_END 0x56
+
+struct ps2mult_port {
+ struct serio *serio;
+ unsigned char sel;
+ unsigned char port;
+};
+
+#define PS2MULT_NUM_PORTS 2
+
+struct ps2mult {
+ struct serio *serio;
+ struct ps2mult_port ports[PS2MULT_NUM_PORTS];
+
+ spinlock_t lock;
+ unsigned char cur_out_port;
+ unsigned char cur_in_port;
+ unsigned escape:1;
+};
+
+static unsigned char ps2mult_selectors[PS2MULT_NUM_PORTS] = {
+ PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
+};
+
+static struct serio_device_id ps2mult_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PS2MULT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
+
+static int ps2mult_serio_write(struct serio *serio, unsigned char data)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio->parent);
+ struct ps2mult_port *psmp = serio->port_data;
+ bool need_escape;
+ unsigned long flags;
+
+ spin_lock_irqsave(&psm->lock, flags);
+ if (psm->cur_out_port != psmp->port) {
+ psm->serio->write(psm->serio, psmp->sel);
+ psm->cur_out_port = psmp->port;
+ dev_dbg(&serio->dev, "switched to sel %02x\n", psmp->sel);
+ }
+
+ need_escape = data == PS2MULT_ESCAPE
+ || data == PS2MULT_BSYNC
+ || data == PS2MULT_SESSION_START
+ || data == PS2MULT_SESSION_END
+ || memchr(ps2mult_selectors, data, PS2MULT_NUM_PORTS);
+
+ dev_dbg(&serio->dev, "write: %s%02x\n",
+ need_escape ? "ESC " : "", data);
+
+ if (need_escape)
+ psm->serio->write(psm->serio, PS2MULT_ESCAPE);
+ psm->serio->write(psm->serio, data);
+
+ spin_unlock_irqrestore(&psm->lock, flags);
+
+ return 0;
+}
+
+static int ps2mult_create_port(struct ps2mult *psm, int i)
+{
+ struct serio *serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!serio)
+ return -ENOMEM;
+
+ strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
+ snprintf(serio->phys, sizeof(serio->phys),
+ "%s/port%d", psm->serio->phys, i);
+ serio->id.type = SERIO_PS2MULT_T;
+ serio->write = ps2mult_serio_write;
+ serio->parent = psm->serio;
+
+ serio->port_data = &psm->ports[i];
+
+ psm->ports[i].serio = serio;
+ psm->ports[i].port = i;
+ psm->ports[i].sel = ps2mult_selectors[i];
+
+ serio_register_port(serio);
+ dev_info(&serio->dev, "%s port at %s\n", serio->name, psm->serio->phys);
+
+ return 0;
+}
+
+static int ps2mult_reconnect(struct serio *serio)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio);
+
+ serio->write(serio, PS2MULT_SESSION_END);
+ serio->write(serio, PS2MULT_SESSION_START);
+ psm->cur_out_port = 0;
+ serio->write(serio, psm->ports[psm->cur_out_port].sel);
+
+ return 0;
+}
+
+static void ps2mult_disconnect(struct serio *serio)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio);
+ int i;
+
+ serio->write(serio, PS2MULT_SESSION_END);
+
+ for (i = 0; i < PS2MULT_NUM_PORTS; i++)
+ psm->ports[i].serio = NULL;
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+
+ kfree(psm);
+}
+
+static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct ps2mult *psm;
+ int i;
+ int rc;
+
+ if (!serio->write)
+ return -EINVAL;
+
+ psm = kzalloc(sizeof(*psm), GFP_KERNEL);
+ if (!psm)
+ return -ENOMEM;
+
+ spin_lock_init(&psm->lock);
+ psm->serio = serio;
+
+ serio_set_drvdata(serio, psm);
+ serio_open(serio, drv);
+
+ for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
+ rc = ps2mult_create_port(psm, i);
+ if (rc)
+ goto err_out;
+ }
+
+ rc = ps2mult_reconnect(serio);
+ if (rc)
+ goto err_out;
+
+ return 0;
+
+err_out:
+ ps2mult_disconnect(serio);
+
+ return rc;
+}
+
+static void ps2mult_selector(struct ps2mult *psm, unsigned char data)
+{
+ int i;
+
+ dev_dbg(&psm->serio->dev, "Received selector %02x\n", data);
+
+ spin_lock(&psm->lock);
+
+ for (i = 0; i < PS2MULT_NUM_PORTS; i++)
+ if (psm->ports[i].sel == data) {
+ psm->cur_in_port = i;
+ break;
+ }
+
+ spin_unlock(&psm->lock);
+}
+
+static irqreturn_t ps2mult_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct ps2mult *psm = serio_get_drvdata(serio);
+
+ dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags);
+ if (psm->escape) {
+ serio_interrupt(psm->ports[psm->cur_in_port].serio,
+ data, flags);
+ psm->escape = 0;
+ } else
+ switch (data) {
+ case PS2MULT_ESCAPE:
+ dev_dbg(&serio->dev, "ESCAPE\n");
+ psm->escape = 1;
+ break;
+ case PS2MULT_BSYNC:
+ dev_dbg(&serio->dev, "BSYNC\n");
+ psm->cur_in_port = psm->cur_out_port;
+ break;
+ case PS2MULT_SESSION_START:
+ dev_dbg(&serio->dev, "SS\n");
+ break;
+ case PS2MULT_SESSION_END:
+ dev_dbg(&serio->dev, "SE\n");
+ break;
+ case PS2MULT_KB_SELECTOR:
+ dev_dbg(&serio->dev, "KB\n");
+ ps2mult_selector(psm, data);
+ break;
+ case PS2MULT_MS_SELECTOR:
+ dev_dbg(&serio->dev, "MS\n");
+ ps2mult_selector(psm, data);
+ break;
+ default:
+ serio_interrupt(psm->ports[psm->cur_in_port].serio,
+ data, flags);
+ }
+ return IRQ_HANDLED;
+}
+
+static struct serio_driver ps2mult_drv = {
+ .driver = {
+ .name = "ps2mult",
+ },
+ .description = "TQC PS/2 Multiplexer driver",
+ .id_table = ps2mult_serio_ids,
+ .interrupt = ps2mult_interrupt,
+ .connect = ps2mult_connect,
+ .disconnect = ps2mult_disconnect,
+ .reconnect = ps2mult_reconnect,
+};
+
+static int __init ps2mult_init(void)
+{
+ return serio_register_driver(&ps2mult_drv);
+}
+
+static void __exit ps2mult_exit(void)
+{
+ serio_unregister_driver(&ps2mult_drv);
+}
+
+module_init(ps2mult_init);
+module_exit(ps2mult_exit);
diff --git a/include/linux/serio.h b/include/linux/serio.h
index 861a72a..7257e6c 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -156,6 +156,7 @@ static inline void serio_continue_rx(struct serio *serio)
#define SERIO_HIL_MLC 0x03
#define SERIO_PS_PSTHRU 0x05
#define SERIO_8042_XL 0x06
+#define SERIO_PS2MULT_T 0x07
/*
* Serio protocols
@@ -200,5 +201,6 @@ static inline void serio_continue_rx(struct serio *serio)
#define SERIO_W8001 0x39
#define SERIO_DYNAPRO 0x3a
#define SERIO_HAMPSHIRE 0x3b
+#define SERIO_PS2MULT 0x3c
#endif
--
1.7.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards
2010-08-16 11:26 [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
2010-08-16 11:26 ` [PATCH 1/2] serio: support multiple child devices per single parent Dmitry Eremin-Solenikov
2010-08-16 11:26 ` [PATCH 2/2] serio: add support for PS2Mult multiplexer protocol Dmitry Eremin-Solenikov
@ 2010-08-31 10:49 ` Dmitry Eremin-Solenikov
2 siblings, 0 replies; 7+ messages in thread
From: Dmitry Eremin-Solenikov @ 2010-08-31 10:49 UTC (permalink / raw)
To: linux-input; +Cc: Dmitry Torokhov
Hello,
On Mon, Aug 16, 2010 at 3:26 PM, Dmitry Eremin-Solenikov
<dbaryshkov@gmail.com> wrote:
> On tqm85xx boards (and several others) keyboard and mice are connected to CPU via
> special controller multiplexing KBD and MS traffic into single UART.
What about this two patches? Are they going to be included for 2.6.37?
Thank you for your answer.
> Changes since v1:
>
> * Rewrote serio to use iterative algorithms instead of recursion for serio
> tree traversal
>
> * Changed ps2mult to register as SERIO_8042, not it's own special type
>
> * Small cleanup in ps2mult
>
> --
> With best wishes
> Dmitry
>
>
--
With best wishes
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] serio: add support for PS2Mult multiplexer protocol
2010-08-16 11:26 ` [PATCH 2/2] serio: add support for PS2Mult multiplexer protocol Dmitry Eremin-Solenikov
@ 2010-09-08 5:35 ` Dmitry Torokhov
0 siblings, 0 replies; 7+ messages in thread
From: Dmitry Torokhov @ 2010-09-08 5:35 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: linux-input
Hi Dmitry,
On Mon, Aug 16, 2010 at 03:26:37PM +0400, Dmitry Eremin-Solenikov wrote:
> PS2Mult is a simple serial protocol used for multiplexing several PS/2 streams
> into one serial data stream. It's used e.g. on TQM85xx serie of boards.
>
> Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
> ---
> drivers/input/serio/Kconfig | 8 ++
> drivers/input/serio/Makefile | 1 +
> drivers/input/serio/ps2mult.c | 265 +++++++++++++++++++++++++++++++++++++++++
> include/linux/serio.h | 2 +
> 4 files changed, 276 insertions(+), 0 deletions(-)
> create mode 100644 drivers/input/serio/ps2mult.c
>
> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
> index 3bfe8fa..63f4658 100644
> --- a/drivers/input/serio/Kconfig
> +++ b/drivers/input/serio/Kconfig
> @@ -226,4 +226,12 @@ config SERIO_AMS_DELTA
> To compile this driver as a module, choose M here;
> the module will be called ams_delta_serio.
>
> +config SERIO_PS2MULT
> + tristate "TQC PS/2 multiplexer"
> + help
> + Say Y here if you have the PS/2 line multiplexer like present on TQC boads
> +
> + To compile this driver as a module, choose M here: the
> + module will be called ps2mult.
> +
> endif
> diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
> index 84c80bf..26714c5 100644
> --- a/drivers/input/serio/Makefile
> +++ b/drivers/input/serio/Makefile
> @@ -24,3 +24,4 @@ obj-$(CONFIG_SERIO_RAW) += serio_raw.o
> obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o
> obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o
> obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
> +obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
> diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
> new file mode 100644
> index 0000000..e9b7951
> --- /dev/null
> +++ b/drivers/input/serio/ps2mult.c
> @@ -0,0 +1,265 @@
> +/*
> + * TQC PS/2 Multiplexer driver
> + *
> + * Copyright (C) 2010 Dmitry Eremin-Solenikov
> + *
> + * 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.
> + */
> +
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/serio.h>
> +
> +MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
> +MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
> +MODULE_LICENSE("GPL");
> +
> +#define PS2MULT_KB_SELECTOR 0xA0
> +#define PS2MULT_MS_SELECTOR 0xA1
> +#define PS2MULT_ESCAPE 0x7D
> +#define PS2MULT_BSYNC 0x7E
> +#define PS2MULT_SESSION_START 0x55
> +#define PS2MULT_SESSION_END 0x56
> +
> +struct ps2mult_port {
> + struct serio *serio;
> + unsigned char sel;
> + unsigned char port;
> +};
> +
> +#define PS2MULT_NUM_PORTS 2
> +
> +struct ps2mult {
> + struct serio *serio;
> + struct ps2mult_port ports[PS2MULT_NUM_PORTS];
> +
> + spinlock_t lock;
> + unsigned char cur_out_port;
> + unsigned char cur_in_port;
I wonder if instead of indices you should make them serio * pointers -
it will save a few cycles at the expense of about 14 bytes in the
structure.
> + unsigned escape:1;
bool please.
> +};
> +
> +static unsigned char ps2mult_selectors[PS2MULT_NUM_PORTS] = {
> + PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
> +};
> +
> +static struct serio_device_id ps2mult_serio_ids[] = {
> + {
> + .type = SERIO_RS232,
> + .proto = SERIO_PS2MULT,
> + .id = SERIO_ANY,
> + .extra = SERIO_ANY,
> + },
> + { 0 }
> +};
> +
> +MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
> +
> +static int ps2mult_serio_write(struct serio *serio, unsigned char data)
> +{
> + struct ps2mult *psm = serio_get_drvdata(serio->parent);
> + struct ps2mult_port *psmp = serio->port_data;
> + bool need_escape;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&psm->lock, flags);
> + if (psm->cur_out_port != psmp->port) {
> + psm->serio->write(psm->serio, psmp->sel);
> + psm->cur_out_port = psmp->port;
> + dev_dbg(&serio->dev, "switched to sel %02x\n", psmp->sel);
> + }
> +
> + need_escape = data == PS2MULT_ESCAPE
> + || data == PS2MULT_BSYNC
> + || data == PS2MULT_SESSION_START
> + || data == PS2MULT_SESSION_END
> + || memchr(ps2mult_selectors, data, PS2MULT_NUM_PORTS);
Maybe pull all commands into ps2mult_ctrl_bytes[] array and use single
need_escape = memchr(ps2mult_ctrl_bytes, data, sizeof(ps2mult_ctrl_bytes));
?
> +
> + dev_dbg(&serio->dev, "write: %s%02x\n",
> + need_escape ? "ESC " : "", data);
> +
> + if (need_escape)
> + psm->serio->write(psm->serio, PS2MULT_ESCAPE);
> + psm->serio->write(psm->serio, data);
> +
> + spin_unlock_irqrestore(&psm->lock, flags);
> +
> + return 0;
> +}
> +
> +static int ps2mult_create_port(struct ps2mult *psm, int i)
> +{
> + struct serio *serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
> + if (!serio)
> + return -ENOMEM;
> +
> + strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
> + snprintf(serio->phys, sizeof(serio->phys),
> + "%s/port%d", psm->serio->phys, i);
> + serio->id.type = SERIO_PS2MULT_T;
I thought you were going to use SERIO_I8042?
> + serio->write = ps2mult_serio_write;
> + serio->parent = psm->serio;
You also need to define serio->stop() method that would mark appropriate
port as being dead and ensure that ps2mult_interrupt() does not try to use
ports that are being destroyed. Otherwise, during tear-down, when
children are being disconnected first, there potential to get interrupt
at a bad time.
> +
> + serio->port_data = &psm->ports[i];
> +
> + psm->ports[i].serio = serio;
> + psm->ports[i].port = i;
> + psm->ports[i].sel = ps2mult_selectors[i];
> +
> + serio_register_port(serio);
> + dev_info(&serio->dev, "%s port at %s\n", serio->name, psm->serio->phys);
> +
> + return 0;
> +}
> +
> +static int ps2mult_reconnect(struct serio *serio)
> +{
> + struct ps2mult *psm = serio_get_drvdata(serio);
> +
> + serio->write(serio, PS2MULT_SESSION_END);
> + serio->write(serio, PS2MULT_SESSION_START);
> + psm->cur_out_port = 0;
> + serio->write(serio, psm->ports[psm->cur_out_port].sel);
> +
> + return 0;
> +}
> +
> +static void ps2mult_disconnect(struct serio *serio)
> +{
> + struct ps2mult *psm = serio_get_drvdata(serio);
> + int i;
> +
> + serio->write(serio, PS2MULT_SESSION_END);
> +
> + for (i = 0; i < PS2MULT_NUM_PORTS; i++)
> + psm->ports[i].serio = NULL;
Meaningless since you free the structure couple of lines below.
> +
> + serio_close(serio);
> + serio_set_drvdata(serio, NULL);
> +
> + kfree(psm);
> +}
> +
> +static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
> +{
> + struct ps2mult *psm;
> + int i;
> + int rc;
> +
> + if (!serio->write)
> + return -EINVAL;
> +
> + psm = kzalloc(sizeof(*psm), GFP_KERNEL);
> + if (!psm)
> + return -ENOMEM;
> +
> + spin_lock_init(&psm->lock);
> + psm->serio = serio;
> +
> + serio_set_drvdata(serio, psm);
> + serio_open(serio, drv);
> +
> + for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
> + rc = ps2mult_create_port(psm, i);
> + if (rc)
> + goto err_out;
> + }
I'd move call to serio_open() here, just in case.
> +
> + rc = ps2mult_reconnect(serio);
> + if (rc)
> + goto err_out;
> +
> + return 0;
> +
> +err_out:
> + ps2mult_disconnect(serio);
> +
> + return rc;
> +}
> +
> +static void ps2mult_selector(struct ps2mult *psm, unsigned char data)
> +{
> + int i;
> +
> + dev_dbg(&psm->serio->dev, "Received selector %02x\n", data);
> +
> + spin_lock(&psm->lock);
Why do you need this lock?
> +
> + for (i = 0; i < PS2MULT_NUM_PORTS; i++)
> + if (psm->ports[i].sel == data) {
> + psm->cur_in_port = i;
> + break;
> + }
> +
> + spin_unlock(&psm->lock);
> +}
> +
> +static irqreturn_t ps2mult_interrupt(struct serio *serio, unsigned char data,
> + unsigned int flags)
> +{
> + struct ps2mult *psm = serio_get_drvdata(serio);
> +
> + dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, flags);
> + if (psm->escape) {
> + serio_interrupt(psm->ports[psm->cur_in_port].serio,
> + data, flags);
> + psm->escape = 0;
> + } else
> + switch (data) {
Incorrect indentation.
> + case PS2MULT_ESCAPE:
> + dev_dbg(&serio->dev, "ESCAPE\n");
> + psm->escape = 1;
> + break;
> + case PS2MULT_BSYNC:
> + dev_dbg(&serio->dev, "BSYNC\n");
> + psm->cur_in_port = psm->cur_out_port;
> + break;
> + case PS2MULT_SESSION_START:
> + dev_dbg(&serio->dev, "SS\n");
> + break;
> + case PS2MULT_SESSION_END:
> + dev_dbg(&serio->dev, "SE\n");
> + break;
> + case PS2MULT_KB_SELECTOR:
> + dev_dbg(&serio->dev, "KB\n");
> + ps2mult_selector(psm, data);
> + break;
> + case PS2MULT_MS_SELECTOR:
> + dev_dbg(&serio->dev, "MS\n");
> + ps2mult_selector(psm, data);
If there are only 2 ports why don't you do:
psm->current_in_port = psm->ports[PSM_MOUSE_IDX].serio;
?
> + break;
> + default:
> + serio_interrupt(psm->ports[psm->cur_in_port].serio,
> + data, flags);
> + }
> + return IRQ_HANDLED;
> +}
> +
> +static struct serio_driver ps2mult_drv = {
> + .driver = {
> + .name = "ps2mult",
> + },
> + .description = "TQC PS/2 Multiplexer driver",
> + .id_table = ps2mult_serio_ids,
> + .interrupt = ps2mult_interrupt,
> + .connect = ps2mult_connect,
> + .disconnect = ps2mult_disconnect,
> + .reconnect = ps2mult_reconnect,
> +};
> +
> +static int __init ps2mult_init(void)
> +{
> + return serio_register_driver(&ps2mult_drv);
> +}
> +
> +static void __exit ps2mult_exit(void)
> +{
> + serio_unregister_driver(&ps2mult_drv);
> +}
> +
> +module_init(ps2mult_init);
> +module_exit(ps2mult_exit);
> diff --git a/include/linux/serio.h b/include/linux/serio.h
> index 861a72a..7257e6c 100644
> --- a/include/linux/serio.h
> +++ b/include/linux/serio.h
> @@ -156,6 +156,7 @@ static inline void serio_continue_rx(struct serio *serio)
> #define SERIO_HIL_MLC 0x03
> #define SERIO_PS_PSTHRU 0x05
> #define SERIO_8042_XL 0x06
> +#define SERIO_PS2MULT_T 0x07
>
> /*
> * Serio protocols
> @@ -200,5 +201,6 @@ static inline void serio_continue_rx(struct serio *serio)
> #define SERIO_W8001 0x39
> #define SERIO_DYNAPRO 0x3a
> #define SERIO_HAMPSHIRE 0x3b
> +#define SERIO_PS2MULT 0x3c
>
> #endif
> --
> 1.7.1
>
Still pondering your other patch...
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] serio: support multiple child devices per single parent
2010-08-16 11:26 ` [PATCH 1/2] serio: support multiple child devices per single parent Dmitry Eremin-Solenikov
@ 2010-09-15 5:03 ` Dmitry Torokhov
2010-09-23 16:36 ` Dmitry Eremin-Solenikov
0 siblings, 1 reply; 7+ messages in thread
From: Dmitry Torokhov @ 2010-09-15 5:03 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov; +Cc: linux-input
[-- Attachment #1: Type: text/plain, Size: 9614 bytes --]
Hi Dmitry,
On Mon, Aug 16, 2010 at 03:26:36PM +0400, Dmitry Eremin-Solenikov wrote:
> @@ -50,6 +52,7 @@ struct serio {
> struct device dev;
>
> struct list_head node;
> + struct list_head internal; /* Used internally to avoid recursion */
I think we can do without this one, how about the patch below? (Depends
on synaptics changes in attached patch).
Thanks.
--
Dmitry
Input: serio - support multiple child devices per single parent
From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Some (rare) serio devices need to have multiple serio children. One of
the examples is PS/2 multiplexer present on several TQC STKxxx boards,
which connect PS/2 keyboard and mouse to single tty port.
Signed-off-by: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
drivers/input/mouse/psmouse-base.c | 4 +
drivers/input/serio/serio.c | 124 ++++++++++++++++++++++++------------
include/linux/serio.h | 4 +
3 files changed, 86 insertions(+), 46 deletions(-)
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 2f7408a..35356ba 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
if (!new_dev)
return -ENOMEM;
- while (serio->child) {
+ while (!list_empty(&serio->children)) {
if (++retry > 3) {
printk(KERN_WARNING
- "psmouse: failed to destroy child port, "
+ "psmouse: failed to destroy children ports, "
"protocol change aborted.\n");
input_free_device(new_dev);
return -EIO;
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index b2730b4..be0e93a 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -54,7 +54,7 @@ static struct bus_type serio_bus;
static void serio_add_port(struct serio *serio);
static int serio_reconnect_port(struct serio *serio);
static void serio_disconnect_port(struct serio *serio);
-static void serio_reconnect_chain(struct serio *serio);
+static void serio_reconnect_subtree(struct serio *serio);
static void serio_attach_driver(struct serio_driver *drv);
static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
@@ -150,7 +150,7 @@ static void serio_find_driver(struct serio *serio)
enum serio_event_type {
SERIO_RESCAN_PORT,
SERIO_RECONNECT_PORT,
- SERIO_RECONNECT_CHAIN,
+ SERIO_RECONNECT_SUBTREE,
SERIO_REGISTER_PORT,
SERIO_ATTACH_DRIVER,
};
@@ -237,8 +237,8 @@ static void serio_handle_event(struct work_struct *work)
serio_find_driver(event->object);
break;
- case SERIO_RECONNECT_CHAIN:
- serio_reconnect_chain(event->object);
+ case SERIO_RECONNECT_SUBTREE:
+ serio_reconnect_subtree(event->object);
break;
case SERIO_ATTACH_DRIVER:
@@ -328,12 +328,10 @@ static void serio_remove_pending_events(void *object)
}
/*
- * Destroy child serio port (if any) that has not been fully registered yet.
+ * Locate child serio port (if any) that has not been fully registered yet.
*
- * Note that we rely on the fact that port can have only one child and therefore
- * only one child registration request can be pending. Additionally, children
- * are registered by driver's connect() handler so there can't be a grandchild
- * pending registration together with a child.
+ * Children are registered by driver's connect() handler so there can't be a
+ * grandchild pending registration together with a child.
*/
static struct serio *serio_get_pending_child(struct serio *parent)
{
@@ -435,7 +433,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
if (!strncmp(buf, "none", count)) {
serio_disconnect_port(serio);
} else if (!strncmp(buf, "reconnect", count)) {
- serio_reconnect_chain(serio);
+ serio_reconnect_subtree(serio);
} else if (!strncmp(buf, "rescan", count)) {
serio_disconnect_port(serio);
serio_find_driver(serio);
@@ -502,6 +500,8 @@ static void serio_init_port(struct serio *serio)
__module_get(THIS_MODULE);
INIT_LIST_HEAD(&serio->node);
+ INIT_LIST_HEAD(&serio->child_node);
+ INIT_LIST_HEAD(&serio->children);
spin_lock_init(&serio->lock);
mutex_init(&serio->drv_mutex);
device_initialize(&serio->dev);
@@ -524,12 +524,13 @@ static void serio_init_port(struct serio *serio)
*/
static void serio_add_port(struct serio *serio)
{
+ struct serio *parent = serio->parent;
int error;
- if (serio->parent) {
- serio_pause_rx(serio->parent);
- serio->parent->child = serio;
- serio_continue_rx(serio->parent);
+ if (parent) {
+ serio_pause_rx(parent);
+ list_add_tail(&serio->child_node, &parent->children);
+ serio_continue_rx(parent);
}
list_add_tail(&serio->node, &serio_list);
@@ -545,15 +546,14 @@ static void serio_add_port(struct serio *serio)
}
/*
- * serio_destroy_port() completes deregistration process and removes
+ * serio_destroy_port() completes unregistration process and removes
* port from the system
*/
static void serio_destroy_port(struct serio *serio)
{
struct serio *child;
- child = serio_get_pending_child(serio);
- if (child) {
+ while ((child = serio_get_pending_child(serio)) != NULL) {
serio_remove_pending_events(child);
put_device(&child->dev);
}
@@ -563,7 +563,7 @@ static void serio_destroy_port(struct serio *serio)
if (serio->parent) {
serio_pause_rx(serio->parent);
- serio->parent->child = NULL;
+ list_del_init(&serio->child_node);
serio_continue_rx(serio->parent);
serio->parent = NULL;
}
@@ -595,46 +595,82 @@ static int serio_reconnect_port(struct serio *serio)
}
/*
- * Reconnect serio port and all its children (re-initialize attached devices)
+ * Reconnect serio port and all its children (re-initialize attached
+ * devices).
*/
-static void serio_reconnect_chain(struct serio *serio)
+static void serio_reconnect_subtree(struct serio *root)
{
+ struct serio *s = root;
+ int error;
+
do {
- if (serio_reconnect_port(serio)) {
- /* Ok, old children are now gone, we are done */
- break;
+ error = serio_reconnect_port(s);
+ if (!error) {
+ /*
+ * Reconnect was successful, move on to do the
+ * first child.
+ */
+ if (!list_empty(&s->children)) {
+ s = list_first_entry(&s->children,
+ struct serio, child_node);
+ continue;
+ }
}
- serio = serio->child;
- } while (serio);
+
+ /*
+ * Either it was a leaf node or reconnect failed and it
+ * became a leaf node. Continue reconnecting starting with
+ * the next sibling of the parent node.
+ */
+ while (s != root) {
+ struct serio *parent = s->parent;
+
+ if (!list_is_last(&s->child_node, &parent->children)) {
+ s = list_entry(s->child_node.next,
+ struct serio, child_node);
+ break;
+ }
+
+ s = parent;
+ }
+ } while (s != root);
}
/*
* serio_disconnect_port() unbinds a port from its driver. As a side effect
- * all child ports are unbound and destroyed.
+ * all children ports are unbound and destroyed.
*/
static void serio_disconnect_port(struct serio *serio)
{
- struct serio *s, *parent;
+ struct serio *s = serio;
+
+ /*
+ * Children ports should be disconnected and destroyed
+ * first; we travel the tree in depth-first order.
+ */
+ while (!list_empty(&serio->children)) {
+
+ /* Locate a leaf */
+ while (!list_empty(&s->children))
+ s = list_first_entry(&s->children,
+ struct serio, child_node);
- if (serio->child) {
/*
- * Children ports should be disconnected and destroyed
- * first, staring with the leaf one, since we don't want
- * to do recursion
+ * Prune this leaf node unless it is the one we
+ * started with.
*/
- for (s = serio; s->child; s = s->child)
- /* empty */;
-
- do {
- parent = s->parent;
+ if (s != serio) {
+ struct serio *parent = s->parent;
device_release_driver(&s->dev);
serio_destroy_port(s);
- } while ((s = parent) != serio);
+
+ s = parent;
+ }
}
/*
- * Ok, no children left, now disconnect this port
+ * OK, no children left, now disconnect this port.
*/
device_release_driver(&serio->dev);
}
@@ -647,7 +683,7 @@ EXPORT_SYMBOL(serio_rescan);
void serio_reconnect(struct serio *serio)
{
- serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
+ serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
}
EXPORT_SYMBOL(serio_reconnect);
@@ -675,14 +711,16 @@ void serio_unregister_port(struct serio *serio)
EXPORT_SYMBOL(serio_unregister_port);
/*
- * Safely unregisters child port if one is present.
+ * Safely unregisters children ports if they are present.
*/
void serio_unregister_child_port(struct serio *serio)
{
+ struct serio *s, *next;
+
mutex_lock(&serio_mutex);
- if (serio->child) {
- serio_disconnect_port(serio->child);
- serio_destroy_port(serio->child);
+ list_for_each_entry_safe(s, next, &serio->children, child_node) {
+ serio_disconnect_port(s);
+ serio_destroy_port(s);
}
mutex_unlock(&serio_mutex);
}
diff --git a/include/linux/serio.h b/include/linux/serio.h
index 111ad50..109b237 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -41,7 +41,9 @@ struct serio {
int (*start)(struct serio *);
void (*stop)(struct serio *);
- struct serio *parent, *child;
+ struct serio *parent;
+ struct list_head child_node; /* Entry in parent->children list */
+ struct list_head children;
unsigned int depth; /* level of nesting in serio hierarchy */
struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */
[-- Attachment #2: synaptics-ptport-handling.patch --]
[-- Type: text/plain, Size: 3449 bytes --]
Input: synaptics - simplify pass-through port handling
From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
There was too much knowledge about internals if serio in the pass-through
handling, clean it up.
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---
drivers/input/mouse/synaptics.c | 36 ++++++++++++++++++++++++++++++------
drivers/input/mouse/synaptics.h | 2 ++
2 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 96b70a4..60b7817 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c)
return 0;
}
-static inline int synaptics_is_pt_packet(unsigned char *buf)
+static int synaptics_pt_start(struct serio *serio)
+{
+ struct psmouse *parent = serio_get_drvdata(serio->parent);
+ struct synaptics_data *priv = parent->private;
+
+ serio_pause_rx(parent->ps2dev.serio);
+ priv->pt_port = serio;
+ serio_continue_rx(parent->ps2dev.serio);
+
+ return 0;
+}
+
+static void synaptics_pt_stop(struct serio *serio)
+{
+ struct psmouse *parent = serio_get_drvdata(serio->parent);
+ struct synaptics_data *priv = parent->private;
+
+ serio_pause_rx(parent->ps2dev.serio);
+ priv->pt_port = NULL;
+ serio_continue_rx(parent->ps2dev.serio);
+}
+
+static int synaptics_is_pt_packet(unsigned char *buf)
{
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
}
@@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
static void synaptics_pt_activate(struct psmouse *psmouse)
{
- struct serio *ptport = psmouse->ps2dev.serio->child;
- struct psmouse *child = serio_get_drvdata(ptport);
struct synaptics_data *priv = psmouse->private;
+ struct psmouse *child = serio_get_drvdata(priv->pt_port);
/* adjust the touchpad to child's choice of protocol */
if (child) {
@@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
serio->write = synaptics_pt_write;
+ serio->start = synaptics_pt_start;
+ serio->stop = synaptics_pt_stop;
serio->parent = psmouse->ps2dev.serio;
psmouse->pt_activate = synaptics_pt_activate;
@@ -578,9 +601,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
if (unlikely(priv->pkt_type == SYN_NEWABS))
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
- if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
- if (psmouse->ps2dev.serio->child)
- synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+ if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+ synaptics_is_pt_packet(psmouse->packet)) {
+ if (priv->pt_port)
+ synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
} else
synaptics_process_packet(psmouse);
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index b6aa7d2..613a365 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -110,6 +110,8 @@ struct synaptics_data {
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */
int scroll;
+
+ struct serio *pt_port; /* Pass-through serio port */
};
void synaptics_module_init(void);
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] serio: support multiple child devices per single parent
2010-09-15 5:03 ` Dmitry Torokhov
@ 2010-09-23 16:36 ` Dmitry Eremin-Solenikov
0 siblings, 0 replies; 7+ messages in thread
From: Dmitry Eremin-Solenikov @ 2010-09-23 16:36 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
On Tue, Sep 14, 2010 at 10:03:16PM -0700, Dmitry Torokhov wrote:
> Hi Dmitry,
>
> On Mon, Aug 16, 2010 at 03:26:36PM +0400, Dmitry Eremin-Solenikov wrote:
> > @@ -50,6 +52,7 @@ struct serio {
> > struct device dev;
> >
> > struct list_head node;
> > + struct list_head internal; /* Used internally to avoid recursion */
>
> I think we can do without this one, how about the patch below? (Depends
> on synaptics changes in attached patch).
Yes, it works for me. Sorry for the (long) delay in testing - I was on
vacation. I'll resubmit updated ps2mult driver in a few minutes.
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-09-23 16:36 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-08-16 11:26 [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
2010-08-16 11:26 ` [PATCH 1/2] serio: support multiple child devices per single parent Dmitry Eremin-Solenikov
2010-09-15 5:03 ` Dmitry Torokhov
2010-09-23 16:36 ` Dmitry Eremin-Solenikov
2010-08-16 11:26 ` [PATCH 2/2] serio: add support for PS2Mult multiplexer protocol Dmitry Eremin-Solenikov
2010-09-08 5:35 ` Dmitry Torokhov
2010-08-31 10:49 ` [PATCH 0/2 v2] Add drivers necessary to support PS/2 port on TQM85xx boards Dmitry Eremin-Solenikov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).