Userspace serial drivers: PTY changes.
Subsystems (2)
RFC PATCH 1/1 Userspace serial drivers: PTY changes.
Hi,
Last time I implemented a complex network-using serial driver, I chose to implement the driver in userspace, communicating wiht the kernel through a special driver I called USSP: User-Space Serial Ports. The general consensus then was that I should've used ptys. I agree.
So this time, I've done things using ptys. There were however, some features that physical serial ports could do which ptys could not.
For example Baud rates. You could set the baud rate on the slave side, which was impossible to get at from the master side. Also modem control signals were not implemented.
Also we don't want the program on the master side to have to poll say every second to see if the other side changed the baud rate or did something else special. So when these are changed, the other side is signalled that a change has happened and notified....
So we implemented all this. Patch attached. What do you think?
(We can dial out or dial in on the remote modem using our userspace driver, without any significant differences from a normal local port!)
(This is not the time to bitch about that I generated the patch against the wrong kernel. I'll generate them against recent kernels when we agree that the followed path is OK. )
Roger.
Signature · 5 lines
** [email protected] ** http://www.BitWizard.nl/ ** +31-15-2600998 ** *-- BitWizard writes Linux device drivers for any device you may have! --* * The Worlds Ecosystem is a stable system. Stable systems may experience * * excursions from the stable situation. We are currently in such an * * excursion: The stable situation does not include humans. ***************
Followup to: <[email protected]> By author: Rogier Wolff <[email protected]> In newsgroup: linux.dev.kernel
Quoted 3 lines
So we implemented all this. Patch attached. What do you think?
-ENOPATCH?
-hpa
Signature · 3 lines
<[email protected]> at work, <[email protected]> in private! "Unix gives you enough rope to shoot yourself in the foot." Architectures needed: ia64 m68k mips64 ppc ppc64 s390 s390x sh v850 x86-64
Quoted 8 lines · H. Peter Anvin
Followup to: <[email protected]> By author: Rogier Wolff <[email protected]> In newsgroup: linux.dev.kernelQuoted 3 lines
So we implemented all this. Patch attached. What do you think?
-ENOPATCH?
--EAGREE
Signature · 5 lines
** [email protected] ** http://www.BitWizard.nl/ ** +31-15-2600998 ** *-- BitWizard writes Linux device drivers for any device you may have! --* * The Worlds Ecosystem is a stable system. Stable systems may experience * * excursions from the stable situation. We are currently in such an * * excursion: The stable situation does not include humans. ***************
drivers/char/pty.c +99 -0
@@ -262,16 +262,109 @@return 0 ; } +static int pty_slave_ioctl (struct tty_struct *tty,struct file *file, +unsigned int cmd,unsigned long arg) +{ + +struct tty_struct * real_tty; + +if (!tty) { +printk (KERN_ERR "%scalled with NULL tty!\n" , __FUNCTION__); +return -EIO ; + } + +if (tty->driver.type ==TTY_DRIVER_TYPE_PTY && + tty->driver.subtype ==PTY_TYPE_MASTER ) + real_tty = tty->link; +else + real_tty = tty; + +switch (cmd) { +case TIOCSBRK : +case TCSBRK : +case TCSBRKP : +if (tty->link && tty->link->packet) { + tty->ctrl_status |=TIOCPKT_BREAK ; +wake_up_interruptible (&tty->link->read_wait); + } +return 0 ; +case TIOCMBIC : +case TIOCMBIS : +case TIOCMSET : { +unsigned int new_val; + +get_user (arg, (unsigned int *) arg); + + new_val = real_tty->modem_status; +switch (cmd) { +case TIOCMBIC : + new_val &= ~arg; +break ; +case TIOCMBIS : + new_val |= arg; +break ; +case TIOCMSET : + new_val = arg; +break ; + } + +if (new_val != real_tty->modem_status) { +printk ("New status: 0x%x 0x%x\n" , new_val , real_tty->modem_status); + real_tty->modem_status = new_val; + real_tty->link->modem_status = new_val; + +if (real_tty->link && real_tty->link->packet) { + real_tty->ctrl_status |=TIOCPKT_SIGNALS ; +wake_up_interruptible (&real_tty->link->read_wait); + } + } + +return 0 ; + } +case TIOCMGET : +printk ("Getting modem status: 0x%x\n" , real_tty->modem_status); +if (put_user (real_tty->modem_status, (unsigned int *)arg)) +return -EFAULT ; +return 0 ; + } + + + +return -ENOIOCTLCMD ; +} + +int set_termios (struct tty_struct * tty,unsigned long arg,int opt); + +static int pty_bsd_ioctl (struct tty_struct *tty,struct file *file,unsigned int cmd,unsigned long arg) { +struct tty_struct * real_tty; +if (!tty) {printk ("pty_ioctl called with NULL tty!\n" );return -EIO ; } + +if (tty->driver.type ==TTY_DRIVER_TYPE_PTY && + tty->driver.subtype ==PTY_TYPE_MASTER ) + real_tty = tty->link; +else + real_tty = tty; + +switch (cmd) {case TIOCSPTLCK :/* Set PT Lock (disallow slave open) */ return pty_set_lock (tty, (int *) arg); +case TCGETS : +if (kernel_termios_to_user_termios ((struct termios *)arg, real_tty->termios)) +return -EFAULT ; +return 0 ; +case TIOCMSET : +get_user (arg, (unsigned int *) arg); + tty->modem_status = arg; + tty->link->modem_status = arg; +return 0 ; }return -ENOIOCTLCMD ; }
@@ -352,6 +445,11 @@ { tty->termios->c_cflag &= ~(CSIZE |PARENB ); tty->termios->c_cflag |= (CS8 |CREAD ); + +if (tty->link && tty->link->packet) { + tty->ctrl_status |=TIOCPKT_TERMIOS ; +wake_up_interruptible (&tty->link->read_wait); + } }int __init pty_init (void )
@@ -420,6 +518,7 @@ pty_slave_driver.termios_locked = ttyp_termios_locked; pty_slave_driver.driver_state = pty_state; pty_slave_driver.other = &pty_driver; + pty_slave_driver.ioctl = pty_slave_ioctl;if (tty_register_driver (&pty_driver))panic ("Couldn't register pty driver" );
drivers/char/tty_ioctl.c +39 -1
@@ -138,7 +138,7 @@ (*tty->ldisc.set_termios)(tty, &old_termios); } -static int set_termios (struct tty_struct * tty,unsigned long arg,int opt) +int set_termios (struct tty_struct * tty,unsigned long arg,int opt) {struct termios tmp_termios;int retval =tty_check_change (tty);
@@ -505,6 +505,44 @@ ((tty->termios->c_cflag & ~CLOCAL ) | (arg ?CLOCAL :0 ));return 0 ; +case TIOCMGET : +if (put_user (tty->link->modem_status, (unsigned int *)arg)) +return -EFAULT ; +return 0 ; +case TIOCMSET : { +unsigned int new_val; +get_user (new_val, (unsigned int *) arg); + real_tty->modem_status = new_val; + real_tty->link->modem_status = new_val; +return 0 ; + } +case TIOCMBIC : +if (get_user (arg, (unsigned int *) arg)) +return -EFAULT ; + +if (arg &TIOCM_DTR ) + tty->link->modem_status &= ~TIOCM_DTR ; + +if (arg &TIOCM_RTS ) + tty->link->modem_status &= ~TIOCM_RTS ; + +wake_up_interruptible (&tty->link->read_wait); +return 0 ; + + +case TIOCMBIS : +if (get_user (arg, (unsigned int *) arg)) +return -EFAULT ; + +if (arg &TIOCM_DTR ) + tty->link->modem_status |=TIOCM_DTR ; + +if (arg &TIOCM_RTS ) + tty->link->modem_status |=TIOCM_RTS ; + +wake_up_interruptible (&tty->link->read_wait); +return 0 ; +default :return -ENOIOCTLCMD ; }
include/asm-i386/ioctls.h +10 -0
@@ -77,6 +77,16 @@#define TIOCPKT_NOSTOP 16#define TIOCPKT_DOSTOP 32 +/* Ugly hack. We use one bit for two purposes to prevent having to expand +the byte (API!). The deal is that you pretend that they are separate bits +when setting or reading these bits. However when you check if a bit is +set (indicating something changed), you have to be prepared that it +in fact didn't change at all..... (As you also have to be prepared to +handle the case where it changed back before you noticed it.) -- REW */ +#define TIOCPKT_SIGNALS 64 +#define TIOCPKT_TERMIOS 64 +#define TIOCPKT_BREAK 128 +#define TIOCSER_TEMT 0x01/* Transmitter physically empty */ #endif
include/linux/tty.h +1 -0
@@ -270,6 +270,7 @@ unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; unsigned char low_latency:1, warned:1;unsigned char ctrl_status; +unsigned int modem_status;struct tty_struct *link;struct fasync_struct *fasync;