Hi Linus,
Please consider including this user space serial driver. It was writen for
the Perle 833 RAS Server but can also be used for other serial devices
more appropriately driven from a userspace program.
Patrick
diff -u -r linux-2.4.0-test13-pre1.clean/Documentation/Configure.help linux-2.4.0-test13-pre1.ussp/Documentation/Configure.help
--- linux-2.4.0-test13-pre1.clean/Documentation/Configure.help Thu Dec 14 09:36:25 2000
+++ linux-2.4.0-test13-pre1.ussp/Documentation/Configure.help Thu Dec 14 10:29:57 2000
@@ -12323,6 +12323,19 @@
inserted in and removed from the running kernel whenever you want).
The module will be called sx.o. If you want to do that, say M here.
+User space serial port support (perle RAS server)
+CONFIG_USSP
+ This is a driver that allows a userspace deamon to handle the
+ internals of a serial port. This is an especially good way to
+ handle things if for instance the serial port is "remote" on
+ a network. Select this if for instance you want to use the
+ Perle 833 RAS server.
+
+ For now, this driver can only be built as a module ( = code which
+ can be inserted in and removed from the running kernel whenever you
+ want). The module will be called ussp.o. If you want to do that,
+ say M here.
+
Hayes ESP serial port support
CONFIG_ESPSERIAL
This is a driver which supports Hayes ESP serial ports. Both single
diff -u -r linux-2.4.0-test13-pre1.clean/Documentation/ussp.txt linux-2.4.0-test13-pre1.ussp/Documentation/ussp.txt
--- linux-2.4.0-test13-pre1.clean/Documentation/ussp.txt Fri Dec 15 12:44:33 2000
+++ linux-2.4.0-test13-pre1.ussp/Documentation/ussp.txt Thu Dec 14 10:29:57 2000
@@ -0,0 +1,80 @@
+
+
+ussp.txt: Userspace serial ports.
+
+Introduction
+------------
+
+This driver allows serial port drivers to be written in userspace.
+This will probably incur a bit over overhead, but I don't expect it to
+be unusable. In fact, intelligent cards like "sx" already hold up
+characters upto 20 ms before reporting them to the OS. It must be very
+well possible to handle things faster than that.
+
+This driver was written to allow access to the perle 833 RAS
+server. It can easily be driven using a userspace deamon, while
+interfacing with the kernel internals on the network level would be
+harder.
+
+
+Technical details
+-----------------
+
+The deamon opens the ussp_master device. It then uses the
+USSP_SET_PORT ioctl to associate the filedescriptor with a port. From
+that moment onward, the device is a "pipe" that transfers data between
+the serial port and the kernel driver.
+
+
+struct operation {
+ enum op;
+ unsigned long len;
+ unsigned long arg;
+ unsigned char data[];
+ };
+
+
+Write: (kernel -> Userspace)
+ op = USSP_WRITE;
+ len = length of data.
+ data[] holds the data.
+
+Read: (Userspace -> kernel)
+ op = USSP_READ;
+ len = length of data.
+ data[] holds the data.
+
+set_termios: (kernel -> userspace)
+ op = USSP_SET_TERMIOS;
+ len = size of termios structure.
+ (userspace can return error on unsupported sizes).
+ data[] Termios structure.
+
+modem_signal_change: (bidirectional)
+ /* Either side ignores the bits that are not setable */
+ op = USSP_MSC;
+ len = 0;
+ arg = the logical OR of:
+ DCD 0x01
+ RI 0x02
+ RTS 0x04
+ CTS 0x08
+ DTR 0x10
+ DSR 0x20
+
+open: (kernel -> userspace)
+ op = USSP_OPEN;
+ len = 0;
+ arg = open flags;
+
+open_ok: (userspace -> kernel)
+ op = USSP_OPEN_RESULT;
+ len = 0l
+ arg = result code (0 = OK, other = Errno);
+
+close: (kernel -> userspace)
+ op = USSP_CLOSE;
+ len = 0;
+
+
+
diff -u -r linux-2.4.0-test13-pre1.clean/drivers/char/Config.in linux-2.4.0-test13-pre1.ussp/drivers/char/Config.in
--- linux-2.4.0-test13-pre1.clean/drivers/char/Config.in Thu Nov 16 22:59:53 2000
+++ linux-2.4.0-test13-pre1.ussp/drivers/char/Config.in Thu Dec 14 10:29:57 2000
@@ -50,6 +50,7 @@
bool ' Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS
fi
tristate ' Specialix SX (and SI) card support' CONFIG_SX
+ tristate ' User Space Serial Ports (for Perle dialout)' CONFIG_USSP
tristate ' Specialix RIO system support' CONFIG_RIO
if [ "$CONFIG_RIO" != "n" ]; then
bool ' Support really old RIO/PCI cards' CONFIG_RIO_OLDPCI
diff -u -r linux-2.4.0-test13-pre1.clean/drivers/char/Makefile linux-2.4.0-test13-pre1.ussp/drivers/char/Makefile
--- linux-2.4.0-test13-pre1.clean/drivers/char/Makefile Thu Dec 14 09:36:58 2000
+++ linux-2.4.0-test13-pre1.ussp/drivers/char/Makefile Thu Dec 14 10:29:57 2000
@@ -120,6 +120,7 @@
obj-$(CONFIG_CYCLADES) += cyclades.o
obj-$(CONFIG_STALLION) += stallion.o
obj-$(CONFIG_ISTALLION) += istallion.o
+obj-$(CONFIG_USSP) += ussp.o
obj-$(CONFIG_COMPUTONE) += ip2.o ip2main.o
obj-$(CONFIG_RISCOM8) += riscom8.o
obj-$(CONFIG_ISI) += isicom.o
diff -u -r linux-2.4.0-test13-pre1.clean/drivers/char/ussp.c linux-2.4.0-test13-pre1.ussp/drivers/char/ussp.c
--- linux-2.4.0-test13-pre1.clean/drivers/char/ussp.c Thu Dec 14 13:51:09 2000
+++ linux-2.4.0-test13-pre1.ussp/drivers/char/ussp.c Thu Dec 14 12:32:46 2000
@@ -0,0 +1,997 @@
+/* -linux-c; c-basic-offset 8; */
+/* ussp.c -- driver to allow a userspace daemon to handle a serial port.
+
+ *
+ * This driver was written to allow Linux to communicate with the
+ * Perle 833 RAS server.
+ *
+ *
+ * (C) 2000 [email protected], [email protected]
+ *
+ * Perle/Specialix paid for the development of this driver. If you are
+ * using this driver in conjunction with a perle product, please DO
+ * contact [email protected] if you require support. But please read
+ * the documentation (ussp.txt) first.
+ *
+ *
+ *
+ * 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.
+ *
+ * Revision history:
+ *
+ * */
+
+
+#define RCS_ID "$Id: $"
+#define RCS_REV "$Revision: $"
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kdev_t.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+#include <linux/pci.h>
+#include <linux/malloc.h>
+#include <linux/miscdevice.h>
+#include <linux/tty_ldisc.h>
+#include <linux/init.h>
+
+#include <linux/compatmac.h>
+#include <linux/poll.h>
+#include <asm/smplock.h>
+
+#include <linux/ussp.h>
+
+
+/* During development this is -1, to reduce nkeystrokes. Later this
+ should be 0. Beta testers: Remind me to set this to 0 before
+ release -- REW */
+int ussp_debug = -1;
+
+/* This parameter makes this driver set "lowlatency". For nomal serial ports
+ the handling of serial characters is aggregated for a lower CPU overhead.
+ In this driver however, that won't buy you anything, as the deamon will
+ already packetize and aggregate the data. Clear this if you feel that
+ this is not true. -- REW
+ */
+int ussp_set_lowlatency = 1;
+
+
+/* I don't think that this driver can handle more than 256 ports on
+ one machine. -- REW */
+
+
+/* Configurable options:
+ (Don't be too sure that it'll work if you toggle them) */
+
+
+MODULE_PARM(ussp_debug, "i");
+MODULE_PARM(ussp_set_lowlatency, "i");
+
+static int ussp_ctl_ioctl (struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+static int ussp_ctl_open(struct inode *inode, struct file *filp);
+static ssize_t ussp_ctl_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos);
+static ssize_t ussp_ctl_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos);
+static int ussp_ctl_close(struct inode *, struct file *);
+static unsigned int ussp_ctl_poll (struct file *, poll_table *);
+
+static struct file_operations ussp_ctl_fops = {
+ owner: THIS_MODULE,
+ read: ussp_ctl_read,
+ write: ussp_ctl_write,
+ poll: ussp_ctl_poll,
+ ioctl: ussp_ctl_ioctl,
+ open: ussp_ctl_open,
+ release: ussp_ctl_close
+};
+
+
+static struct miscdevice ussp_ctl_device = {
+ USSPCTL_MISC_MINOR, "ussp_ctl", &ussp_ctl_fops
+};
+
+
+#define DEBUG_FLOW 0x0001
+#define DEBUG_WRITE_TO_CTL 0x0002
+#define DEBUG_CLOSE 0x0004
+#define DEBUG_IOCTL 0x0008
+#define DEBUG_CIB 0x0010
+#define DEBUG_OPEN 0x0020
+#define DEBUG_BUFFERS 0x0040
+#define DEBUG_READ_CTL 0x0080
+#define DEBUG_INFO 0x0100
+
+#define DEBUG
+
+#ifdef DEBUG
+#define ussp_dprintk(f, str...) if (ussp_debug & f) printk (str)
+#else
+#define ussp_dprintk(f, str...) klasjdhflksjdhflkdjshfslakh/* nothing */
+#endif
+
+
+#define func_enter() ussp_dprintk (DEBUG_FLOW, "ussp: enter " __FUNCTION__ "\n")
+#define func_exit() ussp_dprintk (DEBUG_FLOW, "ussp: exit " __FUNCTION__ "\n")
+
+
+
+/*
+ * TTY callbacks
+ */
+
+static ssize_t ussp_tty_write (struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count);
+static int ussp_tty_ioctl (struct tty_struct *, struct file *, unsigned int,
+ unsigned long);
+static int ussp_tty_open (struct tty_struct *, struct file * filp);
+static void ussp_tty_close (struct tty_struct *, struct file *);
+static int ussp_tty_write_room (struct tty_struct * );
+static int ussp_tty_chars_in_buffer (struct tty_struct * );
+static void ussp_tty_put_char (struct tty_struct *, unsigned char);
+
+void ussp_tty_flush_buffer (struct tty_struct *tty);
+void ussp_tty_flush_chars (struct tty_struct *tty);
+void ussp_tty_stop (struct tty_struct *tty);
+void ussp_tty_start (struct tty_struct *tty);
+void ussp_tty_set_termios (struct tty_struct * tty,
+ struct termios * old_termios);
+void ussp_tty_hangup (struct tty_struct *tty);
+
+struct ussp_port ussp_ports[USSP_MAX_PORTS];
+int ussp_nports = USSP_MAX_PORTS;
+int ussp_refcount;
+
+static struct tty_driver ussp_driver;
+static struct tty_struct * ussp_table[USSP_MAX_PORTS] = { NULL, };
+static struct termios * ussp_termios[USSP_MAX_PORTS];
+static struct termios * ussp_termios_locked[USSP_MAX_PORTS];
+
+#define D_DATA_AVAILABLE(port) ((port->daemon_head - port->daemon_tail) & (PAGE_SIZE-1))
+#define TTY_DATA_AVAILABLE(port) ((port->tty_head - port->tty_tail) & (PAGE_SIZE-1))
+
+#define SPACE_IN_BUFFER(head, tail, bufsz) \
+ ((((tail) - (head)) <= 0)?(tail) - (head) - 1 + (bufsz):((tail)-(head) - 1))
+
+
+void ussp_tty_hangup (struct tty_struct *tty)
+{
+ func_enter();
+
+ /* nothing special for us to do? Oh well. -- REW */
+
+ func_exit();
+}
+
+
+void ussp_tty_flush_buffer (struct tty_struct *tty)
+{
+ struct ussp_port *port = tty->driver_data;
+
+ func_enter ();
+ /* Clear the (output) buffer. But the buffer may contain interesting
+ data (e.g. a baud rate change.). For now just let things be...
+ -- REW
+ */
+
+ if (port && port->tty) {
+ if (port->tty->stopped || port->tty->hw_stopped ||
+ !port->tty_buffer) {
+ func_exit ();
+ return;
+ }
+ wake_up_interruptible (&port->tty_wait);
+ }
+ func_exit();
+}
+
+
+void ussp_tty_flush_chars (struct tty_struct *tty)
+{
+ func_enter();
+ func_exit();
+}
+
+
+void ussp_tty_stop (struct tty_struct *tty)
+{
+ func_enter();
+ func_exit();
+}
+
+
+void ussp_tty_start (struct tty_struct *tty)
+{
+ func_enter();
+ func_exit();
+}
+
+
+static void ussp_dummy(struct tty_struct *tty)
+{
+ func_enter();
+ func_exit();
+
+ return;
+}
+
+static int __init ussp_init (void)
+{
+ int status;
+
+ func_enter();
+
+ ussp_dprintk (DEBUG_INFO, "USSP: " __DATE__ "/" __TIME__ " version 0.01\n");
+
+ memset(&ussp_driver, 0, sizeof(ussp_driver));
+ ussp_driver.magic = TTY_DRIVER_MAGIC;
+ ussp_driver.driver_name = "perle";
+ ussp_driver.name = "ttyUSSP";
+ ussp_driver.major = USSP_NORMAL_MAJOR;
+ ussp_driver.num = 4;
+ ussp_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ ussp_driver.subtype = SERIAL_TYPE_NORMAL;
+ ussp_driver.init_termios = tty_std_termios;
+ ussp_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ ussp_driver.flags = TTY_DRIVER_REAL_RAW;
+ ussp_driver.refcount = &ussp_refcount;
+ ussp_driver.table = ussp_table;
+ ussp_driver.termios = ussp_termios;
+ ussp_driver.termios_locked = ussp_termios_locked;
+
+ ussp_driver.open = ussp_tty_open;
+ ussp_driver.close = ussp_tty_close;
+ ussp_driver.write = ussp_tty_write;
+ ussp_driver.put_char = ussp_tty_put_char;
+ ussp_driver.flush_chars = ussp_tty_flush_chars;
+ ussp_driver.write_room = ussp_tty_write_room;
+ ussp_driver.chars_in_buffer = ussp_tty_chars_in_buffer;
+ ussp_driver.flush_buffer = ussp_tty_flush_buffer;
+ ussp_driver.ioctl = ussp_tty_ioctl;
+ ussp_driver.throttle = ussp_dummy;
+ ussp_driver.unthrottle = ussp_dummy;
+ ussp_driver.set_termios = ussp_tty_set_termios;
+ ussp_driver.stop = ussp_tty_stop;
+ ussp_driver.start = ussp_tty_start;
+ ussp_driver.hangup = ussp_tty_hangup;
+
+ status = tty_register_driver (&ussp_driver);
+ printk (KERN_INFO "Return value registering: %d\n", status);
+ if(status == 0)
+ printk(KERN_INFO "USSP driver registered.\n");
+ else
+ printk(KERN_ERR "error registering driver: %d\n",
+ status);
+
+ if (misc_register(&ussp_ctl_device) < 0) {
+ printk(KERN_ERR "USSP: Unable to register control device.\n");
+ return -EIO;
+ }
+
+ func_exit();
+ return status;
+}
+
+
+#if 0
+void ussp_tty_receive_chars (struct ussp_port *port)
+{
+ int count;
+
+ func_enter ();
+
+ count = TTY_COUNTER(port);
+
+ memcpy(port->tty->flip.char_buf_ptr, port->tty_buffer+port->tty_head, count);
+ memset(port->tty->flip.flag_buf_ptr, TTY_NORMAL, count);
+ port->tty->flip.count += count;
+ port->tty->flip.char_buf_ptr += count;
+ port->tty->flip.flag_buf_ptr += count;
+ port->tty_tail = port->tty_head;
+ tty_flip_buffer_push (port->tty);
+
+ func_exit ();
+}
+#endif
+
+
+int copy_to_circular_buffer (char *buffer, int bufsz, int *head, int *tail,
+ const void *data, int count, int from_user)
+{
+ int c, r, s;
+
+ func_enter ();
+
+ s = SPACE_IN_BUFFER(*head, *tail, bufsz);
+
+ if (count > s){
+ ussp_dprintk (DEBUG_BUFFERS,
+ "ctcb: No space in buffer: count: %d s: %d\n",
+ count, s);
+ return 0;
+ }
+
+ r = 0;
+ while (count > 0) {
+ c = count;
+ if (c > (bufsz - *head)) c = bufsz - *head;
+
+ if (from_user)
+ copy_from_user (buffer + *head, data, c);
+ else
+ memcpy (buffer + *head, data, c);
+
+ data += c;
+ *head += c;
+ if (*head >= bufsz) *head = 0;
+ count -= c;
+ r += c;
+ }
+ func_exit ();
+ return r;
+}
+
+
+int copy_from_circular_buffer (char *buffer, int bufsz, int *head, int *tail,
+ void *data, int count, int to_user)
+{
+ int c, s;
+
+ func_enter ();
+
+ s = *head - *tail;
+ if (s < 0) s += bufsz;
+
+ if (count > s) {
+ ussp_dprintk (DEBUG_BUFFERS,
+ "cfcb: Not enough data in buffer for requested amount. %d s: %d\n",
+ count, s);
+ return -1;
+ }
+ while (count > 0) {
+ c = count;
+ if (c > (bufsz - *tail)) c = bufsz - *tail;
+
+ if (to_user)
+ copy_to_user (data, buffer + *tail, c);
+ else
+ memcpy (data, buffer + *tail, c);
+
+ data += c;
+ *tail += c;
+ if (*tail >= bufsz) *tail = 0;
+ count -= c;
+ }
+
+ func_exit ();
+ return 0;
+}
+
+
+static ssize_t ussp_write_to_ctl(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count, int operation)
+{
+ struct ussp_operation info;
+ struct ussp_port *port;
+ int sib, r;
+
+ func_enter();
+ if (count <= 0){
+ printk ("Count is smaller than 0!\n");
+ func_exit ();
+ return 0;
+ }
+
+ port = tty->driver_data;
+
+ if (!(port->flags & USSP_DEAMON_PRESENT)) {
+ printk("no deamon. Flags: %x\n", port->flags);
+ func_exit ();
+ return -ENODEV;
+ }
+
+ ussp_dprintk(DEBUG_WRITE_TO_CTL, "Port: %p, buf %p, count %d. ", port, buf, count);
+
+ if (!port->daemon_buffer) {
+ printk(KERN_ERR "ERROR!! port->daemon_buf is NULL!!!\n");
+ return -1;
+ }
+
+ sib = SPACE_IN_BUFFER (port->daemon_head, port->daemon_tail, PAGE_SIZE);
+ if (count > sib - sizeof (info))
+ count = sib - sizeof (info);
+
+ ussp_dprintk (DEBUG_WRITE_TO_CTL, "sib: %d count: %d\n", sib, count);
+
+ if (count <= 0) return 0;
+
+ info.op = operation;
+ info.len = count;
+ info.arg = 0;
+
+ r = copy_to_circular_buffer (port->daemon_buffer, PAGE_SIZE,
+ &port->daemon_head, &port->daemon_tail,
+ &info, sizeof (info), 0);
+ r += copy_to_circular_buffer (port->daemon_buffer, PAGE_SIZE,
+ &port->daemon_head, &port->daemon_tail,
+ buf, count, from_user);
+ if (r != (count + sizeof(info))) {
+ ussp_dprintk (DEBUG_WRITE_TO_CTL,
+ "Not enough space in buffer: r: %d, count: %d\n",
+ r, count);
+ return 0;
+ }
+
+ ussp_dprintk (DEBUG_WRITE_TO_CTL, "Waking up queue: %p\n",
+ &port->daemon_wait);
+ wake_up(&port->daemon_wait);
+
+ func_exit();
+ return count;
+}
+
+
+void ussp_tty_set_termios (struct tty_struct * tty,
+ struct termios * old_termios)
+{
+ func_enter ();
+
+ ussp_dprintk (USSP_SET_TERMIOS, "c_cflag: %x\n", tty->termios->c_cflag);
+
+ if (ussp_write_to_ctl (tty, 0, (char*)tty->termios, sizeof (struct termios), USSP_SET_TERMIOS) <= 0)
+ ussp_dprintk (DEBUG_WRITE_TO_CTL, "XXX Fix set termios: wait for space in buffer...\n");
+
+ func_exit ();
+}
+
+
+static ssize_t ussp_tty_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ int ret;
+
+ func_enter ();
+
+ ret = ussp_write_to_ctl (tty, from_user, buf, count, USSP_WRITE);
+
+ func_exit ();
+ return ret;
+}
+
+
+static void ussp_tty_put_char (struct tty_struct * tty, unsigned char ch)
+{
+ char tempchar = ch;
+
+ func_enter ();
+
+ ussp_tty_write (tty, 0, &tempchar, 1);
+
+ func_exit ();
+ return;
+}
+
+
+static int ussp_tty_open (struct tty_struct *tty, struct file * filp)
+{
+ struct ussp_port *port;
+ struct ussp_operation op;
+ int line;
+
+ func_enter();
+
+ MOD_INC_USE_COUNT;
+
+ line = MINOR (tty->device);
+ ussp_dprintk (1, "%d: opening line %d, tty=%p ctty=%p)\n",
+ current->pid, line, tty, current->tty);
+
+ port = & ussp_ports[line];
+
+ if (!(port->flags & USSP_DEAMON_PRESENT)) {
+ ussp_dprintk(DEBUG_OPEN, "no deamon. Flags: %x\n",
+ port->flags);
+ func_exit ();
+ return -ENODEV;
+ }
+
+ port->tty = tty;
+ tty->driver_data = port;
+
+ /* The userspace deamon will already packetize the dataflow,
+ so we don't need to introduce extra delay here in the driver. */
+ if (ussp_set_lowlatency)
+ tty->low_latency = 1;
+
+ ussp_dprintk (DEBUG_OPEN, "Nusers = %d.\n", port->nusers);
+ port->nusers++;
+ if (port->nusers > 1)
+ return 0;
+
+ port->callout_termios = tty_std_termios;
+ port->normal_termios = tty_std_termios;
+
+ op.op = USSP_OPEN;
+ op.len = 0;
+ op.arg = filp->f_flags;
+
+ copy_to_circular_buffer(port->daemon_buffer, PAGE_SIZE,
+ &port->daemon_head, &port->daemon_tail,
+ &op, sizeof (op), 0);
+
+ ussp_dprintk(DEBUG_OPEN, "Waking up daemon_wait\n");
+ wake_up(&port->daemon_wait);
+ while ((!TTY_DATA_AVAILABLE(port)) &&
+ (port->flags & USSP_DEAMON_PRESENT)) {
+ interruptible_sleep_on (&port->tty_wait);
+ ussp_dprintk (DEBUG_OPEN, "Back from sleep\n");
+ if (signal_pending (current)) {
+ func_exit ();
+ /* XXX Dec Use count????? */
+ return -EINTR;
+ }
+ }
+
+ if (!(port->flags & USSP_DEAMON_PRESENT)) {
+ ussp_dprintk(DEBUG_OPEN, "Daemon disappeared.\n");
+ return -EINTR;
+ }
+
+ copy_from_circular_buffer (port->tty_buffer, PAGE_SIZE,
+ &port->tty_head, &port->tty_tail,
+ &op, sizeof (op), 0);
+
+ if ((op.op == USSP_OPEN_RESULT) && (op.arg < 0)) {
+ ussp_dprintk(DEBUG_OPEN, "Daemon returned error.\n");
+ return -EINTR;
+ }
+
+ ussp_dprintk (DEBUG_OPEN, "current->leader: %d current->tty: %d tty->session: %d\n", (int)current->leader, (int)current->tty, (int)tty->session);
+ if (current->leader && !current->tty && tty->session == 0){
+ ussp_dprintk (DEBUG_OPEN, "setting!\n");
+ current->tty = tty;
+ current->tty_old_pgrp = 0;
+ tty->session = current->session;
+ tty->pgrp = current->pgrp;
+ }
+
+ func_exit();
+ return op.arg;
+}
+
+
+static void ussp_tty_close (struct tty_struct *tty, struct file * filep)
+{
+ struct ussp_port *port = tty->driver_data;
+ struct ussp_operation op;
+
+ func_enter();
+
+ ussp_dprintk(DEBUG_CLOSE, "Port: %p\n", port);
+ if (port) {
+ port->nusers--;
+ if (port->nusers < 0) {
+ printk (KERN_WARNING "Aaargh, nusers count has been corrupted: %d.\n", port->nusers);
+ port->nusers = 0;
+ }
+
+ if (port->nusers == 0){
+ op.op = USSP_CLOSE;
+ op.len = 0;
+ op.arg = 0;
+ ussp_dprintk (DEBUG_CLOSE, "Sending close packet to daemon\n");
+ if (port->flags & USSP_DEAMON_PRESENT)
+ copy_to_circular_buffer(port->daemon_buffer, PAGE_SIZE,
+ &port->daemon_head, &port->daemon_tail,
+ &op, sizeof (op), 0);
+ wake_up_interruptible (&port->daemon_wait);
+ port->tty = NULL;
+
+ }
+ }
+ MOD_DEC_USE_COUNT;
+ func_exit();
+}
+
+
+static int ussp_tty_ioctl (struct tty_struct * tty, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ussp_port *port;
+ int rc;
+ int ival;
+
+ func_enter();
+
+ port = tty->driver_data;
+ if (!(port->flags & USSP_DEAMON_PRESENT)) {
+ ussp_dprintk(DEBUG_IOCTL, "no daemon. Flags: %x\n",
+ port->flags);
+ func_exit ();
+ return -ENODEV;
+ }
+
+ ussp_dprintk (DEBUG_IOCTL, "IOCTL %x: %lx\n", cmd, arg);
+
+ rc = 0;
+ switch (cmd ){
+ case TIOCGSOFTCAR:
+ rc = Put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
+ (unsigned int *) arg);
+ break;
+ case TCGETA:
+ case TCGETS:
+ ussp_dprintk (DEBUG_IOCTL, "TCGETS\n");
+ rc = n_tty_ioctl (tty, (struct file *) filp, cmd, (unsigned long) arg);
+ ussp_dprintk (DEBUG_IOCTL, "Returning: %d\n", rc);
+
+ break;
+ case TIOCSSOFTCAR:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(int))) == 0) {
+ Get_user(ival, (unsigned int *) arg);
+ tty->termios->c_cflag =
+ (tty->termios->c_cflag & ~CLOCAL) |
+ (ival ? CLOCAL : 0);
+ }
+ break;
+ case TIOCMGET:
+ ussp_dprintk (DEBUG_IOCTL, "TIOCMGET\n");
+ if (copy_to_user ((unsigned int *)arg, &port->line_status, sizeof(int)))
+ rc = -EFAULT;
+ break;
+
+ case FIONREAD:
+ ussp_dprintk (DEBUG_IOCTL, "FIONREAD\n");
+ ival = PAGE_SIZE - ((port->daemon_head - port->daemon_tail) & (PAGE_SIZE-1)) - 1;
+ if (copy_to_user (tty->driver_data, &ival, sizeof(int)))
+ rc = -EFAULT;
+ break;
+ default:
+ printk (KERN_DEBUG "illegal ioctl: %x\n", cmd);
+ rc = -ENOIOCTLCMD;
+ }
+
+ func_exit();
+ return rc;
+}
+
+
+static int ussp_tty_write_room(struct tty_struct * tty)
+{
+ struct ussp_port *port = tty->driver_data;
+ int ret, ret2;
+
+ func_enter ();
+
+ ret = PAGE_SIZE - ((port->daemon_head - port->daemon_tail) & (PAGE_SIZE-1)) - 1;
+ ret2 = (port->daemon_tail - port->daemon_head) & (PAGE_SIZE-1);
+
+ ussp_dprintk (DEBUG_CIB, "Free size: %d ret2: %d\n", ret, ret2);
+
+ if(ret != ret2) {
+ printk(KERN_ERR "Size not equal\n");
+ }
+ if (ret < 0){
+ ret = 0;
+ printk(KERN_ERR "Something is very wrong here\n");
+ }
+
+ func_exit ();
+ return ret;
+}
+
+
+static int ussp_ctl_open(struct inode *inode, struct file *filp)
+{
+ func_enter();
+
+ MOD_INC_USE_COUNT;
+ filp->private_data = NULL;
+ func_exit();
+
+ return 0;
+}
+
+static int ussp_ctl_ioctl (struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ussp_port *port;
+ unsigned long flags;
+
+ func_enter();
+
+ port = filp->private_data;
+
+ ussp_dprintk (DEBUG_IOCTL, "CTL_IOCTL %x: %lx %ulx\n", cmd, arg, USSP_SET_PORT);
+
+ switch (cmd ) {
+
+ case USSP_SET_PORT:
+ ussp_dprintk (DEBUG_IOCTL, "USSP_SET_PORT\n");
+ if (port) {
+ port->flags &= ~USSP_DEAMON_PRESENT;
+ if (port->daemon_buffer)
+ free_page ((long) port->daemon_buffer);
+ if (port->tty_buffer)
+ free_page ((long) port->tty_buffer);
+ port->daemon_buffer =
+ port->tty_buffer = NULL;
+ filp->private_data = NULL;
+ }
+ if ((arg < 0) || ((int)arg > ussp_nports)) {
+ ussp_dprintk (DEBUG_IOCTL, "invalid arg: %d\n", (int)arg);
+ func_exit ();
+ return -EINVAL;
+ }
+ ussp_dprintk (DEBUG_IOCTL, "arg: %d\n", (int)arg);
+ port = &ussp_ports[(int)arg];
+ save_flags (flags);
+ cli ();
+ if (port->flags & USSP_DEAMON_PRESENT) {
+ restore_flags (flags);
+ return -EBUSY;
+ }
+ port->flags |= USSP_DEAMON_PRESENT;
+ restore_flags (flags);
+ printk("Setting private date to %p\n", filp->private_data);
+ filp->private_data = port;
+ init_waitqueue_head (&port->daemon_wait);
+ init_waitqueue_head (&port->tty_wait);
+ port->nusers = 0;
+ port->daemon_head = port->daemon_tail = port->tty_head = port->tty_tail = 0;
+ port->tty_buffer = (char*)get_free_page(GFP_KERNEL);
+ port->daemon_buffer = (char*)get_free_page(GFP_KERNEL);
+ printk("Got pages: %p, %p\n",port->daemon_buffer, port->tty_buffer);
+ port->line_status = 0;
+ break;
+ default:
+ printk ("illegal ioctl: %x\n", cmd);
+ }
+
+ func_exit();
+ return 0;
+}
+
+
+static ssize_t ussp_ctl_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct ussp_port *port;
+
+ func_enter();
+
+ if (!filp){
+ func_exit ();
+ return -EINVAL;
+ }
+
+ port = filp->private_data;
+ if (!port) {
+ func_exit ();
+ return -EINVAL;
+ }
+ ussp_dprintk (DEBUG_READ_CTL, "Checking data available: %ld\n", D_DATA_AVAILABLE(port));
+ while (!D_DATA_AVAILABLE(port)) {
+ interruptible_sleep_on (&port->daemon_wait);
+ if (signal_pending (current)) {
+ func_exit ();
+ return -EINTR;
+ }
+ }
+
+ if (count > D_DATA_AVAILABLE (port))
+ count = D_DATA_AVAILABLE (port);
+ if (copy_from_circular_buffer (port->daemon_buffer, PAGE_SIZE,
+ &port->daemon_head, &port->daemon_tail,
+ buf, count, 1)){
+ printk("Error copying to the daemon\n");
+ func_exit ();
+ return -EFAULT;
+ }
+
+ if (port->tty) {
+ if ((port->tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ (port->tty->ldisc.write_wakeup) &&
+ (D_DATA_AVAILABLE(port) < (PAGE_SIZE/4)))
+ port->tty->ldisc.write_wakeup(port->tty);
+ ussp_dprintk (DEBUG_READ_CTL, "Waking up write_wait\n");
+ wake_up_interruptible(&port->tty->write_wait);
+ }
+
+ func_exit ();
+
+ return count;
+}
+
+
+static ssize_t ussp_ctl_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct ussp_operation op;
+ struct ussp_port *port;
+ func_enter();
+
+ port = filp->private_data;
+
+ if (count <= 0){
+ ussp_dprintk (DEBUG_WRITE_TO_CTL, "Count is smaller then 0!\n");
+ func_exit ();
+ return 0;
+ }
+
+ if (!filp->private_data){
+ func_exit ();
+ return -EINVAL;
+ }
+
+ if (!port->tty_buffer)
+ ussp_dprintk (DEBUG_WRITE_TO_CTL, "ERROR!! port->tty_buffer is NULL!!!\n");
+
+ if (!port->tty){
+ ussp_dprintk (DEBUG_WRITE_TO_CTL, "ERROR!! port->tty is NULL !!!!!!\n");
+ func_exit ();
+ return -EINVAL;
+ }
+ copy_from_user (&op, buf, sizeof (op));
+ switch(op.op) {
+ case USSP_OPEN_RESULT:
+ printk ("USSP_OPEN_RESULT %d\n", (int)op.arg);
+ copy_to_circular_buffer (port->tty_buffer, PAGE_SIZE,
+ &port->tty_head, &port->tty_tail,
+ buf, sizeof (op), 1);
+ wake_up_interruptible (&port->tty_wait);
+ break;
+ case USSP_READ:
+ printk ("USSP_READ %d\n", (int)op.len);
+ copy_from_user (port->tty->flip.char_buf_ptr, buf + sizeof(struct ussp_operation), op.len);
+ memset(port->tty->flip.flag_buf_ptr, TTY_NORMAL, op.len);
+ port->tty->flip.count += op.len;
+ port->tty->flip.char_buf_ptr += op.len;
+ port->tty->flip.flag_buf_ptr += op.len;
+ tty_flip_buffer_push (port->tty);
+ break;
+ case USSP_MSC:
+ printk ("USSP_MSC packet: %x %x\n", (int)op.arg, (int)port->line_status);
+ if ((port->line_status & USSP_DCD) && (!(op.arg & USSP_DCD))) {
+ ussp_dprintk (DEBUG_IOCTL, "Obliged to perform hangup... \n");
+ /* tty_hangup (port->tty); */
+ }
+ port->line_status = op.arg;
+ break;
+ };
+ func_exit();
+ return 0;
+}
+
+
+static int ussp_ctl_close(struct inode *inodes, struct file * filp)
+{
+ struct ussp_port *port;
+
+ func_enter ();
+
+ ussp_dprintk(DEBUG_CLOSE, "filp: %p\n", filp);
+ if (filp) {
+ port = filp->private_data;
+ if (port) {
+ if (port->tty) {
+ ussp_dprintk (DEBUG_CLOSE, "Hanging up: %p\n", port);
+ /* Make sure the tty sides of this gets shutdown properly... */
+ tty_hangup (port->tty);
+ port->tty = NULL;
+ }
+ ussp_dprintk(DEBUG_CLOSE, "Freeing pages: %p, %p\n", port->daemon_buffer, port->tty_buffer);
+ if (port->daemon_buffer)
+ free_page ((long)port->daemon_buffer);
+
+ if (port->tty_buffer)
+ free_page ((long)port->tty_buffer);
+
+ port->daemon_buffer = port->tty_buffer = NULL;
+ ussp_dprintk(DEBUG_CLOSE, "Removing flag\n");
+ port->flags &= ~USSP_DEAMON_PRESENT;
+ wake_up_interruptible (&port->tty_wait);
+ } else {
+ /* This deamon must've been unattached... */
+ ussp_dprintk(DEBUG_CLOSE, "close: NULL Port!\n");
+ }
+ }
+
+ MOD_DEC_USE_COUNT;
+ func_exit();
+
+ return 0;
+}
+
+
+static unsigned int ussp_ctl_poll (struct file *filep, poll_table * wait)
+{
+ struct ussp_port *port;
+ int mask;
+
+ func_enter();
+ port = filep->private_data;
+ printk("port: %p\n", port);
+ if (port) {
+
+ printk("Before poll_wait port: %p,head: %x, tail: %x\n",
+ port, port->daemon_head, port->daemon_tail);
+ poll_wait (filep, &port->daemon_wait, wait);
+
+ mask = 0;
+ if(port->daemon_head != port->daemon_tail)
+ mask |= POLLIN;
+
+ printk("After poll_wait port: %p,head: %x, tail: %x, mask=%x\n",
+ port, port->daemon_head, port->daemon_tail, mask);
+ } else
+ {
+ printk("Port is 0\n");
+ mask = -ENODEV;
+ }
+ func_exit();
+ return mask;
+}
+
+
+static int ussp_tty_chars_in_buffer (struct tty_struct *tty)
+{
+ struct ussp_port *port;
+ int rv;
+
+ func_enter ();
+ port = tty->driver_data;
+ rv = (port->daemon_head - port->daemon_tail) & (PAGE_SIZE -1);
+ ussp_dprintk(DEBUG_CIB, "Chars in buffer %d\n", rv);
+ func_exit ();
+
+ return rv;
+}
+
+
+static void __exit ussp_exit(void)
+{
+ func_enter();
+
+ if (misc_deregister(&ussp_ctl_device) < 0) {
+ printk (KERN_INFO "ussp: couldn't deregister control device.\n");
+ }
+ if (tty_unregister_driver(&ussp_driver) < 0) {
+ printk (KERN_INFO "ussp: couldn't deregister tty device.\n");
+ }
+ func_exit();
+}
+
+module_init(ussp_init);
+module_exit(ussp_exit);
+
diff -u -r linux-2.4.0-test13-pre1.clean/include/linux/ussp.h linux-2.4.0-test13-pre1.ussp/include/linux/ussp.h
--- linux-2.4.0-test13-pre1.clean/include/linux/ussp.h Thu Dec 14 13:52:14 2000
+++ linux-2.4.0-test13-pre1.ussp/include/linux/ussp.h Thu Dec 14 10:29:57 2000
@@ -0,0 +1,76 @@
+/* -*- linux-c -*- */
+
+/*
+ * ussp.h
+ *
+ * Copyright (C) 2000 [email protected], [email protected]
+ *
+ * Version 0.1 April 2000 .
+ */
+
+#define USSP_WRITE 1
+#define USSP_READ 2
+#define USSP_SET_TERMIOS 3
+#define USSP_MSC 4
+#define USSP_OPEN 5
+#define USSP_CLOSE 6
+#define USSP_OPEN_RESULT 7
+#define USSP_FORCE_CLOSE 8
+
+/* Don't clash with normal openflags */
+#define USSP_ISCALLOUT 0x10000000
+
+#define USSP_MAX_PORTS 16
+
+struct ussp_operation {
+ int op;
+ unsigned long len;
+ unsigned long arg;
+ unsigned char data[0];
+};
+
+#define USSP_SET_PORT _IOW('U', 1, int)
+#define USSP_SET_PORT_WATCHER _IOW('U', 2, int)
+#define TIOSTATGET _IOW('U', 3, void *)
+
+#define USSP_DEAMON_PRESENT 0x000000001
+
+#ifdef __KERNEL__
+
+struct ussp_port {
+ int flags;
+ int nusers;
+ int line_status;
+ wait_queue_head_t daemon_wait;
+ char *daemon_buffer;
+ int daemon_head, daemon_tail;
+ wait_queue_head_t tty_wait;
+ char *tty_buffer;
+ int tty_head, tty_tail;
+
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct tty_struct *tty;
+};
+#endif
+
+#define USSP_DCD TIOCM_CAR
+#define USSP_RI TIOCM_RNG
+#define USSP_RTS TIOCM_RTS
+#define USSP_CTS TIOCM_CTS
+#define USSP_DTR TIOCM_DTR
+#define USSP_DSR TIOCM_DSR
+
+
+#ifndef USSPCTL_MISC_MINOR
+/* Allow others to gather this into "major.h" or something like that */
+#define USSPCTL_MISC_MINOR 192
+#endif
+
+#ifndef USSP_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h"
+ include or something like that */
+#define USSP_NORMAL_MAJOR 120
+#define USSP_CALLOUT_MAJOR 121
+#endif
+