TTY changes to 2.1.65

tytso@mit.edu
Mon, 24 Nov 1997 11:33:18 -0500


Hi Linus,

The following patches fix a few bugs in the serial driver, as
well as starts a process of taking common functionality which is being
done in each tty low-level driver, and moves it up to the high-level tty
driver. The strategy which is followed is much like the one followed by
the SCSI layer --- there are high-level functions which handle the
common case which are available to the low-level driver, but each
low-level driver is always free to do its own thing if it needs to do so
for some reason.

This set of changes moves the SOFTCAR ioctl's, break handling,
and the tty baud rate determination to the high-level code. Future
changes which I plan to make will include the modem control line
ioctl's, the icount functions, and the throttle/unthrottle functions.
(They are all somewhat inter-related.)

The changes are all backwards compatible, so old drivers will
continue to function with the new tty driver changes, although they
won't take advantage of the new high-level functionality. I've made
changes to the esp, serial, and rocketport drivers mainly as a proof of
concept to make sure the new high-level abstractions make sense.

Linus, if you could apply these patches into the 2.1 tree, I'd
greatly appreciate it. Thanks!!

- Ted

Patch generated: on Mon Nov 24 11:26:35 EST 1997 by tytso@rsts-11
against Linux version 2.1.65

===================================================================
RCS file: drivers/char/RCS/ChangeLog,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/ChangeLog
--- drivers/char/ChangeLog 1997/11/24 16:21:16 1.1
+++ drivers/char/ChangeLog 1997/11/24 16:20:23
@@ -1,3 +1,47 @@
+Mon Nov 24 10:37:49 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c, esp.c, rocket.c: Change drivers to take advantage of
+ tty_get_baud_rate().
+
+ * tty_io.c (tty_get_baud_rate): New function which computes the
+ correct baud rate for the tty. More factoring out of
+ common code out of the serial driver to the high-level tty
+ functions....
+
+Sat Nov 22 07:53:36 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c, esp.c, rocket.c: Add tty->driver.break() routine, and
+ allow high-level tty code to handle the break and soft
+ carrier ioctls.
+
+ * tty_ioctl.c (n_tty_ioctl): Support TIOCGSOFTCAR and
+ TIOCSSOFTCAR, so that device drivers don't have to support
+ it.
+
+ * serial.c (autoconfig): Change 16750 test to hopefully eliminate
+ false results by people with strange 16550A's being
+ detected as 16750's. Hopefully 16750's will still be
+ detected as 16750, and other wierd UART's won't get poorly
+ autodetected. If this doesn't work, I'll have to disable
+ the auto identification for the 16750....
+
+ * tty_io.c (tty_hangup): Now do actually do the tty hangup
+ processing during the timer processing, and disable
+ interrupts while doing the hangup processing. This avoids
+ several nasty race conditions which happened when the
+ hangup processing was done asynchronously.
+ (tty_ioctl): Do break handling in the tty driver if
+ driver's break function is supported.
+ (tty_flip_buffer_push): New exported function which should
+ be used by drivers to push characters in the flip buffer
+ to the tty handler. This may either be done using a task
+ queue function for better CPU efficiency, or directly for
+ low latency operation.
+
+ * serial.c (rs_set_termios): Fix bug rs_set_termios when
+ transitioning away from B0, submitted by Stanislav
+ Voronyi.
+
Thu Jun 19 20:05:58 1997 Theodore Ts'o <tytso@rsts-11.mit.edu>

* serial.c (begin_break, end_break, rs_ioctl): Applied patch
===================================================================
RCS file: drivers/char/RCS/serial.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/serial.c
--- drivers/char/serial.c 1997/11/19 06:49:34 1.1
+++ drivers/char/serial.c 1997/11/24 16:26:27
@@ -22,6 +22,9 @@
* 1/97: Extended dumb serial ports are a config option now.
* Saves 4k. Michael A. Griffith <grif@acm.org>
*
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
* This module exports the following rs232 io functions:
*
* int rs_init(void);
@@ -319,13 +322,6 @@
return 0;
}

-/*
- * This is used to figure out the divisor speeds and the timeouts
- */
-static int baud_table[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
-
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
#ifdef CONFIG_HUB6
@@ -541,7 +537,7 @@
ignore_char:
*status = serial_inp(info, UART_LSR);
} while (*status & UART_LSR_DR);
- queue_task(&tty->flip.tqueue, &tq_timer);
+ tty_flip_buffer_push(tty);
}

static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -624,10 +620,10 @@
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
- printk("scheduling hangup...");
+ printk("doing serial hangup...");
#endif
- queue_task(&info->tqueue_hangup,
- &tq_scheduler);
+ if (info->tty)
+ tty_hangup(info->tty);
}
}
if (info->flags & ASYNC_CTS_FLOW) {
@@ -908,28 +904,6 @@
}

/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred. The path of
- * hangup processing is:
- *
- * serial interrupt routine -> (scheduler tqueue) ->
- * do_serial_hangup() -> tty->hangup() -> rs_hangup()
- *
- */
-static void do_serial_hangup(void *private_)
-{
- struct async_struct *info = (struct async_struct *) private_;
- struct tty_struct *tty;
-
- tty = info->tty;
- if (!tty)
- return;
-
- tty_hangup(tty);
-}
-
-
-/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to handle ports that do not have an interrupt
* (irq=0). This doesn't work very well for 16450's, but gives barely
@@ -1062,7 +1036,6 @@
unsigned short ICP;
#endif

-
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
@@ -1365,9 +1338,9 @@
static void change_speed(struct async_struct *info)
{
unsigned short port;
- int quot = 0, baud_base;
+ int quot = 0, baud_base, baud;
unsigned cflag, cval, fcr = 0;
- int i, bits;
+ int bits;
unsigned long flags;

if (!info->tty || !info->tty->termios)
@@ -1401,37 +1374,20 @@
#endif

/* Determine divisor based on baud rate */
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 4)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 2;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- i += 3;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- i += 4;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- quot = info->state->custom_divisor;
- }
+ baud = tty_get_baud_rate(info->tty);
baud_base = info->state->baud_base;
- if (!quot) {
- if (baud_table[i] == 134)
+ if (baud == 38400)
+ quot = info->state->custom_divisor;
+ else {
+ if (baud == 134)
/* Special case since 134 is really 134.5 */
quot = (2*baud_base / 269);
- else if (baud_table[i])
- quot = baud_base / baud_table[i];
- /* If the quotient is ever zero, default to 9600 bps */
- if (!quot)
- quot = baud_base / 9600;
+ else if (baud)
+ quot = baud_base / baud;
}
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
info->quot = quot;
info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
info->timeout += HZ/50; /* Add .02 seconds of slop */
@@ -1843,8 +1799,17 @@
if (state->flags & ASYNC_INITIALIZED) {
if (((old_state.flags & ASYNC_SPD_MASK) !=
(state->flags & ASYNC_SPD_MASK)) ||
- (old_state.custom_divisor != state->custom_divisor))
+ (old_state.custom_divisor != state->custom_divisor)) {
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
change_speed(info);
+ }
} else
retval = startup(info);
return retval;
@@ -1975,51 +1940,27 @@
return 0;
}

-
/*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
*/
-static void send_break( struct async_struct * info, int duration)
+static void rs_break(struct tty_struct *tty, int break_state)
{
- if (!info->port)
- return;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
-#ifdef SERIAL_DEBUG_SEND_BREAK
- printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
-#endif
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- schedule();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- sti();
-#ifdef SERIAL_DEBUG_SEND_BREAK
- printk("done jiffies=%lu\n", jiffies);
-#endif
-}
-
-/*
- * This routine sets the break condition on the serial port.
- */
-static void begin_break(struct async_struct * info)
-{
- if (!info->port)
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "rs_break"))
return;
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
- sti();
-}

-/*
- * This routine clears the break condition on the serial port.
- */
-static void end_break(struct async_struct * info)
-{
if (!info->port)
return;
- cli();
- serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
- sti();
+ save_flags(flags); cli();
+ if (break_state == -1)
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) | UART_LCR_SBC);
+ else
+ serial_out(info, UART_LCR,
+ serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
+ restore_flags(flags);
}

/*
@@ -2186,7 +2127,6 @@
{
int error;
struct async_struct * info = (struct async_struct *)tty->driver_data;
- int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */

@@ -2202,53 +2142,6 @@
}

switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -EINTR;
- if (!arg) {
- send_break(info, HZ/4); /* 1/4 second */
- if (signal_pending(current))
- return -EINTR;
- }
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -EINTR;
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (signal_pending(current))
- return -EINTR;
- return 0;
- case TIOCSBRK:
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- begin_break(info);
- return 0;
- case TIOCCBRK:
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- end_break(info);
- return 0;
- case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
- case TIOCSSOFTCAR:
- error = get_user(arg, (unsigned int *) arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -2379,8 +2272,8 @@
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD)) {
info->MCR |= UART_MCR_DTR;
- if (!tty->hw_stopped ||
- !(tty->termios->c_cflag & CRTSCTS)) {
+ if (!(tty->termios->c_cflag & CRTSCTS) ||
+ !test_bit(TTY_THROTTLED, &tty->flags)) {
info->MCR |= UART_MCR_RTS;
}
cli();
@@ -2617,10 +2510,8 @@
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
- if (info->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
#else
return -EAGAIN;
#endif
@@ -2758,8 +2649,6 @@
info->line = line;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
info->state = sstate;
if (sstate->info) {
kfree_s(info, sizeof(struct async_struct));
@@ -2807,7 +2696,22 @@
else
tmp_buf = (unsigned char *) page;
}
-
+
+ /*
+ * If the port is the middle of closing, bail out now
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ASYNC_CLOSING)) {
+ if (info->flags & ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+ return ((info->flags & ASYNC_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ return -EAGAIN;
+#endif
+ }
+
/*
* Start up serial port
*/
@@ -3201,7 +3105,6 @@
scratch = serial_in(info, UART_IIR) >> 5;
if (scratch == 7) {
serial_outp(info, UART_LCR, 0);
- serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 5;
if (scratch == 6)
state->type = PORT_16750;
@@ -3316,6 +3219,7 @@
serial_driver.stop = rs_stop;
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
+ serial_driver.break_ctl = rs_break;
serial_driver.wait_until_sent = rs_wait_until_sent;
serial_driver.read_proc = rs_read_proc;

===================================================================
RCS file: drivers/char/RCS/tty_io.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/tty_io.c
--- drivers/char/tty_io.c 1997/11/19 06:49:34 1.1
+++ drivers/char/tty_io.c 1997/11/24 01:28:38
@@ -366,14 +366,18 @@
NULL /* hung_up_tty_fasync */
};

-void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
+void do_tty_hangup(void *data)
{
-
+ struct tty_struct *tty = (struct tty_struct *) data;
struct file * filp;
struct task_struct *p;
+ unsigned long flags;

if (!tty)
return;
+
+ save_flags(flags); cli();
+
check_tty_count(tty, "do_tty_hangup");
for (filp = inuse_filps; filp; filp = filp->f_next) {
if (filp->private_data != tty)
@@ -387,7 +391,7 @@
if (filp->f_op != &tty_fops)
continue;
tty_fasync(filp, 0);
- filp->f_op = fops;
+ filp->f_op = &hung_up_tty_fops;
}

if (tty->ldisc.flush_buffer)
@@ -404,6 +408,8 @@
* Shutdown the current line discipline, and reset it to
* N_TTY.
*/
+ if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
+ *tty->termios = tty->driver.init_termios;
if (tty->ldisc.num != ldiscs[N_TTY].num) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
@@ -435,10 +441,9 @@
tty->session = 0;
tty->pgrp = -1;
tty->ctrl_status = 0;
- if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
- *tty->termios = tty->driver.init_termios;
if (tty->driver.hangup)
(tty->driver.hangup)(tty);
+ restore_flags(flags);
}

void tty_hangup(struct tty_struct * tty)
@@ -446,7 +451,7 @@
#ifdef TTY_DEBUG_HANGUP
printk("%s hangup...\n", tty_name(tty));
#endif
- do_tty_hangup(tty, &hung_up_tty_fops);
+ queue_task(&tty->tq_hangup, &tq_timer);
}

void tty_vhangup(struct tty_struct * tty)
@@ -454,7 +459,7 @@
#ifdef TTY_DEBUG_HANGUP
printk("%s vhangup...\n", tty_name(tty));
#endif
- do_tty_hangup(tty, &hung_up_tty_fops);
+ do_tty_hangup((void *) tty);
}

int tty_hung_up_p(struct file * filp)
@@ -1490,15 +1495,26 @@
{
int retval, ldisc;

- retval = tty_check_change(tty);
- if (retval)
- return retval;
retval = get_user(ldisc, arg);
if (retval)
return retval;
return tty_set_ldisc(tty, ldisc);
}

+static int send_break(struct tty_struct *tty, int duration)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + duration;
+
+ tty->driver.break_ctl(tty, -1);
+ if (!signal_pending(current))
+ schedule();
+ tty->driver.break_ctl(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ return 0;
+}
+
/*
* Split this up, as gcc can choke on it otherwise..
*/
@@ -1506,6 +1522,7 @@
unsigned int cmd, unsigned long arg)
{
struct tty_struct *tty, *real_tty;
+ int retval;

tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
@@ -1516,6 +1533,46 @@
tty->driver.subtype == PTY_TYPE_MASTER)
real_tty = tty->link;

+ /*
+ * Break handling by driver
+ */
+ if (!tty->driver.break_ctl) {
+ switch(cmd) {
+ case TIOCSBRK:
+ case TIOCCBRK:
+ return tty->driver.ioctl(tty, file, cmd, arg);
+
+ /* These two ioctl's always return success; even if */
+ /* the driver doesn't support them. */
+ case TCSBRK:
+ case TCSBRKP:
+ retval = tty->driver.ioctl(tty, file, cmd, arg);
+ if (retval == -ENOIOCTLCMD)
+ retval = 0;
+ return retval;
+ }
+ }
+
+ /*
+ * Factor out some common prep work
+ */
+ switch (cmd) {
+ case TIOCSETD:
+ case TIOCSBRK:
+ case TIOCCBRK:
+ case TCSBRK:
+ case TCSBRKP:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ if (cmd != TIOCCBRK) {
+ tty_wait_until_sent(tty, 0);
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ break;
+ }
+
switch (cmd) {
case TIOCSTI:
return tiocsti(tty, (char *)arg);
@@ -1556,6 +1613,28 @@
#endif
case TIOCTTYGSTRUCT:
return tiocttygstruct(tty, (struct tty_struct *) arg);
+
+ /*
+ * Break handling
+ */
+ case TIOCSBRK: /* Turn break on, unconditionally */
+ tty->driver.break_ctl(tty, -1);
+ return 0;
+
+ case TIOCCBRK: /* Turn break off, unconditionally */
+ tty->driver.break_ctl(tty, 0);
+ return 0;
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ /*
+ * XXX is the above comment correct, or the
+ * code below correct? Is this ioctl used at
+ * all by anyone?
+ */
+ if (!arg)
+ return send_break(tty, HZ/4);
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
}
if (tty->driver.ioctl) {
int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
@@ -1630,13 +1709,14 @@
unsigned char *cp;
char *fp;
int count;
+ unsigned long flags;

if (tty->flip.buf_num) {
cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
tty->flip.buf_num = 0;

- cli();
+ save_flags(flags); cli();
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
} else {
@@ -1644,22 +1724,57 @@
fp = tty->flip.flag_buf;
tty->flip.buf_num = 1;

- cli();
+ save_flags(flags); cli();
tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
}
count = tty->flip.count;
tty->flip.count = 0;
- sti();
+ restore_flags(flags);

-#if 0
- if (count > tty->max_flip_cnt)
- tty->max_flip_cnt = count;
-#endif
tty->ldisc.receive_buf(tty, cp, fp, count);
}

/*
+ * Routine which returns the baud rate of the tty
+ */
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+int tty_get_baud_rate(struct tty_struct *tty)
+{
+ unsigned int cflag, i;
+
+ cflag = tty->termios->c_cflag;
+
+ i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 4)
+ tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (i==15 && tty->alt_speed)
+ return(tty->alt_speed);
+
+ return baud_table[i];
+}
+
+void tty_flip_buffer_push(struct tty_struct *tty)
+{
+ if (tty->low_latency)
+ flush_to_ldisc((void *) tty);
+ else
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+/*
* This subroutine initializes a tty structure.
*/
static void initialize_tty_struct(struct tty_struct *tty)
@@ -1673,6 +1788,8 @@
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
tty->flip.pty_sem = MUTEX;
+ tty->tq_hangup.routine = do_tty_hangup;
+ tty->tq_hangup.data = tty;
}

/*
===================================================================
RCS file: drivers/char/RCS/tty_ioctl.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/tty_ioctl.c
--- drivers/char/tty_ioctl.c 1997/11/19 07:36:37 1.1
+++ drivers/char/tty_ioctl.c 1997/11/19 12:36:23
@@ -509,18 +509,15 @@
tty->packet = 0;
return 0;
}
- /* These two ioctl's always return success; even if */
- /* the driver doesn't support them. */
- case TCSBRK: case TCSBRKP:
- retval = tty_check_change(tty);
+ case TIOCGSOFTCAR:
+ return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
+ case TIOCSSOFTCAR:
+ retval = get_user(arg, (unsigned int *) arg);
if (retval)
return retval;
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -EINTR;
- if (!tty->driver.ioctl)
- return 0;
- tty->driver.ioctl(tty, file, cmd, arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
return 0;
default:
return -ENOIOCTLCMD;
===================================================================
RCS file: drivers/char/RCS/esp.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/esp.c
--- drivers/char/esp.c 1997/11/19 06:49:34 1.1
+++ drivers/char/esp.c 1997/11/24 16:19:04
@@ -47,6 +47,7 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
+#include <linux/serialP.h>
#include <linux/serial_reg.h>
#include <linux/major.h>
#include <linux/string.h>
@@ -140,11 +141,8 @@
static void rs_wait_until_sent(struct tty_struct *, int);

/*
- * This assumes you have a 1.8432 MHz clock for your UART.
- *
- * It'd be nice if someone built a serial card with a 24.576 MHz
- * clock, since the 16550A is capable of handling a top speed of 1.5
- * megabits/second; but this requires the faster clock.
+ * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
+ * times the normal 1.8432 Mhz clock of most serial boards).
*/
#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))

@@ -192,15 +190,6 @@
return 0;
}

-/*
- * This is used to figure out the divisor speeds
- */
-static int quot_table[] = {
-/* 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, */
- 0, 18432, 12288, 8378, 6878, 6144, 4608, 3072, 1536, 768,
-/* 1800,2400,4800,9600,19200,38400,57600,115200,230400,460800 */
- 512, 384, 192, 96, 48, 24, 16, 8, 4, 2, 0 };
-
static inline unsigned int serial_in(struct esp_struct *info, int offset)
{
return inb(info->port + offset);
@@ -1096,7 +1085,7 @@
unsigned short port;
int quot = 0;
unsigned cflag,cval;
- int i, bits;
+ int baud, bits;
unsigned char flow1 = 0, flow2 = 0;
unsigned long flags;

@@ -1104,27 +1093,7 @@
return;
cflag = info->tty->termios->c_cflag;
port = info->port;
- i = cflag & CBAUD;
- if (i & CBAUDEX) {
- i &= ~CBAUDEX;
- if (i < 1 || i > 2)
- info->tty->termios->c_cflag &= ~CBAUDEX;
- else
- i += 15;
- }
- if (i == 15) {
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- i += 1;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- i += 2;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- i += 3;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- i += 4;
- if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
- quot = info->custom_divisor;
- }
-
+
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: cval = 0x00; bits = 7; break;
@@ -1148,14 +1117,20 @@
cval |= UART_LCR_SPAR;
#endif

- if (!quot) {
- quot = quot_table[i];
-
- /* default to 9600 bps */
- if (!quot)
- quot = BASE_BAUD / 9600;
- }
-
+ baud = tty_get_baud_rate(info->tty);
+ if (baud == 38400)
+ quot = info->custom_divisor;
+ else {
+ if (baud == 134)
+ /* Special case since 134 is really 134.5 */
+ quot = (2*BASE_BAUD / 269);
+ else if (baud)
+ quot = BASE_BAUD / baud;
+ }
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = BASE_BAUD / 9600;
+
info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);

/* CTS flow control flag and modem status interrupts */
@@ -1632,8 +1607,17 @@
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
(old_info.custom_divisor != info->custom_divisor) ||
- change_flow)
+ change_flow) {
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+ info->tty->alt_speed = 460800;
change_speed(info);
+ }
} else
retval = startup(info);
return retval;
@@ -1723,30 +1707,27 @@
}

/*
- * This routine sends a break character out the serial port.
+ * rs_break() --- routine which turns the break handling on or off
*/
-static void send_break( struct esp_struct * info, int duration)
+static void esp_break(struct tty_struct *tty, int break_state)
{
- cli();
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x01);
+ struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "esp_break"))
+ return;

- interruptible_sleep_on(&info->break_wait);
+ save_flags(flags); cli();
+ if (break_state == -1) {
+ serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+ serial_out(info, UART_ESI_CMD2, 0x01);

- if (signal_pending(current)) {
+ interruptible_sleep_on(&info->break_wait);
+ } else {
serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
serial_out(info, UART_ESI_CMD2, 0x00);
- sti();
- return;
}
-
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + duration;
- schedule();
-
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x00);
- sti();
+ restore_flags(flags);
}

static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1754,7 +1735,6 @@
{
int error;
struct esp_struct * info = (struct esp_struct *)tty->driver_data;
- int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */

@@ -1770,41 +1750,6 @@
}

switch (cmd) {
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -EINTR;
- if (!arg) {
- send_break(info, HZ/4); /* 1/4 second */
- if (signal_pending(current))
- return -EINTR;
- }
- return 0;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- retval = tty_check_change(tty);
- if (retval)
- return retval;
- tty_wait_until_sent(tty, 0);
- if (signal_pending(current))
- return -EINTR;
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- if (signal_pending(current))
- return -EINTR;
- return 0;
- case TIOCGSOFTCAR:
- return put_user(C_CLOCAL(tty) ? 1 : 0,
- (int *) arg);
- case TIOCSSOFTCAR:
- error = get_user(arg, (unsigned int *)arg);
- if (error)
- return error;
- tty->termios->c_cflag =
- ((tty->termios->c_cflag & ~CLOCAL) |
- (arg ? CLOCAL : 0));
- return 0;
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -2527,6 +2472,7 @@
esp_driver.stop = rs_stop;
esp_driver.start = rs_start;
esp_driver.hangup = esp_hangup;
+ esp_driver.break_ctl = esp_break;
esp_driver.wait_until_sent = rs_wait_until_sent;

/*
===================================================================
RCS file: drivers/char/RCS/rocket.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/rocket.c
--- drivers/char/rocket.c 1997/11/19 07:36:46 1.1
+++ drivers/char/rocket.c 1997/11/24 15:47:57
@@ -622,10 +622,12 @@
rp_table[line] = info;
}

+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300,
600, 1200, 1800, 2400, 4800, 9600, 19200,
38400, 57600, 115200, 230400, 460800, 0 };
+#endif

/*
* This routine configures a rocketport port so according to its
@@ -671,6 +673,7 @@
}

/* baud rate */
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
i = cflag & CBAUD;
if (i & CBAUDEX) {
i &= ~CBAUDEX;
@@ -690,6 +693,11 @@
i += 4;
}
baud = baud_table[i] ? baud_table[i] : 9600;
+#else
+ baud = tty_get_baud_rate(info->tty);
+ if (!baud)
+ baud = 9600;
+#endif
info->cps = baud / bits;
sSetBaud(cp, (rp_baud_base/baud) - 1);

@@ -1182,7 +1190,7 @@
/*
* Here are the routines used by rp_ioctl
*/
-
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
static void send_break( struct r_port * info, int duration)
{
current->state = TASK_INTERRUPTIBLE;
@@ -1193,6 +1201,24 @@
sClrBreak(&info->channel);
sti();
}
+#else
+static void rp_break(struct tty_struct *tty, int break_state)
+{
+ struct r_port * info = (struct r_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (rocket_paranoia_check(info, tty->device, "rp_break"))
+ return;
+
+ save_flags(flags); cli();
+ if (break_state == -1) {
+ sSendBreak(&info->channel);
+ } else {
+ sClrBreak(&info->channel);
+ }
+ restore_flags(flags);
+}
+#endif

static int get_modem_info(struct r_port * info, unsigned int *value)
{
@@ -1289,8 +1315,19 @@
(new_serial.flags & ROCKET_FLAGS));
info->close_delay = new_serial.close_delay;
info->closing_wait = new_serial.closing_wait;
- configure_r_port(info);
+
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI)
+ info->tty->alt_speed = 57600;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_VHI)
+ info->tty->alt_speed = 115200;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_SHI)
+ info->tty->alt_speed = 230400;
+ if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_WARP)
+ info->tty->alt_speed = 460800;
+#endif

+ configure_r_port(info);
return 0;
}

@@ -1319,15 +1356,17 @@
static int rp_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int tmp;
struct r_port * info = (struct r_port *)tty->driver_data;
- int retval;
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
+ int retval, tmp;
+#endif

if (cmd != RCKP_GET_PORTS &&
rocket_paranoia_check(info, tty->device, "rp_ioctl"))
return -ENODEV;

switch (cmd) {
+#if (LINUX_VERSION_CODE < 131393) /* Linux 2.1.65 */
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
@@ -1365,6 +1404,7 @@
((tty->termios->c_cflag & ~CLOCAL) |
(tmp ? CLOCAL : 0));
return 0;
+#endif
case TIOCMGET:
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
@@ -2093,6 +2133,9 @@
rocket_driver.stop = rp_stop;
rocket_driver.start = rp_start;
rocket_driver.hangup = rp_hangup;
+#if (LINUX_VERSION_CODE >= 131393) /* Linux 2.1.65 */
+ rocket_driver.break_ctl = rp_break;
+#endif
#if (LINUX_VERSION_CODE >= 131343)
rocket_driver.send_xchar = rp_send_xchar;
rocket_driver.wait_until_sent = rp_wait_until_sent;
===================================================================
RCS file: kernel/RCS/ksyms.c,v
retrieving revision 1.1
diff -u -r1.1 kernel/ksyms.c
--- kernel/ksyms.c 1997/11/19 06:49:34 1.1
+++ kernel/ksyms.c 1997/11/24 15:06:10
@@ -242,6 +242,8 @@
EXPORT_SYMBOL(tty_wait_until_sent);
EXPORT_SYMBOL(tty_check_change);
EXPORT_SYMBOL(tty_hung_up_p);
+EXPORT_SYMBOL(tty_flip_buffer_push);
+EXPORT_SYMBOL(tty_get_baud_rate);
EXPORT_SYMBOL(do_SAK);
EXPORT_SYMBOL(console_print);

===================================================================
RCS file: include/linux/RCS/tty.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty.h
--- include/linux/tty.h 1997/11/19 06:49:34 1.1
+++ include/linux/tty.h 1997/11/24 01:26:09
@@ -21,6 +21,7 @@
#include <linux/tqueue.h>
#include <linux/tty_driver.h>
#include <linux/tty_ldisc.h>
+#include <linux/serialP.h>

#include <asm/system.h>

@@ -223,14 +224,17 @@
int count;
struct winsize winsize;
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
+ unsigned char low_latency:1;
unsigned char ctrl_status;

struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_flip_buffer flip;
int max_flip_cnt;
+ int alt_speed; /* For magic substitution of 38400 bps */
struct wait_queue *write_wait;
struct wait_queue *read_wait;
+ struct tq_struct tq_hangup;
void *disc_data;
void *driver_data;

@@ -329,6 +333,8 @@
extern int tty_hung_up_p(struct file * filp);
extern void do_SAK(struct tty_struct *tty);
extern void disassociate_ctty(int priv);
+extern void tty_flip_buffer_push(struct tty_struct *tty);
+extern int tty_get_baud_rate(struct tty_struct *tty);

/* n_tty.c */
extern struct tty_ldisc tty_ldisc_N_TTY;
===================================================================
RCS file: include/linux/RCS/tty_driver.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty_driver.h
--- include/linux/tty_driver.h 1997/11/19 07:38:05 1.1
+++ include/linux/tty_driver.h 1997/11/24 01:26:09
@@ -92,6 +92,18 @@
* This routine notifies the tty driver that it should hangup the
* tty device.
*
+ * void (*break_ctl)(struct tty_stuct *tty, int state);
+ *
+ * This optional routine requests the tty driver to turn on or
+ * off BREAK status on the RS-232 port. If state is -1,
+ * then the BREAK status should be turned on; if state is 0, then
+ * BREAK should be turned off.
+ *
+ * If this routine is implemented, the high-level tty driver will
+ * handle the following ioctls: TCSBRK, TCSBRKP, TIOCSBRK,
+ * TIOCCBRK. Otherwise, these ioctls will be passed down to the
+ * driver to handle.
+ *
* void (*wait_until_sent)(struct tty_struct *tty, int timeout);
*
* This routine waits until the device has written out all of the
@@ -148,6 +160,7 @@
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
+ void (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
===================================================================
RCS file: include/linux/RCS/serial.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/serial.h
--- include/linux/serial.h 1997/11/19 06:49:34 1.1
+++ include/linux/serial.h 1997/11/19 06:49:48
@@ -132,113 +132,6 @@


#ifdef __KERNEL__
-/*
- * This is our internal structure for each serial port's state.
- *
- * Many fields are paralleled by the structure used by the serial_struct
- * structure.
- *
- * For definitions of the flags field, see tty.h
- */
-
-#include <linux/termios.h>
-#include <linux/tqueue.h>
-
-/*
- * Counters of the input lines (CTS, DSR, RI, CD) interrupts
- */
-struct async_icount {
- __u32 cts, dsr, rng, dcd, tx, rx;
- __u32 frame, parity, overrun, brk;
- __u32 buf_overrun;
-};
-
-struct serial_state {
- int magic;
- int baud_base;
- int port;
- int irq;
- int flags;
- int hub6;
- int type;
- int line;
- int xmit_fifo_size;
- int custom_divisor;
- int count;
- unsigned short close_delay;
- unsigned short closing_wait; /* time to wait before closing */
- struct async_icount icount;
- struct termios normal_termios;
- struct termios callout_termios;
- struct async_struct *info;
-};
-
-struct async_struct {
- int magic;
- int port;
- int hub6;
- int flags;
- int xmit_fifo_size;
- struct serial_state *state;
- struct tty_struct *tty;
- int read_status_mask;
- int ignore_status_mask;
- int timeout;
- int quot;
- int x_char; /* xon/xoff character */
- int close_delay;
- unsigned short closing_wait;
- unsigned short closing_wait2;
- int IER; /* Interrupt Enable Register */
- int MCR; /* Modem control register */
- unsigned long event;
- unsigned long last_active;
- int line;
- int blocked_open; /* # of blocked opens */
- long session; /* Session of opening process */
- long pgrp; /* pgrp of opening process */
- unsigned char *xmit_buf;
- int xmit_head;
- int xmit_tail;
- int xmit_cnt;
- struct tq_struct tqueue;
- struct tq_struct tqueue_hangup;
- struct wait_queue *open_wait;
- struct wait_queue *close_wait;
- struct wait_queue *delta_msr_wait;
- struct async_struct *next_port; /* For the linked list */
- struct async_struct *prev_port;
-};
-
-#define SERIAL_MAGIC 0x5301
-#define SSTATE_MAGIC 0x5302
-
-/*
- * The size of the serial xmit buffer is 1 page, or 4096 bytes
- */
-#define SERIAL_XMIT_SIZE 4096
-
-/*
- * Events are used to schedule things to happen at timer-interrupt
- * time, instead of at rs interrupt time.
- */
-#define RS_EVENT_WRITE_WAKEUP 0
-
-/*
- * Multiport serial configuration structure --- internal structure
- */
-struct rs_multiport_struct {
- int port1;
- unsigned char mask1, match1;
- int port2;
- unsigned char mask2, match2;
- int port3;
- unsigned char mask3, match3;
- int port4;
- unsigned char mask4, match4;
- int port_monitor;
-};
-
/* Export to allow PCMCIA to use this - Dave Hinds */
extern int register_serial(struct serial_struct *req);
extern void unregister_serial(int line);
--- /dev/null Mon Dec 31 23:00:00 1979
+++ include/linux/serialP.h Wed Nov 19 02:29:16 1997
@@ -0,0 +1,119 @@
+/*
+ * Private header file for the (dumb) serial driver
+ *
+ * Copyright (C) 1997 by Theodore Ts'o.
+ *
+ * Redistribution of this file is permitted under the terms of the GNU
+ * Public License (GPL)
+ */
+
+#ifndef _LINUX_SERIALP_H
+#define _LINUX_SERIALP_H
+
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct async_icount {
+ __u32 cts, dsr, rng, dcd, tx, rx;
+ __u32 frame, parity, overrun, brk;
+ __u32 buf_overrun;
+};
+
+struct serial_state {
+ int magic;
+ int baud_base;
+ int port;
+ int irq;
+ int flags;
+ int hub6;
+ int type;
+ int line;
+ int xmit_fifo_size;
+ int custom_divisor;
+ int count;
+ unsigned short close_delay;
+ unsigned short closing_wait; /* time to wait before closing */
+ struct async_icount icount;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct async_struct *info;
+};
+
+struct async_struct {
+ int magic;
+ int port;
+ int hub6;
+ int flags;
+ int xmit_fifo_size;
+ struct serial_state *state;
+ struct tty_struct *tty;
+ int read_status_mask;
+ int ignore_status_mask;
+ int timeout;
+ int quot;
+ int x_char; /* xon/xoff character */
+ int close_delay;
+ unsigned short closing_wait;
+ unsigned short closing_wait2;
+ int IER; /* Interrupt Enable Register */
+ int MCR; /* Modem control register */
+ unsigned long event;
+ unsigned long last_active;
+ int line;
+ int blocked_open; /* # of blocked opens */
+ long session; /* Session of opening process */
+ long pgrp; /* pgrp of opening process */
+ unsigned char *xmit_buf;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct tq_struct tqueue;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct wait_queue *delta_msr_wait;
+ struct async_struct *next_port; /* For the linked list */
+ struct async_struct *prev_port;
+};
+
+#define SERIAL_MAGIC 0x5301
+#define SSTATE_MAGIC 0x5302
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP 0
+
+/*
+ * Multiport serial configuration structure --- internal structure
+ */
+struct rs_multiport_struct {
+ int port1;
+ unsigned char mask1, match1;
+ int port2;
+ unsigned char mask2, match2;
+ int port3;
+ unsigned char mask3, match3;
+ int port4;
+ unsigned char mask4, match4;
+ int port_monitor;
+};
+
+#endif /* _LINUX_SERIAL_H */