All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: linux-input@vger.kernel.org
Subject: [PATCH v3] serio: add support for PS2Mult multiplexer protocol
Date: Thu, 23 Sep 2010 20:44:45 +0400	[thread overview]
Message-ID: <1285260285-660-1-git-send-email-dbaryshkov@gmail.com> (raw)

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>
---

It actually depends on "serio: multiple children" patch. I'm not resending it
as you were the originator of the latest version of the patch.

 drivers/input/serio/Kconfig   |    8 +
 drivers/input/serio/Makefile  |    1 +
 drivers/input/serio/ps2mult.c |  283 +++++++++++++++++++++++++++++++++++++++++
 include/linux/serio.h         |    2 +
 4 files changed, 294 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..bd45e76
--- /dev/null
+++ b/drivers/input/serio/ps2mult.c
@@ -0,0 +1,283 @@
+/*
+ * 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;
+};
+
+#define PS2MULT_NUM_PORTS	2
+
+struct ps2mult {
+	struct serio *serio;
+	struct ps2mult_port ports[PS2MULT_NUM_PORTS];
+
+	spinlock_t lock;
+	struct serio *in_serio;
+	struct serio *out_serio;
+	bool escape;
+};
+
+/* First MUST com PS2MULT_NUM_PORTS selectors */
+static unsigned char ps2mult_controls[] = {
+	PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
+	PS2MULT_ESCAPE, PS2MULT_BSYNC,
+	PS2MULT_SESSION_START, PS2MULT_SESSION_END,
+};
+
+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->out_serio != serio) {
+		psm->serio->write(psm->serio, psmp->sel);
+		psm->out_serio = serio;
+		dev_dbg(&serio->dev, "switched to sel %02x\n", psmp->sel);
+	}
+
+	need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
+
+	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 void ps2mult_serio_stop(struct serio *serio)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio->parent);
+	struct ps2mult_port *psmp = serio->port_data;
+
+	unsigned long flags;
+
+	spin_lock_irqsave(&psm->lock, flags);
+
+	psmp->serio = NULL;
+	if (psm->in_serio == serio)
+		psm->in_serio = NULL;
+	if (psm->out_serio == serio)
+		psm->out_serio = NULL;
+
+	spin_unlock_irqrestore(&psm->lock, flags);
+
+}
+
+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_8042;
+	serio->id.proto = SERIO_PS2MULT;
+	serio->write = ps2mult_serio_write;
+	serio->stop = ps2mult_serio_stop;
+	serio->parent = psm->serio;
+
+	serio->port_data = &psm->ports[i];
+
+	psm->ports[i].serio = serio;
+	psm->ports[i].sel = ps2mult_controls[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);
+	unsigned long flags;
+
+	serio->write(serio, PS2MULT_SESSION_END);
+	serio->write(serio, PS2MULT_SESSION_START);
+
+	spin_lock_irqsave(&psm->lock, flags);
+	psm->out_serio = psm->ports[0].serio;
+	serio->write(serio, psm->ports[0].sel);
+	spin_unlock_irqrestore(&psm->lock, flags);
+
+	return 0;
+}
+
+static void ps2mult_disconnect(struct serio *serio)
+{
+	struct ps2mult *psm = serio_get_drvdata(serio);
+
+	serio->write(serio, PS2MULT_SESSION_END);
+
+	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);
+
+	for (i = 0; i <  PS2MULT_NUM_PORTS; i++) {
+		rc = ps2mult_create_port(psm, i);
+		if (rc)
+			goto err_out;
+	}
+
+	serio_open(serio, drv);
+
+	rc = ps2mult_reconnect(serio);
+	if (rc)
+		goto err_out;
+
+	return 0;
+
+err_out:
+	ps2mult_disconnect(serio);
+
+	return rc;
+}
+
+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) {
+		spin_lock(&psm->lock);
+		if (psm->in_serio)
+			serio_interrupt(psm->in_serio, data, flags);
+		spin_unlock(&psm->lock);
+
+		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");
+			spin_lock(&psm->lock);
+			psm->in_serio = psm->out_serio;
+			spin_unlock(&psm->lock);
+			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");
+
+			spin_lock(&psm->lock);
+			psm->in_serio = psm->ports[0].serio;
+			spin_unlock(&psm->lock);
+
+			break;
+		case PS2MULT_MS_SELECTOR:
+			dev_dbg(&serio->dev, "MS\n");
+
+			spin_lock(&psm->lock);
+			psm->in_serio = psm->ports[1].serio;
+			spin_unlock(&psm->lock);
+
+			break;
+		default:
+			spin_lock(&psm->lock);
+			if (psm->in_serio)
+				serio_interrupt(psm->in_serio, data, flags);
+			spin_unlock(&psm->lock);
+		}
+	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 8e495ba..136863c 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -155,6 +155,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
@@ -199,5 +200,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


             reply	other threads:[~2010-09-23 16:45 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-09-23 16:44 Dmitry Eremin-Solenikov [this message]
2010-09-29 12:45 ` [PATCH v3] serio: add support for PS2Mult multiplexer protocol Dmitry Eremin-Solenikov
2010-09-30  6:25   ` Dmitry Torokhov
2010-10-07 15:19     ` Dmitry Eremin-Solenikov
2010-10-07 16:36       ` Dmitry Torokhov
2010-10-08  8:50         ` Dmitry Eremin-Solenikov
2010-10-14  9:43           ` Dmitry Eremin-Solenikov
2010-10-14 14:23           ` Dmitry Torokhov
2010-10-18 11:24             ` Dmitry Eremin-Solenikov
2010-10-18 15:56               ` Dmitry Torokhov
2010-10-18 16:11                 ` Dmitry Torokhov
2010-10-21 20:54                   ` Dmitry Eremin-Solenikov
2010-10-22  4:57                     ` Dmitry Torokhov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1285260285-660-1-git-send-email-dbaryshkov@gmail.com \
    --to=dbaryshkov@gmail.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.