linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Network Virtual Terminal
@ 2011-01-03 15:15 Rodolfo Giometti
  2011-01-03 15:15 ` [PATCH] char nvtty: Network Virtual Terminal support Rodolfo Giometti
  2011-01-04 17:13 ` Network Virtual Terminal Hans-Peter Jansen
  0 siblings, 2 replies; 9+ messages in thread
From: Rodolfo Giometti @ 2011-01-03 15:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: Russell Coker, Greg Kroah-Hartman

Hello,

here my implementation of Network Virtual terminals (NVT tty)
according to RFC 854 and RFC 2217... actually this is the client side
part since as remote server I used sredird
(http://freshmeat.net/projects/sredird/).

I tested the code under the following configuration:

        ------------------+
             +---------+  |
             | minicom |  |
             +---------+  |
                  |       | local PC with no
                  v       | serial ports
             /dev/nvtty0  |
                  |       |
        ------------------+
                  |
                  v
             /\/\/\/\/\
            | network |
             \/\/\/\/\/
                  |
        ------------------+
                  |       |
                  v       |
             +---------+  |
             | sredird |  |
             +---------+  | remote PC with
                  |       | serial ports
                  v       |
             /dev/ttyS0   |
                  |       |
	------------------+
                  |
                  v
         embedded PC with
         a serial console

however it could work on a different schema I suppose.

By using minicom I can setup the serial settings of the /dev/ttyS0
device through the /dev/nvtty0 virtual device, then the communication
with the embedded PC can start as if it was directly connected with my
local machine.

Ciao,

Rodolfo




^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] char nvtty: Network Virtual Terminal support
  2011-01-03 15:15 Network Virtual Terminal Rodolfo Giometti
@ 2011-01-03 15:15 ` Rodolfo Giometti
  2011-01-03 17:14   ` Alan Cox
  2011-01-03 23:32   ` Randy Dunlap
  2011-01-04 17:13 ` Network Virtual Terminal Hans-Peter Jansen
  1 sibling, 2 replies; 9+ messages in thread
From: Rodolfo Giometti @ 2011-01-03 15:15 UTC (permalink / raw)
  To: linux-kernel; +Cc: Russell Coker, Greg Kroah-Hartman, Rodolfo Giometti

A Network Virtual terminal (NVT tty) is a software device consisting
of one halves: a client device, which is identical to a physical
terminal, who, is turn, get connected with a remote server where real
tty devices are located.

These devices are specified by RFC 854 and RFC 2217 and ther name into
the system is /dev/nvttyX (by default you have 4 devices).

By using these devices and a proper compatible server (not included
here but you can use sredird) you can get access to a remote tty
device as the tty device itself was conneted with your local host.
All data and settings are sent and received through the network.

Signed-off-by: Rodolfo Giometti <giometti@linux.it>
---
 Documentation/ABI/testing/sysfs-nvtty |   35 +
 drivers/char/Kconfig                  |   22 +
 drivers/char/Makefile                 |    1 +
 drivers/char/nvtty.c                  | 1557 +++++++++++++++++++++++++++++++++
 4 files changed, 1615 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-nvtty
 create mode 100644 drivers/char/nvtty.c

diff --git a/Documentation/ABI/testing/sysfs-nvtty b/Documentation/ABI/testing/sysfs-nvtty
new file mode 100644
index 0000000..a52d319
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-nvtty
@@ -0,0 +1,35 @@
+What:		/sys/class/tty/nvttyX/
+Date:		January 2011
+Contact:	Rodolfo Giometti <giometti@linux.it>
+Description:
+		The /sys/class/tty/nvttyX/ directory is related to X-th
+		NVT tty device into the system. Each directory will
+		contain files to manage and control its NVT tty device.
+
+What:		/sys/class/tty/nvttyX/connection
+Date:		January 2011
+Contact:	Rodolfo Giometti <giometti@linux.it>
+Description:
+		The /sys/class/tty/nvttyX/connection file reports 1 if the
+		related NVT tty device is connected with remote server and
+		0 otherwise.
+
+What:		/sys/class/tty/nvttyX/ip
+Date:		January 2011
+Contact:	Rodolfo Giometti <giometti@linux.it>
+Description:
+		The /sys/class/tty/nvttyX/ip file reports the IP address
+		of the remote server where to connecto to.
+		User should change this value, when no connection is active,
+		in order to select a different remote server.
+		Default is localhost (127.0.0.1).
+
+What:		/sys/class/tty/nvttyX/port
+Date:		January 2011
+Contact:	Rodolfo Giometti <giometti@linux.it>
+Description:
+		The /sys/class/tty/nvttyX/port file reports the IP port number
+		of the remote server where to connecto to.
+		User should change this value, when no connection is active,
+		in order to select a different port.
+		Default is 32769.
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 43d3395..13775be 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -451,6 +451,28 @@ config UNIX98_PTYS
 	  All modern Linux systems use the Unix98 ptys.  Say Y unless
 	  you're on an embedded system and want to conserve memory.
 
+config NVT_TTY
+	tristate "Network Virtual Terminal"
+	default m
+	---help---
+	  A Network Virtual terminal (NVT tty) is a software device
+	  consisting of one halves: a client device, which is
+	  identical to a physical terminal, who, is turn, get
+	  connected with a remote server where real tty devices are
+	  located.
+
+	  These devices are specified by RFC 854 and RFC 2217 and ther
+	  name into the system is /dev/nvttyX (by default you have 4
+	  devices).
+
+	  By using these devices and a proper compatible server (not
+	  included here but you can use sredird) you can get access to
+	  a remote tty device as the tty device itself was conneted
+	  with your local host.  All data and settings are sent and
+	  received through the network.
+
+	  If unsure, say M.
+
 config DEVPTS_MULTIPLE_INSTANCES
 	bool "Support multiple instances of devpts"
 	depends on UNIX98_PTYS
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index ba53ec9..7deba37 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -4,6 +4,7 @@
 
 obj-y				+= mem.o random.o
 obj-$(CONFIG_TTY_PRINTK)	+= ttyprintk.o
+obj-$(CONFIG_NVT_TTY)		+= nvtty.o
 obj-y				+= misc.o
 obj-$(CONFIG_BFIN_JTAG_COMM)	+= bfin_jtag_comm.o
 obj-$(CONFIG_MVME147_SCC)	+= generic_serial.o vme_scc.o
diff --git a/drivers/char/nvtty.c b/drivers/char/nvtty.c
new file mode 100644
index 0000000..14b9fb9
--- /dev/null
+++ b/drivers/char/nvtty.c
@@ -0,0 +1,1557 @@
+/*
+ * Network Virtual Terminal (RFC 854) with Com Port option (RFC 2217)
+ *
+ * Copyright (C) 2011   Rodolfo Giometti <giometti@linux.it>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code has been derived from cyclades-serial-client by Cyclades and
+ * Russell Coker <russell@coker.com.au>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kthread.h>
+#include <linux/net.h>
+#include <linux/in.h>
+
+/*
+ * Printing stuff
+ */
+
+#if defined(DEBUG)
+#define nvtty_info(info, fmt, args...)	dev_info((info)->tty->dev, \
+				"%s[%d]: " fmt "\n", __func__, __LINE__ , \
+								## args)
+#define nvtty_err(info, fmt, args...)	dev_err((info)->tty->dev, \
+				"%s[%d]: " fmt "\n", __func__, __LINE__ , \
+								## args)
+#define nvtty_dbg(info, fmt, args...)	dev_dbg((info)->tty->dev, \
+				"%s[%d]: " fmt "\n", __func__, __LINE__ , \
+								## args)
+#else
+#define nvtty_info(info, fmt, args...)	dev_info((info)->tty->dev, \
+				"%s: " fmt "\n" , __func__ , ## args)
+#define nvtty_err(info, fmt, args...)	dev_err((info)->tty->dev, \
+				"%s: " fmt "\n" , __func__ , ## args)
+#define nvtty_dbg(info, fmt, args...)	dev_dbg((info)->tty->dev, \
+				"%s: " fmt "\n" , __func__ , ## args)
+#endif
+
+/*
+ * RFC stuff
+ */
+
+/* Telnet Special chars */
+#define IAC				255
+#define WILL				251
+#define WONT				252
+#define DO				253
+#define DONT				254
+#define SE				240
+#define SB				250
+
+/* Telnet receiver substates */
+enum s_state {
+	S_DATA,
+	S_IAC,
+	S_WILL,
+	S_WONT,
+	S_DO,
+	S_DONT,
+	S_SB,
+	S_SE
+};
+
+/* Telnet Options stuff */
+enum nvt_opt {
+	NVT_BINARY			= 0,
+	NVT_ECHO			= 1,
+	NVT_SUPP_GO_AHEAD		= 3,
+	NVT_COM_PORT_OPTION		= 44,
+	__NVT_NUMOPTS
+};
+
+#define I_WILL				0x01 /* I desire to support it */
+#define I_DO				0x02 /* I do support it */
+#define I_SENT				0x04 /* I desire and already sent it */
+#define HE_WILL				0x10 /* I want he supports it */
+#define HE_DOES				0x20 /* He supports it */
+#define HE_RECV				0x40 /* He recv my response */
+
+#define I_WANT_TO_SUPPORT(info, opt)	((info)->option[opt] & I_WILL)
+#define I_DO_SUPPORT(info, opt)		((info)->option[opt] & I_DO)
+#define I_SENT_IT(info, opt)		((info)->option[opt] & I_SENT)
+
+#define HE_MAY_SUPPORT(info, opt)	((info)->option[opt] & HE_WILL)
+#define HE_DOES_SUPPORT(info, opt)	((info)->option[opt] & HE_DOES)
+#define HE_RECV_IT(info, opt)		((info)->option[opt] & HE_RECV)
+
+#define SET_I_WANT_TO_SUPPORT(info, opt)((info)->option[opt] |= I_WILL)
+#define SET_I_DO_SUPPORT(info, opt)	((info)->option[opt] |= I_DO)
+#define SET_I_SENT_IT(info, opt)	((info)->option[opt] |= I_SENT)
+
+#define SET_HE_MAY_SUPPORT(info, opt)	((info)->option[opt] |= HE_WILL)
+#define SET_HE_DOES_SUPPORT(info, opt)	((info)->option[opt] |= HE_DOES)
+#define SET_HE_RECV_IT(info, opt)	((info)->option[opt] |= HE_RECV)
+
+#define CLR_I_WANT_TO_SUPPORT(info, opt)((info)->option[opt] &= ~I_WILL)
+#define CLR_I_DO_SUPPORT(info, opt)	((info)->option[opt] &= ~I_DO)
+#define CLR_I_SENT_IT(info, opt)	((info)->option[opt] &= ~I_SENT)
+
+#define CLR_HE_MAY_SUPPORT(info, opt)	((info)->option[opt] &= ~HE_WILL)
+#define CLR_HE_DOES_SUPPORT(info, opt)	((info)->option[opt] &= ~HE_DOES)
+#define CLR_HE_RECV_IT(info, opt)	((info)->option[opt] &= ~HE_RECV)
+
+/* Com port commands and notifications */
+
+/* Client codes  */
+enum nvt_c_code {
+	USR_COM_SIGNATURE,		/* none, RFC2217 says */
+	USR_COM_SET_BAUDRATE,
+	USR_COM_SET_DATASIZE,
+	USR_COM_SET_PARITY,
+	USR_COM_SET_STOPSIZE,
+	USR_COM_SET_CONTROL,
+	USR_COM_NOTIFY_LINESTATE,
+	USR_COM_NOTIFY_MODEMSTATE,
+	USR_COM_FLOWCONTROL_SUSPEND,
+	USR_COM_FLOWCONTROL_RESUME,
+	USR_COM_SET_LINESTATE_MASK,
+	USR_COM_SET_MODEMSTATE_MASK,
+	USR_COM_PURGE_DATA,
+	__USR_NUMCOMS
+};
+
+#define SET_CMD_ACTIVE(info, n)		\
+		init_completion(&((info)->cmd[n]))
+#define CLR_CMD_ACTIVE(info, n)		\
+		complete_all(&((info)->cmd[n]))
+#define WAIT_CMD_ACTIVE(info, n)	\
+		wait_for_completion_interruptible((&(info)->cmd[n]))
+
+/*
+ * State control of NVT Com Port Commands
+ */
+
+/* SET-BAUDRATE Stuff */
+# define COM_BAUD_REQ			0
+# define COM_BAUD(x)			(x)
+
+/* SET-DATASIZE Stuff */
+# define COM_DSIZE_REQ			0
+# define COM_DSIZE(x)			(x)
+
+/* SET-PARITY Stuff */
+enum parity_set {
+	COM_PARITY_REQ,
+	COM_PARITY_NONE,
+	COM_PARITY_ODD,
+	COM_PARITY_EVEN,
+	COM_PARITY_MARK,
+	COM_PARITY_SPACE
+};
+
+/* COM-STOPSIZE Stuff */
+enum stopsize_set {
+	COM_SSIZE_REQ,
+	COM_SSIZE_ONE,
+	COM_SSIZE_TWO,
+	COM_SSIZE_1DOT5
+};
+
+/* SET-CONTROL Stuff */
+enum control_set {
+	COM_OFLOW_REQ,
+	COM_OFLOW_NONE,
+	COM_OFLOW_SOFT,
+	COM_OFLOW_HARD,
+
+	COM_BREAK_REQ,
+	COM_BREAK_ON,
+	COM_BREAK_OFF,
+
+	COM_DTR_REQ,
+	COM_DTR_ON,
+	COM_DTR_OFF,
+
+	COM_RTS_REQ,
+	COM_RTS_ON,
+	COM_RTS_OFF,
+
+	COM_IFLOW_REQ,
+	COM_IFLOW_NONE,
+	COM_IFLOW_SOFT,
+	COM_IFLOW_HARD,
+
+	COM_DCD_FLOW,
+	COM_DTR_FLOW,
+	COM_DSR_FLOW
+};
+
+#define COM_FLOW_REQ			COM_OFLOW_REQ
+#define COM_FLOW_NONE			COM_OFLOW_NONE
+#define COM_FLOW_SOFT			COM_OFLOW_SOFT
+#define COM_FLOW_HARD			COM_OFLOW_HARD
+
+/* LINESTATE MASK (COM-LINESTATE-MASK command / NOTIFY-LINESTATE notification*/
+#define LINE_TIMEOUT_ERROR		128
+#define LINE_SHIFTREG_EMPTY		64
+#define LINE_HOLDREG_EMPTY		32
+#define LINE_BREAK_ERROR		16
+#define LINE_FRAME_ERROR		8
+#define LINE_PARITY_ERROR		4
+#define LINE_OVERRUN_ERROR		2
+#define LINE_DATA_READY			1
+
+/* MODEMSTATE MASK (SET-MODEMSTATE-MASK / NOTIFY-MODEMSTATE */
+#define MODEM_DCD			128
+#define MODEM_RI			64
+#define MODEM_DSR			32
+#define MODEM_CTS			16
+#define MODEM_DELTA_DCD			8
+#define MODEM_TRAIL_RI			4
+#define MODEM_DELTA_DSR			2
+#define MODEM_DELTA_CTS			1
+
+/* PURGE-DATA Stuff */
+enum purgedata_set {
+	COM_PURGE_RECV			= 1,
+	COM_PURGE_XMIT,
+	COM_PURGE_BOTH
+};
+
+/*
+ * Driver defines & structs
+ */
+
+#define DRIVER_NAME			"nvtty"
+#define DRIVER_VERSION			"1.0.0"
+
+#define NVTTY_MAJOR			240
+#define NVTTY_MINORS			4
+#define NVTTY_TCP_ADDR			INADDR_LOOPBACK
+#define NVTTY_TCP_PORT			32769
+
+#define PUTDATA_MAXSIZE			512
+#define SUBOPT_MAXSIZE			64
+
+static int major = NVTTY_MAJOR;
+module_param(major, int, 0644);
+MODULE_PARM_DESC(major, "NVT devices' major number (default: " \
+			__stringify(NVTTY_MAJOR) ")");
+static int minors = NVTTY_MINORS;
+module_param(minors, int, 0644);
+MODULE_PARM_DESC(minors, "number of NVT devices to initialize (default: " \
+			__stringify(NVTTY_MINORS) ")");
+
+
+struct nvtty_serial {
+	struct tty_struct *tty;
+	struct device *dev;
+	int open_count;
+	struct mutex mutex;
+	int index;
+
+	struct task_struct *task;
+	unsigned int task_is_running:1;
+
+	struct socket *sock;
+	u32 addr;
+	u16 port;
+	unsigned int connection_ok:1;
+
+	struct completion init_done;
+
+	enum s_state state;
+	enum nvt_opt option[__NVT_NUMOPTS];
+	u8 subopt[SUBOPT_MAXSIZE];
+	int subopt_size;
+
+	struct completion cmd[__USR_NUMCOMS];
+	int arg[__USR_NUMCOMS];
+
+	u8 modemstate;
+};
+
+static struct nvtty_serial *nvtty_info;
+
+/*
+ * Network functions
+ */
+
+static int net_recv(struct nvtty_serial *info,
+				unsigned char *buf, int size, unsigned flags)
+{
+	struct msghdr msg = { NULL, };
+	struct kvec iov = { (void *) buf, size };
+	int ret = kernel_recvmsg(info->sock, &msg, &iov, 1, size, flags);
+
+	return ret;
+}
+
+static int net_send(struct nvtty_serial *info,
+			const unsigned char *buf, int size, unsigned flags)
+{
+	struct msghdr msg = { .msg_flags = flags };
+	struct kvec iov = { (void *) buf, size };
+
+	return kernel_sendmsg(info->sock, &msg, &iov, 1, size);
+}
+
+static int net_connect(struct nvtty_serial *info)
+{
+	struct sockaddr_in src, dest;
+	int ret;
+
+	ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &info->sock);
+	if (ret < 0)
+		goto exit;
+
+	src.sin_family = AF_INET;
+	src.sin_addr.s_addr = htonl(INADDR_ANY);
+	src.sin_port = htons(0);
+
+	ret = kernel_bind(info->sock, (struct sockaddr *) &src, sizeof(src));
+	if (ret) {
+		nvtty_err(info, "bind failed with %08x at address %08lx",
+			 ret, INADDR_ANY);
+		sock_release(info->sock);
+		goto exit;
+	}
+
+	dest.sin_family = AF_INET;
+	dest.sin_addr.s_addr = htonl(info->addr);
+	dest.sin_port = htons(info->port);
+
+	nvtty_dbg(info, "trying to connect with %d.%d.%d.%d:%d",
+				(info->addr & 0xff000000) >> 24,
+				(info->addr & 0x00ff0000) >> 16,
+				(info->addr & 0x0000ff00) >> 8,
+				info->addr & 0x000000ff, info->port);
+	ret = kernel_connect(info->sock,
+			(struct sockaddr *) &dest, sizeof(dest), 0);
+	if (ret == -EINPROGRESS)
+		ret = 0;
+
+exit:
+	return ret;
+}
+
+static void net_disconnect(struct nvtty_serial *info)
+{
+	kernel_sock_shutdown(info->sock, SHUT_RDWR);
+}
+
+/*
+ * Local TTY functionsSIZE
+ */
+
+#define TEST_SET_BAUDRATE(b)	case b: tty->termios->c_cflag |= B ## b ; break
+static void nvtty_set_baudrate(struct nvtty_serial *info, unsigned int baudrate)
+{
+	struct tty_struct *tty = info->tty;
+
+	nvtty_dbg(info, "baudrate=%d", baudrate);
+
+	tty->termios->c_cflag &= ~CBAUD;
+	switch (baudrate) {
+	TEST_SET_BAUDRATE(50);
+	TEST_SET_BAUDRATE(75);
+	TEST_SET_BAUDRATE(110);
+	TEST_SET_BAUDRATE(134);
+	TEST_SET_BAUDRATE(150);
+	TEST_SET_BAUDRATE(200);
+	TEST_SET_BAUDRATE(300);
+	TEST_SET_BAUDRATE(600);
+	TEST_SET_BAUDRATE(1200);
+	TEST_SET_BAUDRATE(1800);
+	TEST_SET_BAUDRATE(2400);
+	TEST_SET_BAUDRATE(4800);
+	TEST_SET_BAUDRATE(9600);
+	TEST_SET_BAUDRATE(19200);
+	TEST_SET_BAUDRATE(38400);
+	TEST_SET_BAUDRATE(57600);
+	TEST_SET_BAUDRATE(115200);
+	TEST_SET_BAUDRATE(230400);
+	TEST_SET_BAUDRATE(460800);
+	TEST_SET_BAUDRATE(500000);
+	TEST_SET_BAUDRATE(576000);
+	TEST_SET_BAUDRATE(921600);
+	TEST_SET_BAUDRATE(1000000);
+	TEST_SET_BAUDRATE(1152000);
+	TEST_SET_BAUDRATE(1500000);
+	TEST_SET_BAUDRATE(2000000);
+	TEST_SET_BAUDRATE(2500000);
+	TEST_SET_BAUDRATE(3000000);
+	TEST_SET_BAUDRATE(3500000);
+	TEST_SET_BAUDRATE(4000000);
+	default:
+		tty->termios->c_cflag = B0;
+	}
+}
+#undef TEST_SET_BAUDRATE
+
+#define TEST_SET_DATASIZE(b)	case b: tty->termios->c_cflag |= CS ## b ; break
+static void nvtty_set_datasize(struct nvtty_serial *info, unsigned int datasize)
+{
+	struct tty_struct *tty = info->tty;
+
+	nvtty_dbg(info, "datasize=%d", datasize);
+
+	tty->termios->c_cflag &= ~CSIZE;
+	switch (datasize) {
+	TEST_SET_DATASIZE(5);
+	TEST_SET_DATASIZE(6);
+	TEST_SET_DATASIZE(7);
+	TEST_SET_DATASIZE(8);
+	default:
+		tty->termios->c_cflag = CS5;
+	}
+}
+#undef TEST_SET_DATASIZE
+
+static void nvtty_set_parity(struct nvtty_serial *info, unsigned int parity)
+{
+	struct tty_struct *tty = info->tty;
+
+	nvtty_dbg(info, "parity=%d", parity);
+
+	tty->termios->c_cflag &= ~(PARENB | PARODD);
+	switch (parity) {
+	case COM_PARITY_ODD:
+		tty->termios->c_cflag |= PARENB | PARODD;
+		break;
+	case COM_PARITY_EVEN:
+		tty->termios->c_cflag |= PARENB;
+		break;
+	case COM_PARITY_NONE:
+	default:
+		/* nop */;
+	}
+}
+
+static void nvtty_set_stopsize(struct nvtty_serial *info, unsigned int stopsize)
+{
+	struct tty_struct *tty = info->tty;
+
+	nvtty_dbg(info, "stopsize=%d", stopsize);
+
+	tty->termios->c_cflag &= ~CSTOPB;
+	switch (stopsize) {
+	case COM_SSIZE_TWO:
+		tty->termios->c_cflag |= CSTOPB;
+		break;
+	default:
+	case COM_SSIZE_ONE:
+		/* nop */;
+	}
+}
+
+static void nvtty_set_control(struct nvtty_serial *info, unsigned int control)
+{
+	struct tty_struct *tty = info->tty;
+
+	nvtty_dbg(info, "control=%d", control);
+
+		tty->termios->c_cflag &= ~CRTSCTS;
+		switch (control) {
+		case COM_OFLOW_SOFT:
+		case COM_OFLOW_HARD:
+			tty->termios->c_cflag |= CRTSCTS;
+			break;
+		default:
+		case COM_OFLOW_NONE:
+			/* nop */;
+		}
+	}
+
+/*
+ * TTY push function
+ */
+
+static void nvtty_push(struct nvtty_serial *info, u8 rx)
+{
+	struct tty_struct *tty = info->tty;
+
+	tty_insert_flip_char(tty, rx, TTY_NORMAL);
+	tty_schedule_flip(tty);
+}
+
+/*
+ * Telnet Protocol Internal Routines
+ */
+
+static int send_option(struct nvtty_serial *info, int type, int opt)
+{
+	u8 buf[] = { IAC, type, opt };
+
+	return net_send(info, buf, ARRAY_SIZE(buf), 0);
+}
+
+#define send_do(info, opt)		send_option(info, DO, opt)
+#define send_dont(info, opt)		send_option(info, DONT, opt)
+#define send_will(info, opt)		send_option(info, WILL, opt)
+#define send_wont(info, opt)		send_option(info, WONT, opt)
+
+static int do_option(struct nvtty_serial *info, int opt)
+{
+	int ret;
+
+	if (I_WANT_TO_SUPPORT(info, opt)) {
+		SET_I_DO_SUPPORT(info, opt);
+		if (!I_SENT_IT(info, opt)) {
+			ret = send_will(info, opt);
+			if (ret < 0) {
+				nvtty_err(info, "error sending WILL %d", opt);
+				return ret;
+			}
+			SET_I_SENT_IT(info, opt);
+		}
+	} else {
+		ret = send_wont(info, opt);
+		if (ret < 0) {
+			nvtty_err(info, "error sending WONT %d", opt);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void dont_option(struct nvtty_serial *info, int opt)
+{
+	CLR_I_DO_SUPPORT(info, opt);
+}
+
+static int will_option(struct nvtty_serial *info, int opt)
+{
+	int ret;
+
+	if (HE_MAY_SUPPORT(info, opt)) {
+		SET_HE_DOES_SUPPORT(info, opt);
+		if (!HE_RECV_IT(info, opt)) {
+			ret = send_do(info, opt);
+			if (ret < 0) {
+				nvtty_err(info, "error sending DO %d", opt);
+				return ret;
+			}
+			SET_HE_RECV_IT(info, opt);
+		}
+	} else {
+		ret = send_dont(info, opt);
+		if (ret < 0) {
+			nvtty_err(info, "error sending DONT %d", opt);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void wont_option(struct nvtty_serial *info, int opt)
+{
+	CLR_HE_DOES_SUPPORT(info, opt);
+}
+
+static int handle_comport_command(struct nvtty_serial *info)
+{
+	struct tty_struct *tty = info->tty;
+	int idx = 1;
+	u8 cmd = info->subopt[idx++];
+	int data, is_async = 0;
+
+	nvtty_dbg(info, "cmd=%d", cmd);
+	if (cmd < 100) {
+		nvtty_err(info, "invalid remote command %d!", cmd);
+		return -1;
+	}
+	cmd -= 100;
+
+	switch (cmd) {
+	case USR_COM_SIGNATURE:
+	case USR_COM_FLOWCONTROL_SUSPEND:
+	case USR_COM_FLOWCONTROL_RESUME:
+		nvtty_dbg(info, "SIGNATURE/FLOWCONTROL_xxx");
+		/* nop */
+		break;
+
+	case USR_COM_SET_BAUDRATE:
+		if (idx + 4 > info->subopt_size) {
+			nvtty_err(info, "invalid BAUDRATE data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = ntohl(*((u32 *) &info->subopt[idx]));
+		nvtty_set_baudrate(info, data);
+
+		break;
+
+	case USR_COM_SET_DATASIZE:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid DATASIZE data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_set_datasize(info, data);
+
+		break;
+
+	case USR_COM_SET_PARITY:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid PARITY data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_set_parity(info, data);
+
+		break;
+
+	case USR_COM_SET_STOPSIZE:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid STOPSIZE data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_set_stopsize(info, data);
+
+		break;
+
+	case USR_COM_SET_CONTROL:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid CONTROL data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_set_control(info, data);
+
+		break;
+
+	case USR_COM_NOTIFY_LINESTATE:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid LINESTATE data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_dbg(info, "linestate=%x", data);
+
+		if (data & LINE_BREAK_ERROR)
+			tty_insert_flip_char(tty, 0, TTY_BREAK);
+		if (data & LINE_PARITY_ERROR)
+			tty_insert_flip_char(tty, 0, TTY_PARITY);
+
+		is_async = 1;
+
+		break;
+
+	case USR_COM_SET_LINESTATE_MASK:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid LINESTATE MASK data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_dbg(info, "linestate_mask=%x", data);
+
+		break;
+
+	case USR_COM_NOTIFY_MODEMSTATE:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid MODEMSTATE data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_dbg(info, "modemstate=%x", data);
+
+		if ((data ^ info->modemstate) & MODEM_DCD) {
+			if (info->modemstate & MODEM_DCD)
+				info->modemstate &= ~MODEM_DCD;
+			else
+				info->modemstate |= MODEM_DCD;
+		}
+
+		is_async = 1;
+
+		break;
+
+	case USR_COM_SET_MODEMSTATE_MASK:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid MODEMSTATE MASK data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_dbg(info, "modemstate_mask=%x", data);
+
+		break;
+
+	case USR_COM_PURGE_DATA:
+		if (idx + 1 > info->subopt_size) {
+			nvtty_err(info, "invalid PURGE_DATA data!");
+			return -1;
+		}
+
+		info->arg[cmd] = data = info->subopt[idx];
+		nvtty_dbg(info, "purgedata=%x", data);
+
+		break;
+
+	default:
+		nvtty_err(info, "unknow comport command %d", cmd);
+		break;
+	}
+
+	/* Complete synchronous operation */
+	if (!is_async) {
+		nvtty_dbg(info, "deactivate command %d", cmd);
+		CLR_CMD_ACTIVE(info, cmd);
+	}
+
+	return 0;
+}
+
+static int handle_suboption(struct nvtty_serial *info)
+{
+	u8 subopt = info->subopt[0];
+
+	switch (subopt) {
+	case NVT_COM_PORT_OPTION:
+		return handle_comport_command(info);
+
+	default:
+		nvtty_err(info, "unkown suboption %d", subopt);
+	}
+
+	return 0;
+}
+
+static int getdata(struct nvtty_serial *info)
+{
+	u8 c, buf[128];
+	int i, len;
+	int ret;
+
+	ret = net_recv(info, buf, ARRAY_SIZE(buf), 0);
+	if (ret <= 0)
+		return ret == 0 ? -EIO : ret;
+	len = ret;
+
+	for (i = 0; i < len; i++) {
+		c = buf[i];
+
+		switch (info->state) {
+		case S_DATA:
+			if (c == IAC)
+				info->state = S_IAC;
+			else
+				nvtty_push(info, c);
+			break;
+
+		case S_IAC:
+			switch (c) {
+			case DO:
+				info->state = S_DO;
+				break;
+
+			case DONT:
+				info->state = S_DONT;
+				break;
+
+			case WILL:
+				info->state = S_WILL;
+				break;
+
+			case WONT:
+				info->state = S_WONT;
+				break;
+
+			case SB:
+				info->state = S_SB;
+				info->subopt_size = 0;
+				break;
+
+			case IAC:
+			default:
+				info->state = S_DATA;
+				nvtty_push(info, c);
+				break;
+
+			}
+			break;
+
+		case S_DO:
+			info->state = S_DATA;
+			do_option(info, c);
+			break;
+
+		case S_DONT:
+			info->state = S_DATA;
+			dont_option(info, c);
+			break;
+
+		case S_WILL:
+			info->state = S_DATA;
+			will_option(info, c);
+			break;
+
+		case S_WONT:
+			info->state = S_DATA;
+			wont_option(info, c);
+			break;
+
+		case S_SB:
+			if (c == IAC)
+				info->state = S_SE;
+			else {
+				if (info->subopt_size > SUBOPT_MAXSIZE)
+					nvtty_err(info, "suboption too large!");
+				else {
+					info->subopt[info->subopt_size] = c;
+					info->subopt_size++;
+				}
+			}
+			break;
+
+		case S_SE:
+			if (c == SE) {
+				info->state = S_DATA;
+				handle_suboption(info);
+				info->subopt_size = 0;
+			} else {
+				info->state = S_DATA;
+				nvtty_err(info, "suboption not terminated!");
+			}
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int putdata(struct nvtty_serial *info,
+			const unsigned char *buf, int count)
+{
+	unsigned char buf2[PUTDATA_MAXSIZE * 2]; /* in case of all IAC chars */
+	int i, n;
+
+	/* This should NOT happen due write_room()... */
+	BUG_ON(count > PUTDATA_MAXSIZE);
+
+	/* Must escape IAC... */
+	for (i = n = 0; i < count; i++, n++) {
+		if (buf[i] == IAC)
+			buf2[n++] = IAC;
+		buf2[n] = buf[i];
+	}
+
+	return net_send(info, buf2, n, 0);
+}
+
+static int comport_command(struct nvtty_serial *info,
+				unsigned int cmd, unsigned int arg)
+{
+	u8 buf[16] = { IAC, SB, NVT_COM_PORT_OPTION, cmd, } ;
+	int size = 4;
+	int i, ret;
+
+	switch (cmd) {
+	case USR_COM_SET_BAUDRATE:
+		*((u32 *) &buf[size]) = htonl(arg);
+		size += 4;
+		break;
+
+	default:
+		buf[size++] = (u8) arg;
+		break;
+	}
+	buf[size++] = IAC;
+	buf[size++] = SE;
+
+	i = 0;
+	while (i < size) {
+		ret = net_send(info, &buf[i], size - i, 0);
+		if (ret < 0)
+			return ret;
+
+		i += ret;
+	}
+
+	return 0;
+}
+
+static int sync_comport_command(struct nvtty_serial *info,
+				unsigned int cmd, unsigned int arg)
+{
+	int ret;
+
+	nvtty_dbg(info, "cmd=%d arg=%d", cmd, arg);
+	SET_CMD_ACTIVE(info, cmd);
+	ret = comport_command(info, cmd, arg);
+	if (ret < 0) {
+		nvtty_err(info, "unable to send comport command %d!", cmd);
+		return ret;
+	}
+
+	nvtty_dbg(info, "command %d - start", cmd);
+	ret = WAIT_CMD_ACTIVE(info, cmd);
+	if (ret < 0) {
+		nvtty_err(info, "unable to receive comport command %d!", cmd);
+		return ret;
+	}
+	nvtty_dbg(info, "command %d - ret=%d", cmd, info->arg[cmd]);
+
+	return info->arg[cmd];
+}
+
+static int comport_config(struct nvtty_serial *info)
+{
+	int mask;
+	int ret;
+
+	/* Get configuration values */
+	ret = sync_comport_command(info, USR_COM_SET_BAUDRATE, COM_BAUD_REQ);
+	if (ret < 0)
+		return ret;
+	ret = sync_comport_command(info, USR_COM_SET_DATASIZE, COM_DSIZE_REQ);
+	if (ret < 0)
+		return ret;
+	ret = sync_comport_command(info, USR_COM_SET_PARITY, COM_PARITY_REQ);
+	if (ret < 0)
+		return ret;
+	ret = sync_comport_command(info, USR_COM_SET_STOPSIZE, COM_SSIZE_REQ);
+	if (ret < 0)
+		return ret;
+	ret = sync_comport_command(info, USR_COM_SET_CONTROL, COM_FLOW_REQ);
+	if (ret < 0)
+		return ret;
+
+	/* Set port events mask */
+	mask = MODEM_DCD;
+	ret = sync_comport_command(info, USR_COM_SET_MODEMSTATE_MASK, mask);
+	if (ret < 0)
+		return ret;
+	mask = LINE_BREAK_ERROR | LINE_PARITY_ERROR;
+	ret = sync_comport_command(info, USR_COM_SET_LINESTATE_MASK, mask);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int comport_init(struct nvtty_serial *info)
+{
+	unsigned long timeout;
+	int ret;
+
+	SET_I_WANT_TO_SUPPORT(info, NVT_COM_PORT_OPTION);
+	ret = send_will(info, NVT_COM_PORT_OPTION);
+	if (ret < 0) {
+		nvtty_err(info, "error sending WILL %d", NVT_COM_PORT_OPTION);
+		return ret;
+	}
+	SET_I_SENT_IT(info, NVT_COM_PORT_OPTION);
+
+	SET_HE_MAY_SUPPORT(info, NVT_SUPP_GO_AHEAD);
+	ret = send_do(info, NVT_SUPP_GO_AHEAD);
+	if (ret < 0) {
+		nvtty_err(info, "error sending DO %d\n", NVT_SUPP_GO_AHEAD);
+		return ret;
+	}
+	SET_HE_RECV_IT(info, NVT_SUPP_GO_AHEAD);
+
+	timeout = jiffies + 5 * HZ;
+	do {
+		schedule();
+	} while (!I_DO_SUPPORT(info, NVT_COM_PORT_OPTION) &&
+					time_before(jiffies, timeout));
+
+	if (I_DO_SUPPORT(info, NVT_COM_PORT_OPTION)) {
+		ret = comport_config(info);
+		if (ret < 0) {
+			nvtty_err(info, "unable to configure port");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * The NVT task
+ */
+
+static int task_body(void *ptr)
+{
+	struct nvtty_serial *info = (struct nvtty_serial *) ptr;
+	int ret = 0;
+
+	/* Se should kill this task in some way... */
+	allow_signal(SIGTERM);
+	allow_signal(SIGKILL);
+
+	nvtty_dbg(info, "main loop started...");
+	while (!kthread_should_stop()) {
+		ret = getdata(info);
+		if (ret < 0) {
+			nvtty_dbg(info, "error on getting data");
+			break;
+		}
+	}
+	nvtty_dbg(info, "task is now exiting...");
+
+	/* Before exiting we should wait the last device's close... */
+	while (!kthread_should_stop())
+		schedule();
+
+	return 0;
+}
+
+/*
+ * TTY methods
+ */
+
+static int nvtty_tiocmget(struct tty_struct *tty, struct file *file)
+{
+	struct nvtty_serial *info = tty->driver_data;
+	int status, ret;
+
+	ret = wait_for_completion_interruptible(&info->init_done);
+	if (ret < 0) {
+		nvtty_err(info, "warning! Port not initialized");
+		return ret;
+	}
+
+	mutex_lock(&info->mutex);
+
+	status = (info->modemstate & MODEM_DCD) ? TIOCM_CD : 0;
+	nvtty_dbg(info, "status=%x", status);
+
+	mutex_unlock(&info->mutex);
+
+	return status;
+}
+
+static int nvtty_tiocmset(struct tty_struct *tty, struct file *file,
+			   unsigned int set, unsigned int clear)
+{
+	struct nvtty_serial *info = tty->driver_data;
+	int ret;
+
+	nvtty_dbg(info, "set=%x clear=%x", set, clear);
+
+	ret = wait_for_completion_interruptible(&info->init_done);
+	if (ret < 0) {
+		nvtty_err(info, "warning! Port not initialized");
+		return ret;
+	}
+
+	mutex_lock(&info->mutex);
+
+	if (set & TIOCM_RTS) {
+		ret = sync_comport_command(info,
+				USR_COM_SET_CONTROL, COM_RTS_ON);
+		if (ret < 0) {
+			nvtty_err(info, "unable to set RTS on");
+			goto exit;
+		}
+	}
+	if (set & TIOCM_DTR) {
+		ret = sync_comport_command(info,
+				USR_COM_SET_CONTROL, COM_DTR_ON);
+		if (ret < 0) {
+			nvtty_err(info, "unable to set DTR on");
+			goto exit;
+		}
+	}
+	if (clear & TIOCM_RTS) {
+		ret = sync_comport_command(info,
+				USR_COM_SET_CONTROL, COM_RTS_OFF);
+		if (ret < 0) {
+			nvtty_err(info, "unable to set RTS off");
+			goto exit;
+		}
+	}
+	if (clear & TIOCM_DTR) {
+		ret = sync_comport_command(info,
+				USR_COM_SET_CONTROL, COM_DTR_OFF);
+		if (ret < 0) {
+			nvtty_err(info, "unable to set DTR off");
+			goto exit;
+		}
+	}
+
+exit:
+	mutex_unlock(&info->mutex);
+
+	return 0;
+}
+
+#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+static void nvtty_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	struct nvtty_serial *info = tty->driver_data;
+	unsigned int cflag;
+	int tmp, ret;
+
+	ret = wait_for_completion_interruptible(&info->init_done);
+	if (ret < 0) {
+		nvtty_err(info, "warning! Port not initialized");
+		return;
+	}
+
+	mutex_lock(&info->mutex);
+
+	cflag = tty->termios->c_cflag;
+
+	/* Check that they really want us to change something */
+	if (old) {
+		if ((cflag == old->c_cflag) &&
+				(RELEVANT_IFLAG(tty->termios->c_iflag) ==
+				RELEVANT_IFLAG(old->c_iflag))) {
+			nvtty_dbg(info, "nothing to change...");
+			goto exit;
+		}
+	}
+
+	/* Set the byte size */
+	switch (cflag & CSIZE) {
+	case CS5:
+		tmp = 5;
+		break;
+	case CS6:
+		tmp = 6;
+		break;
+	case CS7:
+		tmp = 7;
+		break;
+	default:
+	case CS8:
+		tmp = 8;
+		break;
+	}
+	ret = sync_comport_command(info, USR_COM_SET_DATASIZE, COM_DSIZE(tmp));
+	if (ret < 0 || tmp != ret) {
+		nvtty_err(info, "unable to set datasize to %d", tmp);
+		ret = ret >= 0 ? ret : 8;
+		nvtty_info(info, "reset datasize to %d", ret);
+		nvtty_set_datasize(info, ret);
+	}
+
+	/* Set the parity */
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			tmp = COM_PARITY_ODD;
+		else
+			tmp = COM_PARITY_EVEN;
+	} else
+		tmp = COM_PARITY_NONE;
+	ret = sync_comport_command(info, USR_COM_SET_PARITY, tmp);
+	if (ret < 0 || tmp != ret) {
+		nvtty_err(info, "unable to set parity to %d", tmp);
+		ret = ret >= 0 ? ret : COM_PARITY_NONE;
+		nvtty_info(info, "reset parity to %d", ret);
+		nvtty_set_parity(info, ret);
+	}
+
+	/* Set the stop bits */
+	if (cflag & CSTOPB)
+		tmp = COM_SSIZE_TWO;
+	else
+		tmp = COM_SSIZE_ONE;
+	ret = sync_comport_command(info, USR_COM_SET_STOPSIZE, tmp);
+	if (ret < 0 || tmp != ret) {
+		nvtty_err(info, "unable to set stopsize to %d", tmp);
+		ret = ret >= 0 ? ret : COM_SSIZE_ONE;
+		nvtty_info(info, "reset stopsize to %d", ret);
+		nvtty_set_stopsize(info, ret);
+	}
+
+	/* Set the flow control */
+	if (cflag & CRTSCTS)
+		tmp = COM_OFLOW_HARD;
+	else
+		tmp = COM_OFLOW_NONE;
+	ret = sync_comport_command(info, USR_COM_SET_CONTROL, tmp);
+	if (ret < 0 || tmp != ret) {
+		nvtty_err(info, "unable to set control to %d", tmp);
+		ret = ret >= 0 ? ret : COM_OFLOW_NONE;
+		nvtty_info(info, "reset control to %d", ret);
+		nvtty_set_control(info, ret);
+	}
+
+	/* Set the baud rate */
+	tmp = tty_get_baud_rate(tty);
+	ret = sync_comport_command(info, USR_COM_SET_BAUDRATE, COM_BAUD(tmp));
+	if (ret < 0 || tmp != ret) {
+		nvtty_err(info, "unable to set baudrate to %d", tmp);
+		ret = ret >= 0 ? ret : 9600;
+		nvtty_info(info, "reset baudrate to %d", ret);
+		nvtty_set_baudrate(info, ret);
+	}
+
+exit:
+	mutex_unlock(&info->mutex);
+
+	return;
+}
+
+static int nvtty_write_room(struct tty_struct *tty)
+{
+	/* This value should be ok for any communication... */
+	return PUTDATA_MAXSIZE;
+}
+
+static int nvtty_write(struct tty_struct *tty,
+		      const unsigned char *buf, int count)
+{
+	struct nvtty_serial *info = tty->driver_data;
+	int ret;
+
+	ret = wait_for_completion_interruptible(&info->init_done);
+	if (ret < 0) {
+		nvtty_err(info, "warning! Port not initialized");
+		return ret == 0 ? -EIO : ret;
+	}
+
+	ret = putdata(info, buf, count);
+
+	return ret;
+}
+
+static int nvtty_open(struct tty_struct *tty, struct file *file)
+{
+	struct nvtty_serial *info;
+	int line;
+	int ret = 0;
+
+	line = tty->index;
+	if (line > minors || line < 0)
+		return -ENODEV;
+
+	info = &nvtty_info[line];
+
+	mutex_lock(&info->mutex);
+
+	info->open_count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	if (info->open_count == 1) {
+		init_completion(&info->init_done);
+
+		/* First get connected with remote server... */
+		ret = net_connect(info);
+		if (ret < 0) {
+			nvtty_err(info, "unable to connect with server");
+			goto unlock;
+		}
+		info->connection_ok = 1;
+
+		/* ... then start main kthread to get remote data... */
+		info->task = kthread_run(task_body, info,
+						DRIVER_NAME "%d", tty->index);
+		if (IS_ERR(info->task)) {
+			nvtty_err(info, "unable to create thread");
+			ret = PTR_ERR(info->task);
+			goto unlock;
+		}
+		info->task_is_running = 1;
+
+		/* ... in the end configure the nvtty port */
+		ret = comport_init(info);
+		if (ret < 0) {
+			info->connection_ok = 0;
+			goto unlock;
+		}
+
+		nvtty_dbg(info, "init_done");
+		complete_all(&info->init_done);
+	}
+
+unlock:
+	mutex_unlock(&info->mutex);
+
+	return ret;
+}
+
+static void nvtty_close(struct tty_struct *tty, struct file *file)
+{
+	struct nvtty_serial *info = tty->driver_data;
+
+	mutex_lock(&info->mutex);
+
+	info->open_count--;
+	if (info->open_count <= 0) {
+		nvtty_dbg(info, "last close");
+		net_disconnect(info);
+		info->connection_ok = 0;
+
+		if (info->task_is_running) {
+			kthread_stop(info->task);
+			info->task_is_running = 0;
+		}
+		sock_release(info->sock);
+	}
+
+	mutex_unlock(&info->mutex);
+
+	return;
+}
+
+static const struct tty_operations nvtty_serial_ops = {
+	.tiocmget	= nvtty_tiocmget,
+	.tiocmset	= nvtty_tiocmset,
+	.set_termios	= nvtty_set_termios,
+	.write_room	= nvtty_write_room,
+	.write		= nvtty_write,
+	.open		= nvtty_open,
+	.close		= nvtty_close,
+};
+
+/*
+ * sysfs stuff
+ */
+
+static ssize_t connection_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct nvtty_serial *info = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", info->connection_ok);
+}
+
+static ssize_t ip_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct nvtty_serial *info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&info->mutex);
+	ret = sprintf(buf, "%d.%d.%d.%d\n",
+				(info->addr & 0xff000000) >> 24,
+				(info->addr & 0x00ff0000) >> 16,
+				(info->addr & 0x0000ff00) >> 8,
+				info->addr & 0x000000ff);
+	mutex_unlock(&info->mutex);
+
+	return ret;
+}
+
+static ssize_t ip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t n)
+{
+	struct nvtty_serial *info = dev_get_drvdata(dev);
+	int i1, i2, i3, i4;
+	int ret;
+
+	ret = sscanf(buf, "%d.%d.%d.%d", &i1, &i2, &i3, &i4);
+	if (ret != 4 ||
+			i1 < 0 || i1 > 255 || i2 < 0 || i2 > 255 ||
+			i3 < 0 || i3 > 255 || i4 < 0 || i4 > 255)
+		return -EINVAL;
+
+	mutex_lock(&info->mutex);
+
+	if (info->connection_ok) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	info->addr = (i1 << 24) | (i2 << 16) | (i3 << 8) | i4;
+
+exit:
+	mutex_unlock(&info->mutex);
+
+	return n;
+}
+
+static ssize_t port_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct nvtty_serial *info = dev_get_drvdata(dev);
+	int ret;
+
+	mutex_lock(&info->mutex);
+	ret = sprintf(buf, "%d\n", info->port);
+	mutex_unlock(&info->mutex);
+
+	return ret;
+}
+
+static ssize_t port_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t n)
+{
+	struct nvtty_serial *info = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	ret = strict_strtoul(buf, 0, &val);
+	if (ret)
+		return -EINVAL;
+
+	if (val > 0xffff)
+		return -EINVAL;
+
+	mutex_lock(&info->mutex);
+
+	if (info->connection_ok) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	info->port = val;
+
+exit:
+	mutex_unlock(&info->mutex);
+
+	return n;
+}
+
+static DEVICE_ATTR(connection, 0444, connection_show, NULL);
+static DEVICE_ATTR(ip, 0644, ip_show, ip_store);
+static DEVICE_ATTR(port, 0644, port_show, port_store);
+
+static const struct attribute *nvtty_attr[] = {
+	&dev_attr_connection.attr,
+	&dev_attr_ip.attr,
+	&dev_attr_port.attr,
+	NULL
+};
+
+/*
+ * Module stuff
+ */
+
+static struct tty_driver *drv;
+
+static int __init nvtty_init(void)
+{
+	struct device *dev;
+	int i, ret;
+
+	/* Allocate main struct the tty driver */
+	nvtty_info = kzalloc(minors * sizeof(struct nvtty_serial), GFP_KERNEL);
+	if (!nvtty_info)
+		return -ENOMEM;
+	drv = alloc_tty_driver(minors);
+	if (!drv) {
+		ret = -ENOMEM;
+		goto unalloc_main_struct;
+	}
+
+	/* initialize the tty driver */
+	drv->owner = THIS_MODULE;
+	drv->driver_name = DRIVER_NAME "_tty";
+	drv->name = DRIVER_NAME;
+	drv->major = major,
+	drv->type = TTY_DRIVER_TYPE_SERIAL,
+	drv->subtype = SERIAL_TYPE_NORMAL,
+	drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV,
+	drv->init_termios = tty_std_termios;
+	drv->init_termios.c_cflag = CREAD | HUPCL | CLOCAL;
+	tty_set_operations(drv, &nvtty_serial_ops);
+
+	/* register the tty driver */
+	ret = tty_register_driver(drv);
+	if (ret) {
+		pr_err("failed to register nvtty tty driver");
+		goto unalloc_tty_driver;
+	}
+
+	for (i = 0; i < minors; i++) {
+		dev = tty_register_device(drv, i, NULL);
+		if (IS_ERR(dev)) {
+			pr_warning("failed to register device nvtty%d", i);
+			goto unregister_tty_device;
+		}
+
+		/* Init main data struct */
+		nvtty_info[i].addr = NVTTY_TCP_ADDR;
+		nvtty_info[i].port = NVTTY_TCP_PORT;
+		mutex_init(&nvtty_info[i].mutex);
+
+		/* Double link main data struct with each tty driver */
+		dev_set_drvdata(dev, &nvtty_info[i]);
+		nvtty_info[i].dev = dev;
+
+		ret = sysfs_create_files(&dev->kobj, nvtty_attr);
+		if (ret < 0) {
+			pr_warning("failed to register device nvtty%d", i);
+			goto unregister_tty_device;
+		}
+	}
+
+	pr_info(DRIVER_NAME ": serial port driver loaded (%d ports)\n", minors);
+
+	return 0;
+
+unregister_tty_device:
+	for ( ; i >= 0; i--) {
+		dev = nvtty_info[i].dev;
+		tty_unregister_device(drv, i);
+
+		sysfs_remove_files(&dev->kobj, nvtty_attr);
+	}
+
+unalloc_tty_driver:
+	put_tty_driver(drv);
+unalloc_main_struct:
+	kfree(nvtty_info);
+
+	return ret;
+}
+
+static void __exit nvtty_exit(void)
+{
+	struct device *dev;
+	int i;
+
+	for (i = 0; i < minors; i++) {
+		dev = nvtty_info[i].dev;
+		tty_unregister_device(drv, i);
+
+		sysfs_remove_files(&dev->kobj, nvtty_attr);
+	}
+
+	tty_unregister_driver(drv);
+	put_tty_driver(drv);
+	kfree(nvtty_info);
+
+	pr_info(DRIVER_NAME ": serial port driver removed\n");
+}
+
+module_init(nvtty_init);
+module_exit(nvtty_exit);
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("Network Virtual Terminal (RFC 854) "
+			"with Com Port option (RFC 2217)");
+MODULE_LICENSE("GPL");
-- 
1.5.6.5


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] char nvtty: Network Virtual Terminal support
  2011-01-03 15:15 ` [PATCH] char nvtty: Network Virtual Terminal support Rodolfo Giometti
@ 2011-01-03 17:14   ` Alan Cox
  2011-01-03 18:02     ` Rodolfo Giometti
  2011-01-03 23:32   ` Randy Dunlap
  1 sibling, 1 reply; 9+ messages in thread
From: Alan Cox @ 2011-01-03 17:14 UTC (permalink / raw)
  To: Rodolfo Giometti; +Cc: linux-kernel, Russell Coker, Greg Kroah-Hartman

> By using these devices and a proper compatible server (not included
> here but you can use sredird) you can get access to a remote tty
> device as the tty device itself was conneted with your local host.
> All data and settings are sent and received through the network.

Interesting but would it not be better to keep all the network stuff in
user space given that most sane environments will have crypto on them ?

It's also a less than ideal protocol in that it can't represent some of
the things termios can, nor can it report arbitary speeds properly.

Is there a reason it couldn't use pty/tty pairs either as is or tweaked
to better report termios changes and allow modem line reporting ?

Alan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] char nvtty: Network Virtual Terminal support
  2011-01-03 17:14   ` Alan Cox
@ 2011-01-03 18:02     ` Rodolfo Giometti
  2011-01-03 18:22       ` Samuel Thibault
  0 siblings, 1 reply; 9+ messages in thread
From: Rodolfo Giometti @ 2011-01-03 18:02 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-kernel, Russell Coker, Greg Kroah-Hartman

On Mon, Jan 03, 2011 at 05:14:52PM +0000, Alan Cox wrote:
> > By using these devices and a proper compatible server (not included
> > here but you can use sredird) you can get access to a remote tty
> > device as the tty device itself was conneted with your local host.
> > All data and settings are sent and received through the network.
> 
> Interesting but would it not be better to keep all the network stuff in
> user space given that most sane environments will have crypto on them ?

Yes, but in most cases this is used on not-crypted connections (as far
as I know there are no crypted server for this protocol yet), however
we can easily get a crypted connection by connecting with localhost
and then using a program like cryptcat for the crypto stuff.

> It's also a less than ideal protocol in that it can't represent some of
> the things termios can, nor can it report arbitary speeds properly.

I see, but it is widely used and also it can be enhanced to better fit
termios capabilities.

However by using this protocol "as is" we can do several useful
things! :)

> Is there a reason it couldn't use pty/tty pairs either as is or tweaked
> to better report termios changes and allow modem line reporting ?

This is the solution that cyclades-serial-client uses (the userland
program where my patch is derived from) but as far as I know pty/tty
pairs cannot be used to report termios settings but only data... is
that right? =:-o

Ciao,

Rodolfo

-- 

GNU/Linux Solutions                  e-mail: giometti@enneenne.com
Linux Device Driver                          giometti@linux.it
Embedded Systems                     phone:  +39 349 2432127
UNIX programming                     skype:  rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] char nvtty: Network Virtual Terminal support
  2011-01-03 18:02     ` Rodolfo Giometti
@ 2011-01-03 18:22       ` Samuel Thibault
  0 siblings, 0 replies; 9+ messages in thread
From: Samuel Thibault @ 2011-01-03 18:22 UTC (permalink / raw)
  To: Alan Cox, linux-kernel, Russell Coker, Greg Kroah-Hartman

Rodolfo Giometti, le Mon 03 Jan 2011 19:02:47 +0100, a écrit :
> but as far as I know pty/tty
> pairs cannot be used to report termios settings but only data... is
> that right? =:-o

ptys are meant to let you deal with termios, precisely (contrary to mere
pipes for instance).

Samuel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] char nvtty: Network Virtual Terminal support
  2011-01-03 15:15 ` [PATCH] char nvtty: Network Virtual Terminal support Rodolfo Giometti
  2011-01-03 17:14   ` Alan Cox
@ 2011-01-03 23:32   ` Randy Dunlap
  2011-01-05 14:48     ` Rodolfo Giometti
  1 sibling, 1 reply; 9+ messages in thread
From: Randy Dunlap @ 2011-01-03 23:32 UTC (permalink / raw)
  To: Rodolfo Giometti; +Cc: linux-kernel, Russell Coker, Greg Kroah-Hartman

On Mon,  3 Jan 2011 16:15:35 +0100 Rodolfo Giometti wrote:

> A Network Virtual terminal (NVT tty) is a software device consisting
> of one halves: a client device, which is identical to a physical
> terminal, who, is turn, get connected with a remote server where real
> tty devices are located.
> 
> These devices are specified by RFC 854 and RFC 2217 and ther name into
> the system is /dev/nvttyX (by default you have 4 devices).
> 
> By using these devices and a proper compatible server (not included
> here but you can use sredird) you can get access to a remote tty
> device as the tty device itself was conneted with your local host.
> All data and settings are sent and received through the network.
> 
> Signed-off-by: Rodolfo Giometti <giometti@linux.it>
> ---
>  Documentation/ABI/testing/sysfs-nvtty |   35 +
>  drivers/char/Kconfig                  |   22 +
>  drivers/char/Makefile                 |    1 +
>  drivers/char/nvtty.c                  | 1557 +++++++++++++++++++++++++++++++++
>  4 files changed, 1615 insertions(+), 0 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-nvtty
>  create mode 100644 drivers/char/nvtty.c
> 

> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> index 43d3395..13775be 100644
> --- a/drivers/char/Kconfig
> +++ b/drivers/char/Kconfig
> @@ -451,6 +451,28 @@ config UNIX98_PTYS
>  	  All modern Linux systems use the Unix98 ptys.  Say Y unless
>  	  you're on an embedded system and want to conserve memory.
>  
> +config NVT_TTY
> +	tristate "Network Virtual Terminal"
> +	default m

Don't enable random drivers, please.

> +	---help---
> +	  A Network Virtual terminal (NVT tty) is a software device
> +	  consisting of one halves: a client device, which is

	"one" and "halves" do not go together.

> +	  identical to a physical terminal, who, is turn, get

	                                         in turn, gets

> +	  connected with a remote server where real tty devices are

	            to a remote server

> +	  located.
> +
> +	  These devices are specified by RFC 854 and RFC 2217 and ther

	                                                          the ?? or their ??

> +	  name into the system is /dev/nvttyX (by default you have 4

	                                      (by default there are 4

> +	  devices).
> +
> +	  By using these devices and a proper compatible server (not
> +	  included here but you can use sredird) you can get access to
> +	  a remote tty device as the tty device itself was conneted

	                      as though the tty device itself was connected

> +	  with your local host.  All data and settings are sent and
> +	  received through the network.
> +
> +	  If unsure, say M.
> +
>  config DEVPTS_MULTIPLE_INSTANCES
>  	bool "Support multiple instances of devpts"
>  	depends on UNIX98_PTYS


---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
desserts:  http://www.xenotime.net/linux/recipes/

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Network Virtual Terminal
  2011-01-03 15:15 Network Virtual Terminal Rodolfo Giometti
  2011-01-03 15:15 ` [PATCH] char nvtty: Network Virtual Terminal support Rodolfo Giometti
@ 2011-01-04 17:13 ` Hans-Peter Jansen
  2011-01-05 15:23   ` Rodolfo Giometti
  1 sibling, 1 reply; 9+ messages in thread
From: Hans-Peter Jansen @ 2011-01-04 17:13 UTC (permalink / raw)
  To: linux-kernel; +Cc: Rodolfo Giometti, Russell Coker, Greg Kroah-Hartman

Dear Rodolfo,

On Monday 03 January 2011, 16:15:34 Rodolfo Giometti wrote:
> Hello,
>
> here my implementation of Network Virtual terminals (NVT tty)
> according to RFC 854 and RFC 2217... actually this is the client side
> part since as remote server I used sredird
> (http://freshmeat.net/projects/sredird/).

Sounds interesting. Could you outline the limits a bit? In one of my use 
cases, I need to support 1200 baud with a rather esoteric 7E2 serial 
setup. (Needless to say, the client is a butt ugly win app to control 
gasoline pumps, running inself in a VMware WS setup. I'm supporting 
this since ten years now, where VMware took about 4 years to get the 
serial setup right finally...) Being able to redirect the serial data 
over network (in an inexpensive, but reliable way) would make my life 
significant easier in this respect.

Pete

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] char nvtty: Network Virtual Terminal support
  2011-01-03 23:32   ` Randy Dunlap
@ 2011-01-05 14:48     ` Rodolfo Giometti
  0 siblings, 0 replies; 9+ messages in thread
From: Rodolfo Giometti @ 2011-01-05 14:48 UTC (permalink / raw)
  To: Randy Dunlap; +Cc: linux-kernel, Russell Coker, Greg Kroah-Hartman

On Mon, Jan 03, 2011 at 03:32:31PM -0800, Randy Dunlap wrote:
> On Mon,  3 Jan 2011 16:15:35 +0100 Rodolfo Giometti wrote:
> 
> > A Network Virtual terminal (NVT tty) is a software device consisting
> > of one halves: a client device, which is identical to a physical
> > terminal, who, is turn, get connected with a remote server where real
> > tty devices are located.
> > 
> > These devices are specified by RFC 854 and RFC 2217 and ther name into
> > the system is /dev/nvttyX (by default you have 4 devices).
> > 
> > By using these devices and a proper compatible server (not included
> > here but you can use sredird) you can get access to a remote tty
> > device as the tty device itself was conneted with your local host.
> > All data and settings are sent and received through the network.
> > 
> > Signed-off-by: Rodolfo Giometti <giometti@linux.it>
> > ---
> >  Documentation/ABI/testing/sysfs-nvtty |   35 +
> >  drivers/char/Kconfig                  |   22 +
> >  drivers/char/Makefile                 |    1 +
> >  drivers/char/nvtty.c                  | 1557 +++++++++++++++++++++++++++++++++
> >  4 files changed, 1615 insertions(+), 0 deletions(-)
> >  create mode 100644 Documentation/ABI/testing/sysfs-nvtty
> >  create mode 100644 drivers/char/nvtty.c
> > 
> 
> > diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
> > index 43d3395..13775be 100644
> > --- a/drivers/char/Kconfig
> > +++ b/drivers/char/Kconfig
> > @@ -451,6 +451,28 @@ config UNIX98_PTYS
> >  	  All modern Linux systems use the Unix98 ptys.  Say Y unless
> >  	  you're on an embedded system and want to conserve memory.
> >  
> > +config NVT_TTY
> > +	tristate "Network Virtual Terminal"
> > +	default m
> 
> Don't enable random drivers, please.

Ok, moved to 'n'.

> > +	---help---
> > +	  A Network Virtual terminal (NVT tty) is a software device
> > +	  consisting of one halves: a client device, which is
> 
> 	"one" and "halves" do not go together.

Fixed.

> > +	  identical to a physical terminal, who, is turn, get
> 
> 	                                         in turn, gets

Fixed.

> > +	  connected with a remote server where real tty devices are
> 
> 	            to a remote server

Fixed.

> > +	  located.
> > +
> > +	  These devices are specified by RFC 854 and RFC 2217 and ther
> 
> 	                                                          the ?? or their ??

Fixed.

> > +	  name into the system is /dev/nvttyX (by default you have 4
> 
> 	                                      (by default there are 4

Fixed.

> > +	  devices).
> > +
> > +	  By using these devices and a proper compatible server (not
> > +	  included here but you can use sredird) you can get access to
> > +	  a remote tty device as the tty device itself was conneted
> 
> 	                      as though the tty device itself was connected

Fixed.

> > +	  with your local host.  All data and settings are sent and
> > +	  received through the network.
> > +
> > +	  If unsure, say M.
> > +
> >  config DEVPTS_MULTIPLE_INSTANCES
> >  	bool "Support multiple instances of devpts"
> >  	depends on UNIX98_PTYS

Thanks a lot, I'm going to repropose a new patch ASAP.

Ciao,

Rodolfo

-- 

GNU/Linux Solutions                  e-mail: giometti@enneenne.com
Linux Device Driver                          giometti@linux.it
Embedded Systems                     phone:  +39 349 2432127
UNIX programming                     skype:  rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: Network Virtual Terminal
  2011-01-04 17:13 ` Network Virtual Terminal Hans-Peter Jansen
@ 2011-01-05 15:23   ` Rodolfo Giometti
  0 siblings, 0 replies; 9+ messages in thread
From: Rodolfo Giometti @ 2011-01-05 15:23 UTC (permalink / raw)
  To: Hans-Peter Jansen; +Cc: linux-kernel, Russell Coker, Greg Kroah-Hartman

On Tue, Jan 04, 2011 at 06:13:46PM +0100, Hans-Peter Jansen wrote:
> Dear Rodolfo,
> 
> On Monday 03 January 2011, 16:15:34 Rodolfo Giometti wrote:
> > Hello,
> >
> > here my implementation of Network Virtual terminals (NVT tty)
> > according to RFC 854 and RFC 2217... actually this is the client side
> > part since as remote server I used sredird
> > (http://freshmeat.net/projects/sredird/).
> 
> Sounds interesting. Could you outline the limits a bit? In one of my use 
> cases, I need to support 1200 baud with a rather esoteric 7E2 serial 
> setup. (Needless to say, the client is a butt ugly win app to control 
> gasoline pumps, running inself in a VMware WS setup. I'm supporting 
> this since ten years now, where VMware took about 4 years to get the 
> serial setup right finally...) Being able to redirect the serial data 
> over network (in an inexpensive, but reliable way) would make my life 
> significant easier in this respect.

My code is just one halve of the game... to solve your problem you
need a server on the machine where the serial port is installed and
you need that the server itself can manage it.

Here a simple schema of the whole game (it's my testing environment
but it can explain the situation anyway):

        ------------------+
             +---------+  |
             | minicom |  |
             +---------+  |
                  |       | local PC with no
                  v       | serial ports
             /dev/nvtty0  |
                  |       |
        ------------------+
                  |
                  v
             /\/\/\/\/\
            | network |
             \/\/\/\/\/
                  |
        ------------------+
                  |       |
                  v       |
             +---------+  |
             | sredird |  |
             +---------+  | remote PC with
                  |       | serial ports
                  v       |
             /dev/ttyS0   |
                  |       |
        ------------------+
                  |
                  v
         embedded PC with
         a serial console

As you can see the program sredird
(http://freshmeat.net/projects/sredird/) manages the real serial port
according to the commands received from nvtty0 device and it
sends/receives the serial data to/from it and the userland application
(minicom in this example) doesn't see any difference from working on a
real serial port.

Hope this is useful to you.

Ciao,

Rodolfo

-- 

GNU/Linux Solutions                  e-mail: giometti@enneenne.com
Linux Device Driver                          giometti@linux.it
Embedded Systems                     phone:  +39 349 2432127
UNIX programming                     skype:  rodolfo.giometti
Freelance ICT Italia - Consulente ICT Italia - www.consulenti-ict.it

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-01-05 15:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-03 15:15 Network Virtual Terminal Rodolfo Giometti
2011-01-03 15:15 ` [PATCH] char nvtty: Network Virtual Terminal support Rodolfo Giometti
2011-01-03 17:14   ` Alan Cox
2011-01-03 18:02     ` Rodolfo Giometti
2011-01-03 18:22       ` Samuel Thibault
2011-01-03 23:32   ` Randy Dunlap
2011-01-05 14:48     ` Rodolfo Giometti
2011-01-04 17:13 ` Network Virtual Terminal Hans-Peter Jansen
2011-01-05 15:23   ` Rodolfo Giometti

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