Patches to the serial driver

Theodre Ts'o (tytso@mit.edu)
Sun, 3 Nov 1996 00:40:48 -0500


Hi Linus,
Enclosed please find patches to the serial driver; could you
please apply them to the 2.1 tree? They add a number of enhancements,
including support for the StarTech 16650 and TI 16750 UART's, as well as
support for the faster boards which can support 230kbps and 460kbps.

I've also made some changes to reduce the amount of memory
required by the serial driver in storing the port configuration
information. Also, I've looked at the POSIX changes which the Unifix
people proposed, and found some better ways to address their issues.
The resulting serial driver should be much closer in meeting the POSIX
conformance tests.

Finally, there's patches to better support the ntp/hardpps
features which Ulrich Windl has been working on.

WARNING: It looks like StarTech has done it again. As of the
June, 1996 specification sheets, it looks like they've made a new
revision of the 16650, which is labelled the 16650A. The specification
sheet, as per usual, is filled with internally inconsistent and
contradictory data. One interpretation (where you assume one part of
the document is true and ignore the parts which contradict it) would
tend to indicate that the 16650A once again breaks compatibility with
the 16550 UART. For this reason, I cannot recommend that anyone use a
StarTech 16550A UART, and I cannot support this UART chip at this time.
Given what a horrible job StarTech has done in documenting its chips, at
this point I can not really recommend that people give them their
business.

- Ted

P.S. Thanks to Robert Forsman (thoth@purplefrog.com) for sending me a
card capable of doing 230kbps, so I could test the new high-speed serial
functionality.

Patch generated: on Sun Nov 3 00:19:34 EST 1996 by tytso@rsts-11.mit.edu
against Linux version 2.1.7

===================================================================
RCS file: drivers/char/RCS/ChangeLog,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/ChangeLog
--- drivers/char/ChangeLog 1996/11/03 01:42:30 1.1
+++ drivers/char/ChangeLog 1996/11/03 01:50:32
@@ -1,6 +1,28 @@
+Sat Nov 2 20:43:10 1996 Theodore Ts'o <tytso@rsts-11.mit.edu>
+
+ * serial.c: Wholesale changes. Added support for the Startech
+ 16650 and 16650V2 chips. (WARNING: the new startech
+ 16650A may or may not work!) Added support for the
+ TI16750 (not yet tested). Split async_struct into a
+ transient part (async_struct) and a permanent part
+ (serial_state) which contains the configuration
+ information for the ports. Added new driver routines
+ wait_until_sent() and send_xchar() to help with POSIX
+ compliance. Added support for radio clocks which waggle
+ the carrier detect line (CONFIG_HARD_PPS).
+
+ * tty_ioctl.c (tty_wait_until_sent): Added call to new driver
+ function tty->driver.wait_until_sent(), which returns when
+ the tty's device xmit buffers are drained. Needed for
+ full POSIX compliance.
+
+ (send_prio_char): New function, called by the ioctl's
+ TCIOFF and TCION; uses the new driver call send_xchar(),
+ which will send the XON or XOFF character at high priority
+ (and even if tty output is stopped).
+
Wed Jun 5 18:52:04 1996 Theodore Ts'o <tytso@rsts-11.mit.edu>

- * tty_io.c (do_tty_hangup):
* pty.c (pty_close): When closing a pty, make sure packet mode is
cleared.

===================================================================
RCS file: drivers/char/RCS/serial.c,v
retrieving revision 1.1
diff -u -r1.1 drivers/char/serial.c
--- drivers/char/serial.c 1996/11/02 01:12:35 1.1
+++ drivers/char/serial.c 1996/11/02 04:57:01
@@ -42,6 +42,7 @@
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
+#include <linux/malloc.h>

#include <asm/system.h>
#include <asm/io.h>
@@ -49,7 +50,7 @@
#include <asm/bitops.h>

static char *serial_name = "Serial driver";
-static char *serial_version = "4.13";
+static char *serial_version = "4.20";

DECLARE_TASK_QUEUE(tq_serial);

@@ -109,8 +110,27 @@
static volatile int rs_triggered;
static int rs_wild_int_mask;

-static void autoconfig(struct async_struct * info);
+static void autoconfig(struct serial_state * info);
static void change_speed(struct async_struct *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * Here we define the default xmit fifo size used for each type of
+ * UART
+ */
+static struct serial_uart_config uart_config[] = {
+ { "unknown", 1, 0 },
+ { "8250", 1, 0 },
+ { "16450", 1, 0 },
+ { "16550", 1, 0 },
+ { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+ { "cirrus", 1, 0 },
+ { "ST16650", 1, UART_CLEAR_FIFO |UART_STARTECH },
+ { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
+ UART_STARTECH },
+ { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
+ { 0, 0}
+};

/*
* This assumes you have a 1.8432 MHz clock for your UART.
@@ -146,7 +166,7 @@

#define C_P(card,port) (((card)<<6|(port)<<3) + 1)

-struct async_struct rs_table[] = {
+struct serial_state rs_table[] = {
/* UART CLK PORT IRQ FLAGS */
{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */
{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */
@@ -204,7 +224,7 @@
#endif
};

-#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))

static struct tty_struct *serial_table[NR_PORTS];
static struct termios *serial_termios[NR_PORTS];
@@ -252,7 +272,7 @@
*/
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 0 };
+ 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };

static inline unsigned int serial_in(struct async_struct *info, int offset)
{
@@ -400,11 +420,14 @@

do {
ch = serial_inp(info, UART_RX);
+ if (*status & UART_LSR_BI)
+ *status &= ~(UART_LSR_FE | UART_LSR_PE);
if (*status & info->ignore_status_mask) {
if (++ignored > 100)
break;
goto ignore_char;
}
+ *status &= info->read_status_mask;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
tty->flip.count++;
@@ -425,7 +448,7 @@
*tty->flip.flag_buf_ptr++ = 0;
*tty->flip.char_buf_ptr++ = ch;
ignore_char:
- *status = serial_inp(info, UART_LSR) & info->read_status_mask;
+ *status = serial_inp(info, UART_LSR);
} while (*status & UART_LSR_DR);
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
#ifdef SERIAL_DEBUG_INTR
@@ -477,19 +500,27 @@
static _INLINE_ void check_modem_status(struct async_struct *info)
{
int status;
+ struct async_icount *icount;

status = serial_in(info, UART_MSR);

if (status & UART_MSR_ANY_DELTA) {
+ icount = &info->state->icount;
/* update input line counters */
if (status & UART_MSR_TERI)
- info->icount.rng++;
+ icount->rng++;
if (status & UART_MSR_DDSR)
- info->icount.dsr++;
- if (status & UART_MSR_DDCD)
- info->icount.dcd++;
+ icount->dsr++;
+ if (status & UART_MSR_DDCD) {
+ icount->dcd++;
+#ifdef CONFIG_HARD_PPS
+ if ((info->flags & ASYNC_HARDPPS_CD) &&
+ (status & UART_MSR_DCD))
+ hardpps();
+#endif
+ }
if (status & UART_MSR_DCTS)
- info->icount.cts++;
+ icount->cts++;
wake_up_interruptible(&info->delta_msr_wait);
}

@@ -569,7 +600,7 @@

info->last_active = jiffies;

- status = serial_inp(info, UART_LSR) & info->read_status_mask;
+ status = serial_inp(info, UART_LSR);
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
@@ -594,7 +625,8 @@
} while (end_mark != info);
if (multi->port_monitor)
printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
- info->irq, first_multi, inb(multi->port_monitor));
+ info->state->irq, first_multi,
+ inb(multi->port_monitor));
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
@@ -624,7 +656,7 @@
first_multi = inb(multi->port_monitor);

do {
- status = serial_inp(info, UART_LSR) & info->read_status_mask;
+ status = serial_inp(info, UART_LSR);
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
@@ -643,7 +675,8 @@
info->last_active = jiffies;
if (multi->port_monitor)
printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
- info->irq, first_multi, inb(multi->port_monitor));
+ info->state->irq, first_multi,
+ inb(multi->port_monitor));
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
@@ -683,7 +716,7 @@

info->last_active = jiffies;

- status = serial_inp(info, UART_LSR) & info->read_status_mask;
+ status = serial_inp(info, UART_LSR);
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
@@ -707,7 +740,7 @@
}
if (multi->port_monitor)
printk("rs port monitor irq %d: 0x%x, 0x%x\n",
- info->irq, first_multi,
+ info->state->irq, first_multi,
inb(multi->port_monitor));
if ((inb(multi->port1) & multi->mask1) != multi->match1)
continue;
@@ -910,13 +943,13 @@
unsigned long flags;
int retval;
void (*handler)(int, void *, struct pt_regs *);
+ struct serial_state *state= info->state;
unsigned long page;

page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;

-
save_flags(flags); cli();

if (info->flags & ASYNC_INITIALIZED) {
@@ -925,7 +958,7 @@
return 0;
}

- if (!info->port || !info->type) {
+ if (!state->port || !state->type) {
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
free_page(page);
@@ -938,23 +971,30 @@
info->xmit_buf = (unsigned char *) page;

#ifdef SERIAL_DEBUG_OPEN
- printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+ printk("starting up ttys%d (irq %d)...", info->line, state->irq);
#endif

+ if (uart_config[info->state->type].flags & UART_STARTECH) {
+ /* Wake up UART */
+ serial_outp(info, UART_LCR, 0xBF);
+ serial_outp(info, UART_EFR, UART_EFR_ECB);
+ serial_outp(info, UART_IER, 0);
+ serial_outp(info, UART_EFR, 0);
+ serial_outp(info, UART_LCR, 0);
+ }
+
+ if (info->state->type == PORT_16750) {
+ /* Wake up UART */
+ serial_outp(info, UART_IER, 0);
+ }
+
/*
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
- if (info->type == PORT_16650) {
- serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
- UART_FCR_CLEAR_XMIT));
- info->xmit_fifo_size = 1; /* disabled for now */
- } else if (info->type == PORT_16550A) {
+ if (uart_config[state->type].flags & UART_CLEAR_FIFO)
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
- info->xmit_fifo_size = 16;
- } else
- info->xmit_fifo_size = 1;

/*
* At this point there's no way the LSR could still be 0xFF;
@@ -974,18 +1014,18 @@
/*
* Allocate the IRQ if necessary
*/
- if (info->irq && (!IRQ_ports[info->irq] ||
- !IRQ_ports[info->irq]->next_port)) {
- if (IRQ_ports[info->irq]) {
- free_irq(info->irq, NULL);
- if (rs_multiport[info->irq].port1)
+ if (state->irq && (!IRQ_ports[state->irq] ||
+ !IRQ_ports[state->irq]->next_port)) {
+ if (IRQ_ports[state->irq]) {
+ free_irq(state->irq, NULL);
+ if (rs_multiport[state->irq].port1)
handler = rs_interrupt_multi;
else
handler = rs_interrupt;
} else
handler = rs_interrupt_single;

- retval = request_irq(info->irq, handler, IRQ_T(info),
+ retval = request_irq(state->irq, handler, IRQ_T(info),
"serial", NULL);
if (retval) {
restore_flags(flags);
@@ -1022,7 +1062,7 @@
info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
#endif
- if (info->irq == 0)
+ if (state->irq == 0)
info->MCR = info->MCR_noint;
serial_outp(info, UART_MCR, info->MCR);

@@ -1055,11 +1095,11 @@
* Insert serial port into IRQ chain.
*/
info->prev_port = 0;
- info->next_port = IRQ_ports[info->irq];
+ info->next_port = IRQ_ports[state->irq];
if (info->next_port)
info->next_port->prev_port = info;
- IRQ_ports[info->irq] = info;
- figure_IRQ_timeout(info->irq);
+ IRQ_ports[state->irq] = info;
+ figure_IRQ_timeout(state->irq);

/*
* Set up serial timers...
@@ -1084,14 +1124,17 @@
static void shutdown(struct async_struct * info)
{
unsigned long flags;
+ struct serial_state *state;
int retval;

if (!(info->flags & ASYNC_INITIALIZED))
return;

+ state = info->state;
+
#ifdef SERIAL_DEBUG_OPEN
printk("Shutting down serial port %d (irq %d)....", info->line,
- info->irq);
+ state->irq);
#endif

save_flags(flags); cli(); /* Disable interrupts */
@@ -1110,24 +1153,24 @@
if (info->prev_port)
info->prev_port->next_port = info->next_port;
else
- IRQ_ports[info->irq] = info->next_port;
- figure_IRQ_timeout(info->irq);
+ IRQ_ports[state->irq] = info->next_port;
+ figure_IRQ_timeout(state->irq);

/*
* Free the IRQ, if necessary
*/
- if (info->irq && (!IRQ_ports[info->irq] ||
- !IRQ_ports[info->irq]->next_port)) {
- if (IRQ_ports[info->irq]) {
- free_irq(info->irq, NULL);
- retval = request_irq(info->irq, rs_interrupt_single,
+ if (state->irq && (!IRQ_ports[state->irq] ||
+ !IRQ_ports[state->irq]->next_port)) {
+ if (IRQ_ports[state->irq]) {
+ free_irq(state->irq, NULL);
+ retval = request_irq(state->irq, rs_interrupt_single,
IRQ_T(info), "serial", NULL);

if (retval)
printk("serial shutdown: request_irq: error %d"
" Couldn't reacquire IRQ.\n", retval);
} else
- free_irq(info->irq, NULL);
+ free_irq(state->irq, NULL);
}

if (info->xmit_buf) {
@@ -1155,7 +1198,18 @@

if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
-
+
+ if (uart_config[info->state->type].flags & UART_STARTECH) {
+ /* Arrange to enter sleep mode */
+ serial_outp(info, UART_LCR, 0xBF);
+ serial_outp(info, UART_EFR, UART_EFR_ECB);
+ serial_outp(info, UART_IER, UART_IERX_SLEEP);
+ serial_outp(info, UART_LCR, 0);
+ }
+ if (info->state->type == PORT_16750) {
+ /* Arrange to enter sleep mode */
+ serial_outp(info, UART_IER, UART_IERX_SLEEP);
+ }
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
@@ -1167,8 +1221,8 @@
static void change_speed(struct async_struct *info)
{
unsigned short port;
- int quot = 0;
- unsigned cflag,cval,fcr;
+ int quot = 0, baud_base;
+ unsigned cflag, cval, fcr = 0;
int i;

if (!info->tty || !info->tty->termios)
@@ -1179,7 +1233,7 @@
i = cflag & CBAUD;
if (i & CBAUDEX) {
i &= ~CBAUDEX;
- if (i < 1 || i > 2)
+ if (i < 1 || i > 4)
info->tty->termios->c_cflag &= ~CBAUDEX;
else
i += 15;
@@ -1189,29 +1243,28 @@
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;
+ quot = info->state->custom_divisor;
}
+ baud_base = info->state->baud_base;
if (quot) {
info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
- info->baud_base) + 2;
+ baud_base) + 2;
} else if (baud_table[i] == 134) {
- quot = (2*info->baud_base / 269);
+ quot = (2*baud_base / 269);
info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
} else if (baud_table[i]) {
- quot = info->baud_base / baud_table[i];
+ quot = baud_base / baud_table[i];
info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
} else {
quot = 0;
info->timeout = 0;
}
- if (quot) {
- info->MCR |= UART_MCR_DTR;
- info->MCR_noint |= UART_MCR_DTR;
- cli();
- serial_out(info, UART_MCR, info->MCR);
- sti();
- } else {
+ if (!quot) {
info->MCR &= ~UART_MCR_DTR;
info->MCR_noint &= ~UART_MCR_DTR;
cli();
@@ -1234,28 +1287,19 @@
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
- if (info->type == PORT_16550A) {
- if ((info->baud_base / quot) < 2400)
+ if (uart_config[info->state->type].flags & UART_USE_FIFO) {
+ if ((info->state->baud_base / quot) < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
- } else if (info->type == PORT_16650) {
- /*
- * On the 16650, we disable the FIFOs altogether
- * because of a design bug in how the implement
- * things. We could support it by completely changing
- * how we handle the interrupt driver, but not today....
- *
- * N.B. Because there's no way to set a FIFO trigger
- * at 1 char, we'd probably disable at speed below
- * 2400 baud anyway...
- */
- fcr = 0;
- } else
- fcr = 0;
+ }
+ if (info->state->type == PORT_16750)
+ fcr |= UART_FCR7_64BYTE;

/* CTS flow control flag and modem status interrupts */
info->IER &= ~UART_IER_MSI;
+ if (info->flags & ASYNC_HARDPPS_CD)
+ info->IER |= UART_IER_MSI;
if (cflag & CRTSCTS) {
info->flags |= ASYNC_CTS_FLOW;
info->IER |= UART_IER_MSI;
@@ -1281,31 +1325,28 @@
info->read_status_mask |= UART_LSR_BI;

info->ignore_status_mask = 0;
-#if 0
- /* This should be safe, but for some broken bits of hardware... */
- if (I_IGNPAR(info->tty)) {
+ if (I_IGNPAR(info->tty))
info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
- }
-#endif
if (I_IGNBRK(info->tty)) {
info->ignore_status_mask |= UART_LSR_BI;
- info->read_status_mask |= UART_LSR_BI;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
- if (I_IGNPAR(info->tty)) {
- info->ignore_status_mask |= UART_LSR_OE | \
- UART_LSR_PE | UART_LSR_FE;
- info->read_status_mask |= UART_LSR_OE | \
- UART_LSR_PE | UART_LSR_FE;
- }
+ if (I_IGNPAR(info->tty))
+ info->ignore_status_mask |= UART_LSR_OE;
}
cli();
+ if (uart_config[info->state->type].flags & UART_STARTECH) {
+ serial_outp(info, UART_LCR, 0xBF);
+ serial_outp(info, UART_EFR,
+ (cflag & CRTSCTS) ? UART_EFR_CTS : 0);
+ }
serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
+ if (info->state->type == PORT_16750)
+ serial_outp(info, UART_FCR, fcr); /* set fcr */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
serial_outp(info, UART_FCR, fcr); /* set fcr */
sti();
@@ -1438,6 +1479,25 @@
}

/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct async_struct *info = (struct async_struct *)tty->driver_data;
+
+ if (serial_paranoia_check(info, tty->device, "rs_send_char"))
+ return;
+
+ info->x_char = ch;
+ if (ch) {
+ /* Make sure transmit interrupts are on */
+ info->IER |= UART_IER_THRI;
+ serial_out(info, UART_IER, info->IER);
+ }
+}
+
+/*
* ------------------------------------------------------------
* rs_throttle()
*
@@ -1459,10 +1519,12 @@
return;

if (I_IXOFF(tty))
- info->x_char = STOP_CHAR(tty);
+ rs_send_xchar(tty, STOP_CHAR(tty));

- info->MCR &= ~UART_MCR_RTS;
- info->MCR_noint &= ~UART_MCR_RTS;
+ if (tty->termios->c_cflag & CRTSCTS) {
+ info->MCR &= ~UART_MCR_RTS;
+ info->MCR_noint &= ~UART_MCR_RTS;
+ }
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
@@ -1485,10 +1547,12 @@
if (info->x_char)
info->x_char = 0;
else
- info->x_char = START_CHAR(tty);
+ rs_send_xchar(tty, START_CHAR(tty));
+ }
+ if (tty->termios->c_cflag & CRTSCTS) {
+ info->MCR |= UART_MCR_RTS;
+ info->MCR_noint |= UART_MCR_RTS;
}
- info->MCR |= UART_MCR_RTS;
- info->MCR_noint |= UART_MCR_RTS;
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
@@ -1504,20 +1568,22 @@
struct serial_struct * retinfo)
{
struct serial_struct tmp;
-
+ struct serial_state *state = info->state;
+
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
- tmp.type = info->type;
- tmp.line = info->line;
- tmp.port = info->port;
- tmp.irq = info->irq;
- tmp.flags = info->flags;
- tmp.baud_base = info->baud_base;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- tmp.hub6 = info->hub6;
+ tmp.type = state->type;
+ tmp.line = state->line;
+ tmp.port = state->port;
+ tmp.irq = state->irq;
+ tmp.flags = state->flags;
+ tmp.xmit_fifo_size = state->xmit_fifo_size;
+ tmp.baud_base = state->baud_base;
+ tmp.close_delay = state->close_delay;
+ tmp.closing_wait = state->closing_wait;
+ tmp.custom_divisor = state->custom_divisor;
+ tmp.hub6 = state->hub6;
copy_to_user(retinfo,&tmp,sizeof(*retinfo));
return 0;
}
@@ -1526,29 +1592,32 @@
struct serial_struct * new_info)
{
struct serial_struct new_serial;
- struct async_struct old_info;
+ struct serial_state old_state, *state;
unsigned int i,change_irq,change_port;
int retval = 0;

if (!new_info)
return -EFAULT;
copy_from_user(&new_serial,new_info,sizeof(new_serial));
- old_info = *info;
-
- change_irq = new_serial.irq != info->irq;
- change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
-
+ state = info->state;
+ old_state = *state;
+
+ change_irq = new_serial.irq != state->irq;
+ change_port = (new_serial.port != state->port) ||
+ (new_serial.hub6 != state->hub6);
+
if (!suser()) {
if (change_irq || change_port ||
- (new_serial.baud_base != info->baud_base) ||
- (new_serial.type != info->type) ||
- (new_serial.close_delay != info->close_delay) ||
+ (new_serial.baud_base != state->baud_base) ||
+ (new_serial.type != state->type) ||
+ (new_serial.close_delay != state->close_delay) ||
+ (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->flags & ~ASYNC_USR_MASK)))
+ (state->flags & ~ASYNC_USR_MASK)))
return -EPERM;
- info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+ state->flags = ((state->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
+ state->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}

@@ -1563,13 +1632,13 @@
/* Make sure address is not already in use */
if (new_serial.type) {
for (i = 0 ; i < NR_PORTS; i++)
- if ((info != &rs_table[i]) &&
+ if ((state != &rs_table[i]) &&
(rs_table[i].port == new_serial.port) &&
rs_table[i].type)
return -EADDRINUSE;
}

- if ((change_port || change_irq) && (info->count > 1))
+ if ((change_port || change_irq) && (state->count > 1))
return -EBUSY;

/*
@@ -1577,36 +1646,43 @@
* At this point, we start making changes.....
*/

- info->baud_base = new_serial.baud_base;
- info->flags = ((info->flags & ~ASYNC_FLAGS) |
+ state->baud_base = new_serial.baud_base;
+ state->flags = ((state->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->type = new_serial.type;
- info->close_delay = new_serial.close_delay * HZ/100;
- info->closing_wait = new_serial.closing_wait * HZ/100;
+ info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
+ (info->flags & ASYNC_INTERNAL_FLAGS));
+ state->custom_divisor = new_serial.custom_divisor;
+ state->type = new_serial.type;
+ state->close_delay = new_serial.close_delay * HZ/100;
+ state->closing_wait = new_serial.closing_wait * HZ/100;
+ info->xmit_fifo_size = state->xmit_fifo_size =
+ new_serial.xmit_fifo_size;

- release_region(info->port,8);
+ release_region(state->port,8);
if (change_port || change_irq) {
/*
* We need to shutdown the serial port at the old
* port/irq combination.
*/
shutdown(info);
- info->irq = new_serial.irq;
- info->port = new_serial.port;
- info->hub6 = new_serial.hub6;
+ state->irq = new_serial.irq;
+ info->port = state->port = new_serial.port;
+ info->hub6 = state->hub6 = new_serial.hub6;
}
- if(info->type != PORT_UNKNOWN)
- request_region(info->port,8,"serial(set)");
+ if (state->type != PORT_UNKNOWN)
+ request_region(state->port,8,"serial(set)");


check_and_exit:
- if (!info->port || !info->type)
+ if (!state->port || !state->type)
return 0;
- if (info->flags & ASYNC_INITIALIZED) {
- if (((old_info.flags & ASYNC_SPD_MASK) !=
- (info->flags & ASYNC_SPD_MASK)) ||
- (old_info.custom_divisor != info->custom_divisor))
+ if (state->type != old_state.type)
+ state->xmit_fifo_size =
+ uart_config[state->type].dfl_xmit_fifo_size;
+ if (state->flags & ASYNC_INITIALIZED) {
+ if (((old_state.flags & ASYNC_SPD_MASK) !=
+ (state->flags & ASYNC_SPD_MASK)) ||
+ (old_state.custom_divisor != state->custom_divisor))
change_speed(info);
} else
retval = startup(info);
@@ -1713,13 +1789,13 @@
if (!suser())
return -EPERM;

- if (info->count > 1)
+ if (info->state->count > 1)
return -EBUSY;

shutdown(info);

cli();
- autoconfig(info);
+ autoconfig(info->state);
sti();

retval = startup(info);
@@ -1795,7 +1871,7 @@
struct serial_multiport_struct ret;
struct rs_multiport_struct *multi;

- multi = &rs_multiport[info->irq];
+ multi = &rs_multiport[info->state->irq];

ret.port_monitor = multi->port_monitor;

@@ -1815,7 +1891,7 @@
ret.mask4 = multi->mask4;
ret.match4 = multi->match4;

- ret.irq = info->irq;
+ ret.irq = info->state->irq;

copy_to_user(retinfo,&ret,sizeof(*retinfo));
return 0;
@@ -1827,6 +1903,7 @@
{
struct serial_multiport_struct new_multi;
struct rs_multiport_struct *multi;
+ struct serial_state *state;
int was_multi, now_multi;
int retval;
void (*handler)(int, void *, struct pt_regs *);
@@ -1835,14 +1912,16 @@
return -EPERM;
if (!in_multi)
return -EFAULT;
+ state = info->state;
+
copy_from_user(&new_multi, in_multi,
sizeof(struct serial_multiport_struct));

- if (new_multi.irq != info->irq || info->irq == 0 ||
- !IRQ_ports[info->irq])
+ if (new_multi.irq != state->irq || state->irq == 0 ||
+ !IRQ_ports[state->irq])
return -EINVAL;

- multi = &rs_multiport[info->irq];
+ multi = &rs_multiport[state->irq];
was_multi = (multi->port1 != 0);

multi->port_monitor = new_multi.port_monitor;
@@ -1881,15 +1960,15 @@

now_multi = (multi->port1 != 0);

- if (IRQ_ports[info->irq]->next_port &&
+ if (IRQ_ports[state->irq]->next_port &&
(was_multi != now_multi)) {
- free_irq(info->irq, NULL);
+ free_irq(state->irq, NULL);
if (now_multi)
handler = rs_interrupt_multi;
else
handler = rs_interrupt;

- retval = request_irq(info->irq, handler, IRQ_T(info),
+ retval = request_irq(state->irq, handler, IRQ_T(info),
"serial", NULL);
if (retval) {
printk("Couldn't reallocate serial interrupt "
@@ -2036,7 +2115,8 @@
*/
case TIOCMIWAIT:
cli();
- cprev = info->icount; /* note the counters on entry */
+ /* note the counters on entry */
+ cprev = info->state->icount;
sti();
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
@@ -2044,7 +2124,7 @@
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
cli();
- cnow = info->icount; /* atomic copy */
+ cnow = info->state->icount; /* atomic copy */
sti();
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
@@ -2071,7 +2151,7 @@
if (error)
return error;
cli();
- cnow = info->icount;
+ cnow = info->state->icount;
sti();
p_cuser = (struct serial_icounter_struct *) arg;
put_user(cnow.cts, &p_cuser->cts);
@@ -2097,6 +2177,14 @@

change_speed(info);

+ if (!(old_termios->c_cflag & CBAUD) &&
+ (tty->termios->c_cflag & CBAUD)) {
+ info->MCR |= UART_MCR_DTR;
+ info->MCR_noint |= UART_MCR_DTR;
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+ }
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
@@ -2129,11 +2217,13 @@
static void rs_close(struct tty_struct *tty, struct file * filp)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct serial_state *state;
unsigned long flags;
- unsigned long timeout;

if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
return;
+
+ state = info->state;

save_flags(flags); cli();

@@ -2145,26 +2235,26 @@
}

#ifdef SERIAL_DEBUG_OPEN
- printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+ printk("rs_close ttys%d, count = %d\n", info->line, state->count);
#endif
- if ((tty->count == 1) && (info->count != 1)) {
+ if ((tty->count == 1) && (state->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
+ * structure will be freed. state->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk("rs_close: bad serial port count; tty->count is 1, "
- "info->count is %d\n", info->count);
- info->count = 1;
+ "state->count is %d\n", state->count);
+ state->count = 1;
}
- if (--info->count < 0) {
+ if (--state->count < 0) {
printk("rs_close: bad serial port count for ttys%d: %d\n",
- info->line, info->count);
- info->count = 0;
+ info->line, state->count);
+ state->count = 0;
}
- if (info->count) {
+ if (state->count) {
DBG_CNT("before DEC-2");
MOD_DEC_USE_COUNT;
restore_flags(flags);
@@ -2176,9 +2266,9 @@
* separate termios for callout and dialin.
*/
if (info->flags & ASYNC_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
+ info->state->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
- info->callout_termios = *tty->termios;
+ info->state->callout_termios = *tty->termios;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
@@ -2201,14 +2291,7 @@
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
- timeout = jiffies+HZ;
- while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + info->timeout;
- schedule();
- if (jiffies > timeout)
- break;
- }
+ rs_wait_until_sent(tty, HZ);
}
shutdown(info);
if (tty->driver.flush_buffer)
@@ -2234,19 +2317,49 @@
}

/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ struct async_struct * info = (struct async_struct *)tty->driver_data;
+ unsigned long orig_jiffies;
+
+ if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
+ return;
+
+ orig_jiffies = jiffies;
+ current->state = TASK_INTERRUPTIBLE;
+ current->counter = 0; /* make us low-priority */
+ while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
+ current->timeout = jiffies + info->timeout;
+ schedule();
+ if (current->signal & ~current->blocked)
+ break;
+ if (timeout && ((orig_jiffies + timeout) > jiffies))
+ break;
+ if (jiffies > timeout)
+ break;
+ }
+ current->state = TASK_RUNNING;
+}
+
+/*
* rs_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
void rs_hangup(struct tty_struct *tty)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
+ struct serial_state *state = info->state;

if (serial_paranoia_check(info, tty->device, "rs_hangup"))
return;
+
+ state = info->state;

rs_flush_buffer(tty);
shutdown(info);
info->event = 0;
- info->count = 0;
+ state->count = 0;
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = 0;
wake_up_interruptible(&info->open_wait);
@@ -2261,6 +2374,7 @@
struct async_struct *info)
{
struct wait_queue wait = { current, NULL };
+ struct serial_state *state = info->state;
int retval;
int do_clocal = 0;

@@ -2314,7 +2428,7 @@
}

if (info->flags & ASYNC_CALLOUT_ACTIVE) {
- if (info->normal_termios.c_cflag & CLOCAL)
+ if (state->normal_termios.c_cflag & CLOCAL)
do_clocal = 1;
} else {
if (tty->termios->c_cflag & CLOCAL)
@@ -2324,7 +2438,7 @@
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
- * this loop, info->count is dropped by one, so that
+ * this loop, state->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
@@ -2332,11 +2446,11 @@
add_wait_queue(&info->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: ttys%d, count = %d\n",
- info->line, info->count);
+ state->line, state->count);
#endif
cli();
if (!tty_hung_up_p(filp))
- info->count--;
+ state->count--;
sti();
info->blocked_open++;
while (1) {
@@ -2370,24 +2484,60 @@
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: ttys%d, count = %d\n",
- info->line, info->count);
+ info->line, state->count);
#endif
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp))
- info->count++;
+ state->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttys%d, count = %d\n",
- info->line, info->count);
+ info->line, state->count);
#endif
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
-}
+}
+
+int get_async_struct(int line, struct async_struct **ret_info)
+{
+ struct async_struct *info;
+ struct serial_state *sstate;
+
+ sstate = rs_table + line;
+ sstate->count++;
+ if (sstate->info) {
+ *ret_info = sstate->info;
+ return 0;
+ }
+ info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+ if (!info) {
+ sstate->count--;
+ return -ENOMEM;
+ }
+ memset(info, 0, sizeof(struct async_struct));
+ info->magic = SERIAL_MAGIC;
+ info->port = sstate->port;
+ info->flags = sstate->flags;
+ info->xmit_fifo_size = sstate->xmit_fifo_size;
+ 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));
+ *ret_info = sstate->info;
+ return 0;
+ }
+ *ret_info = sstate->info = info;
+ return 0;
+}

/*
* This routine is called whenever a serial port is opened. It
@@ -2404,15 +2554,16 @@
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
- info = rs_table + line;
+ retval = get_async_struct(line, &info);
+ if (retval)
+ return retval;
if (serial_paranoia_check(info, tty->device, "rs_open"))
return -ENODEV;

#ifdef SERIAL_DEBUG_OPEN
printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
- info->count);
+ info->state->count);
#endif
- info->count++;
tty->driver_data = info;
info->tty = tty;

@@ -2443,11 +2594,12 @@
return retval;
}

- if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+ if ((info->state->count == 1) &&
+ (info->flags & ASYNC_SPLIT_TERMIOS)) {
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
- *tty->termios = info->normal_termios;
+ *tty->termios = info->state->normal_termios;
else
- *tty->termios = info->callout_termios;
+ *tty->termios = info->state->callout_termios;
change_speed(info);
}

@@ -2587,16 +2739,18 @@
* whether or not this UART is a 16550A or not, since this will
* determine whether or not we can use its FIFO features or not.
*/
-static void autoconfig(struct async_struct * info)
+static void autoconfig(struct serial_state * state)
{
unsigned char status1, status2, scratch, scratch2;
- unsigned port = info->port;
+ struct async_struct *info, scr_info;
unsigned long flags;

- info->type = PORT_UNKNOWN;
+ state->type = PORT_UNKNOWN;

- if (!port)
+ if (!state->port)
return;
+ info = &scr_info; /* This is just for serial_{in,out} */
+ info->port = state->port;

save_flags(flags); cli();

@@ -2628,7 +2782,7 @@
* manufacturer would be stupid enough to design a board
* that conflicts with COM 1-4 --- we hope!
*/
- if (!(info->flags & ASYNC_SKIP_TEST)) {
+ if (!(state->flags & ASYNC_SKIP_TEST)) {
scratch = serial_inp(info, UART_MCR);
serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
scratch2 = serial_inp(info, UART_MSR);
@@ -2646,39 +2800,52 @@
* If the AUTO_IRQ flag is set, try to do the automatic IRQ
* detection.
*/
- if (info->flags & ASYNC_AUTO_IRQ)
- info->irq = do_auto_irq(info);
+ if (state->flags & ASYNC_AUTO_IRQ)
+ state->irq = do_auto_irq(info);

scratch2 = serial_in(info, UART_LCR);
- serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
- serial_outp(info, UART_LCR, scratch2);
+ serial_outp(info, UART_LCR, 0);
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 6;
- info->xmit_fifo_size = 1;
switch (scratch) {
case 0:
- info->type = PORT_16450;
+ state->type = PORT_16450;
break;
case 1:
- info->type = PORT_UNKNOWN;
+ state->type = PORT_UNKNOWN;
break;
case 2:
- info->type = PORT_16550;
+ state->type = PORT_16550;
break;
case 3:
- serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
- if (serial_in(info, UART_EFR) == 0) {
- info->type = PORT_16650;
- info->xmit_fifo_size = 32;
- } else {
- info->type = PORT_16550A;
- info->xmit_fifo_size = 16;
- }
- serial_outp(info, UART_LCR, scratch2);
+ state->type = PORT_16550A;
break;
}
- if (info->type == PORT_16450) {
+ if (state->type == PORT_16550A) {
+ /* Check for Startech UART's */
+ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ if (serial_in(info, UART_EFR) == 0) {
+ state->type = PORT_16650;
+ } else {
+ serial_outp(info, UART_LCR, 0xBF);
+ if (serial_in(info, UART_EFR) == 0)
+ state->type = PORT_16650V2;
+ }
+ }
+ if (state->type == PORT_16550A) {
+ /* Check for TI 16750 */
+ serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
+ serial_outp(info, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+ scratch = serial_in(info, UART_IIR) >> 5;
+ if (scratch == 7)
+ state->type = PORT_16750;
+ serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
+ }
+ serial_outp(info, UART_LCR, scratch2);
+ if (state->type == PORT_16450) {
scratch = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, 0xa5);
status1 = serial_in(info, UART_SCR);
@@ -2687,8 +2854,10 @@
serial_outp(info, UART_SCR, scratch);

if ((status1 != 0xa5) || (status2 != 0x5a))
- info->type = PORT_8250;
+ state->type = PORT_8250;
}
+ state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size;
+
request_region(info->port,8,"serial(auto)");

/*
@@ -2726,7 +2895,7 @@
int rs_init(void)
{
int i;
- struct async_struct * info;
+ struct serial_state * state;

init_bh(SERIAL_BH, do_serial_bh);
timer_table[RS_TIMER].fn = rs_timer;
@@ -2773,10 +2942,12 @@
serial_driver.ioctl = rs_ioctl;
serial_driver.throttle = rs_throttle;
serial_driver.unthrottle = rs_unthrottle;
+ serial_driver.send_xchar = rs_send_xchar;
serial_driver.set_termios = rs_set_termios;
serial_driver.stop = rs_stop;
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
+ serial_driver.wait_until_sent = rs_wait_until_sent;

/*
* The callout device is just like normal device except for
@@ -2792,63 +2963,31 @@
if (tty_register_driver(&callout_driver))
panic("Couldn't register callout driver\n");

- for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
- info->magic = SERIAL_MAGIC;
- info->line = i;
- info->tty = 0;
- info->type = PORT_UNKNOWN;
- info->custom_divisor = 0;
- info->close_delay = 5*HZ/10;
- info->closing_wait = 30*HZ;
- info->x_char = 0;
- info->event = 0;
- info->count = 0;
- info->blocked_open = 0;
- info->tqueue.routine = do_softint;
- info->tqueue.data = info;
- info->tqueue_hangup.routine = do_serial_hangup;
- info->tqueue_hangup.data = info;
- info->callout_termios =callout_driver.init_termios;
- info->normal_termios = serial_driver.init_termios;
- info->open_wait = 0;
- info->close_wait = 0;
- info->delta_msr_wait = 0;
- info->icount.cts = info->icount.dsr =
- info->icount.rng = info->icount.dcd = 0;
- info->next_port = 0;
- info->prev_port = 0;
- if (info->irq == 2)
- info->irq = 9;
- if (info->type == PORT_UNKNOWN) {
- if (!(info->flags & ASYNC_BOOT_AUTOCONF))
+ for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
+ state->magic = SSTATE_MAGIC;
+ state->line = i;
+ state->type = PORT_UNKNOWN;
+ state->custom_divisor = 0;
+ state->close_delay = 5*HZ/10;
+ state->closing_wait = 30*HZ;
+ state->callout_termios = callout_driver.init_termios;
+ state->normal_termios = serial_driver.init_termios;
+ state->icount.cts = state->icount.dsr =
+ state->icount.rng = state->icount.dcd = 0;
+ if (state->irq == 2)
+ state->irq = 9;
+ if (state->type == PORT_UNKNOWN) {
+ if (!(state->flags & ASYNC_BOOT_AUTOCONF))
continue;
- autoconfig(info);
- if (info->type == PORT_UNKNOWN)
+ autoconfig(state);
+ if (state->type == PORT_UNKNOWN)
continue;
}
- printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line,
- (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
- info->port, info->irq);
- switch (info->type) {
- case PORT_8250:
- printk(" is a 8250\n");
- break;
- case PORT_16450:
- printk(" is a 16450\n");
- break;
- case PORT_16550:
- printk(" is a 16550\n");
- break;
- case PORT_16550A:
- printk(" is a 16550A\n");
- break;
- case PORT_16650:
- printk(" is a 16650\n");
- break;
- default:
- printk("\n");
- break;
- }
+ printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d) is a %s\n",
+ state->line,
+ (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
+ state->port, state->irq,
+ uart_config[state->type].name);
}
register_symtab(&serial_syms);
return 0;
@@ -2862,7 +3001,7 @@
{
int i;
unsigned long flags;
- struct async_struct *info;
+ struct serial_state *state;

save_flags(flags);
cli();
@@ -2880,51 +3019,40 @@
restore_flags(flags);
return -1;
}
- info = &rs_table[i];
+ state = &rs_table[i];
if (rs_table[i].count) {
restore_flags(flags);
printk("Couldn't configure serial #%d (port=%d,irq=%d): "
"device already open\n", i, req->port, req->irq);
return -1;
}
- info->irq = req->irq;
- info->port = req->port;
- info->flags = req->flags;
- autoconfig(info);
- if (info->type == PORT_UNKNOWN) {
+ state->irq = req->irq;
+ state->port = req->port;
+ state->flags = req->flags;
+ autoconfig(state);
+ if (state->type == PORT_UNKNOWN) {
restore_flags(flags);
printk("register_serial(): autoconfig failed\n");
return -1;
}
- printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line,
- info->port, info->irq);
- switch (info->type) {
- case PORT_8250:
- printk(" is a 8250\n"); break;
- case PORT_16450:
- printk(" is a 16450\n"); break;
- case PORT_16550:
- printk(" is a 16550\n"); break;
- case PORT_16550A:
- printk(" is a 16550A\n"); break;
- default:
- printk("\n"); break;
- }
+ printk(KERN_INFO "tty%02d at 0x%04x (irq = %d) is a %s\n",
+ state->line, state->port, state->irq,
+ uart_config[state->type].name);
restore_flags(flags);
- return info->line;
+ return state->line;
}

void unregister_serial(int line)
{
unsigned long flags;
- struct async_struct *info = &rs_table[line];
+ struct serial_state *state = &rs_table[line];

save_flags(flags);
cli();
- if (info->tty)
- tty_hangup(info->tty);
- info->type = PORT_UNKNOWN;
- printk(KERN_INFO "tty%02d unloaded\n", info->line);
+ if (state->info && state->info->tty)
+ tty_hangup(state->info->tty);
+ state->type = PORT_UNKNOWN;
+ printk(KERN_INFO "tty%02d unloaded\n", state->line);
restore_flags(flags);
}

===================================================================
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 1996/11/02 01:12:36 1.1
+++ drivers/char/tty_ioctl.c 1996/11/02 04:58:50
@@ -62,11 +62,19 @@
#endif
current->state = TASK_INTERRUPTIBLE;
if (current->signal & ~current->blocked)
- break;
+ goto stop_waiting;
if (!tty->driver.chars_in_buffer(tty))
break;
schedule();
} while (current->timeout);
+ if (tty->driver.wait_until_sent) {
+ if (current->timeout == -1)
+ timeout = 0;
+ else
+ timeout = current->timeout - jiffies;
+ tty->driver.wait_until_sent(tty, timeout);
+ }
+stop_waiting:
current->state = TASK_RUNNING;
remove_wait_queue(&tty->write_wait, &wait);
}
@@ -371,6 +379,24 @@
}
#endif

+/*
+ * Send a high priority character to the tty.
+ */
+void send_prio_char(struct tty_struct *tty, char ch)
+{
+ int was_stopped = tty->stopped;
+
+ if (tty->driver.send_xchar) {
+ tty->driver.send_xchar(tty, ch);
+ return;
+ }
+ if (was_stopped)
+ start_tty(tty);
+ tty->driver.write(tty, 0, &ch, 1);
+ if (was_stopped)
+ stop_tty(tty);
+}
+
int n_tty_ioctl(struct tty_struct * tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
@@ -440,13 +466,11 @@
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
- tty->driver.write(tty, 0,
- &STOP_CHAR(tty), 1);
+ send_prio_char(tty, STOP_CHAR(tty));
break;
case TCION:
if (START_CHAR(tty) != __DISABLED_CHAR)
- tty->driver.write(tty, 0,
- &START_CHAR(tty), 1);
+ send_prio_char(tty, START_CHAR(tty));
break;
default:
return -EINVAL;
@@ -538,6 +562,8 @@
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
if (!tty->driver.ioctl)
return 0;
tty->driver.ioctl(tty, file, cmd, arg);
===================================================================
RCS file: include/linux/RCS/serial.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/serial.h
--- include/linux/serial.h 1996/11/02 01:12:36 1.1
+++ include/linux/serial.h 1996/11/02 01:12:42
@@ -44,7 +44,19 @@
#define PORT_16550A 4
#define PORT_CIRRUS 5
#define PORT_16650 6
-#define PORT_MAX 6
+#define PORT_16650V2 7
+#define PORT_16750 8
+#define PORT_MAX 8
+
+struct serial_uart_config {
+ char *name;
+ int dfl_xmit_fifo_size;
+ int flags;
+};
+
+#define UART_CLEAR_FIFO 0x01
+#define UART_USE_FIFO 0x02
+#define UART_STARTECH 0x04

/*
* Definitions for async_struct (and serial_struct) flags field
@@ -55,7 +67,7 @@
#define ASYNC_SAK 0x0004 /* Secure Attention Key (Orange book) */
#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */

-#define ASYNC_SPD_MASK 0x0030
+#define ASYNC_SPD_MASK 0x1030
#define ASYNC_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */

#define ASYNC_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
@@ -67,8 +79,13 @@
#define ASYNC_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
#define ASYNC_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */

-#define ASYNC_FLAGS 0x0FFF /* Possible legal async flags */
-#define ASYNC_USR_MASK 0x0430 /* Legal flags that non-privileged
+#define ASYNC_HARDPPS_CD 0x0800 /* Call hardpps when CD goes high */
+
+#define ASYNC_SPD_SHI 0x1000 /* Use 230400 instead of 38400 bps */
+#define ASYNC_SPD_WARP 0x1010 /* Use 460800 instead of 38400 bps */
+
+#define ASYNC_FLAGS 0x1FFF /* Possible legal async flags */
+#define ASYNC_USR_MASK 0x1430 /* Legal flags that non-privileged
* users can set or reset */

/* Internal flags used only by kernel/chr_drv/serial.c */
@@ -81,6 +98,8 @@
#define ASYNC_CHECK_CD 0x02000000 /* i.e., CLOCAL */
#define ASYNC_SHARE_IRQ 0x01000000 /* for multifunction cards */

+#define ASYNC_INTERNAL_FLAGS 0xFF000000 /* Internal flags */
+
/*
* Multiport serial configuration structure --- external structure
*/
@@ -128,20 +147,37 @@
__u32 cts, dsr, rng, dcd;
};

+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 baud_base;
int port;
- int irq;
- int flags; /* defined in tty.h */
- int hub6; /* HUB6 plus one */
- int type; /* UART type */
+ 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 xmit_fifo_size;
- int custom_divisor;
int x_char; /* xon/xoff character */
int close_delay;
unsigned short closing_wait;
@@ -152,7 +188,6 @@
unsigned long event;
unsigned long last_active;
int line;
- int count; /* # of fd on device */
int blocked_open; /* # of blocked opens */
long session; /* Session of opening process */
long pgrp; /* pgrp of opening process */
@@ -162,17 +197,15 @@
int xmit_cnt;
struct tq_struct tqueue;
struct tq_struct tqueue_hangup;
- struct termios normal_termios;
- struct termios callout_termios;
struct wait_queue *open_wait;
struct wait_queue *close_wait;
struct wait_queue *delta_msr_wait;
- struct async_icount icount; /* kernel counters for the 4 input interrupts */
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
===================================================================
RCS file: include/linux/RCS/serial_reg.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/serial_reg.h
--- include/linux/serial_reg.h 1996/11/02 01:12:36 1.1
+++ include/linux/serial_reg.h 1996/11/02 01:12:42
@@ -51,6 +51,8 @@
#define UART_FCR6_T_TRIGGER_8 0x10 /* Mask for transmit trigger set at 8 */
#define UART_FCR6_T_TRIGGER_24 0x20 /* Mask for transmit trigger set at 24 */
#define UART_FCR6_T_TRIGGER_30 0x30 /* Mask for transmit trigger set at 30 */
+/* TI 16750 definitions */
+#define UART_FCR7_64BYTE 0x20 /* Go into 64 byte mode */

/*
* These are the definitions for the Line Control Register
@@ -98,6 +100,11 @@
#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
+/*
+ * Sleep mode for ST16650 and TI16750.
+ * Note that for 16650, EFR-bit 4 must be selected as well.
+ */
+#define UART_IERX_SLEEP 0x10 /* Enable sleep mode */

/*
* These are the definitions for the Modem Control Register
@@ -128,7 +135,7 @@
#define UART_EFR_CTS 0x80 /* CTS flow control */
#define UART_EFR_RTS 0x40 /* RTS flow control */
#define UART_EFR_SCD 0x20 /* Special character detect */
-#define UART_EFR_ENI 0x10 /* Enhanced Interrupt */
+#define UART_EFR_ECB 0x10 /* Enhanced control bit */
/*
* the low four bits control software flow control
*/
===================================================================
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 1996/11/02 01:12:36 1.1
+++ include/linux/tty_driver.h 1996/11/02 04:52:21
@@ -91,7 +91,16 @@
*
* This routine notifies the tty driver that it should hangup the
* tty device.
+ *
+ * void (*wait_until_sent)(struct tty_struct *tty, int timeout);
*
+ * This routine waits until the device has written out all of the
+ * characters in its transmitter FIFO.
+ *
+ * void (*send_xchar)(struct tty_struct *tty, char ch);
+ *
+ * This routine is used to send a high-priority XON/XOFF
+ * character to the device.
*/

#include <linux/fs.h>
@@ -139,6 +148,8 @@
void (*hangup)(struct tty_struct *tty);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
+ void (*wait_until_sent)(struct tty_struct *tty, int timeout);
+ void (*send_xchar)(struct tty_struct *tty, char ch);

/*
* linked list pointers
===================================================================
RCS file: include/linux/RCS/tty_ldisc.h,v
retrieving revision 1.1
diff -u -r1.1 include/linux/tty_ldisc.h
--- include/linux/tty_ldisc.h 1996/11/02 01:12:36 1.1
+++ include/linux/tty_ldisc.h 1996/11/02 01:18:14
@@ -2,7 +2,100 @@
#define _LINUX_TTY_LDISC_H

/*
- * Definitions for the tty line discipline
+ * This structure defines the interface between the tty line discpline
+ * implementation and the tty routines. The following routines can be
+ * defined; unless noted otherwise, they are optional, and can be
+ * filled in with a null pointer.
+ *
+ * int (*open)(struct tty_struct *);
+ *
+ * This function is called when the line discpline is associated
+ * with the tty. The line discpline can use this as an
+ * opportunity to initialize any state needed by the ldisc routines.
+ *
+ * void (*close)(struct tty_struct *);
+ *
+ * This function is called when the line discpline is being
+ * shutdown, either because the tty is being closed or because
+ * the tty is being changed to use a new line discpline
+ *
+ * void (*flush_buffer)(struct tty_struct *tty);
+ *
+ * This function instructs the line discipline to clear its
+ * buffers of any input characters it may have queued to be
+ * delivered to the user mode process.
+ *
+ * int (*chars_in_buffer)(struct tty_struct *tty);
+ *
+ * This function returns the number of input characters the line
+ * iscpline may have queued up to be delivered to the user mode
+ * process.
+ *
+ * int (*read)(struct tty_struct * tty, struct file * file,
+ * unsigned char * buf, unsigned int nr);
+ *
+ * This function is called when the user requests to read from
+ * the tty. The line discpline will return whatever characters
+ * it has buffered up for the user. If this function is not
+ * defined, the user will receive an EIO error.
+ *
+ * int (*write)(struct tty_struct * tty, struct file * file,
+ * const unsigned char * buf, unsigned int nr);
+ *
+ * This function is called when the user requests to write to the
+ * tty. The line discpline will deliver the characters to the
+ * low-level tty device for transmission, optionally performing
+ * some processing on the characters first. If this function is
+ * not defined, the user will receive an EIO error.
+ *
+ * int (*ioctl)(struct tty_struct * tty, struct file * file,
+ * unsigned int cmd, unsigned long arg);
+ *
+ * This function is called when the user requests an ioctl which
+ * is not handled by the tty layer or the low-level tty driver.
+ * It is intended for ioctls which affect line discpline
+ * operation. Not that the search order for ioctls is (1) tty
+ * layer, (2) tty low-level driver, (3) line discpline. So a
+ * low-level driver can "grab" an ioctl request before the line
+ * discpline has a chance to see it.
+ *
+ * void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ *
+ * This function notifies the line discpline that a change has
+ * been made to the termios stucture.
+ *
+ * int (*select)(struct tty_struct * tty, struct inode * inode,
+ * struct file * file, int sel_type,
+ * struct select_table_struct *wait);
+ *
+ * This function is called when a user attempts to select on a
+ * tty device. It is solely the responsibility of the line
+ * discipline to handle select requests.
+ *
+ * void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
+ * char *fp, int count);
+ *
+ * This function is called by the low-level tty driver to send
+ * characters received by the hardware to the line discpline for
+ * processing. <cp> is a pointer to the buffer of input
+ * character received by the device. <fp> is a pointer to a
+ * pointer of flag bytes which indicate whether a character was
+ * received with a parity error, etc.
+ *
+ * int (*receive_room)(struct tty_struct *);
+ *
+ * This function is called by the low-level tty driver to
+ * determine how many characters the line discpline can accept.
+ * The low-level driver must not send more characters than was
+ * indicated by receive_room, or the line discpline may drop
+ * those characters.
+ *
+ * void (*write_wakeup)(struct tty_struct *);
+ *
+ * This function is called by the low-level tty driver to signal
+ * that line discpline should try to send more characters to the
+ * low-level driver for transmission. If the line discpline does
+ * not have any more data to send, it can just return.
*/

#include <linux/fs.h>