patch-2.1.15 linux/drivers/char/serial.c
Next file: linux/drivers/char/softdog.c
Previous file: linux/drivers/char/psaux.c
Back to the patch index
Back to the overall index
- Lines: 472
- Date:
Thu Dec 12 16:51:09 1996
- Orig file:
v2.1.14/linux/drivers/char/serial.c
- Orig date:
Thu Dec 12 17:02:42 1996
diff -u --recursive --new-file v2.1.14/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -50,7 +50,7 @@
#include <asm/bitops.h>
static char *serial_name = "Serial driver";
-static char *serial_version = "4.21";
+static char *serial_version = "4.22";
DECLARE_TASK_QUEUE(tq_serial);
@@ -83,6 +83,7 @@
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
#define RS_STROBE_TIME (10*HZ)
#define RS_ISR_PASS_LIMIT 256
@@ -222,6 +223,14 @@
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) }, /* ttyS42 */
{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) }, /* ttyS43 */
#endif
+#ifdef CONFIG_MCA
+ { 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
+ { 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
+ { 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS },
+ { 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS },
+ { 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS },
+ { 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS },
+#endif
};
#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state))
@@ -1040,6 +1049,16 @@
}
/*
+ * Insert serial port into IRQ chain.
+ */
+ info->prev_port = 0;
+ info->next_port = IRQ_ports[state->irq];
+ if (info->next_port)
+ info->next_port->prev_port = info;
+ IRQ_ports[state->irq] = info;
+ figure_IRQ_timeout(state->irq);
+
+ /*
* Clear the interrupt registers.
*/
/* (void) serial_inp(info, UART_LSR); */ /* (see above) */
@@ -1051,19 +1070,23 @@
* Now, initialize the UART
*/
serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
- if (info->flags & ASYNC_FOURPORT) {
+
+ info->MCR = 0;
+ if (info->tty->termios->c_cflag & CBAUD)
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
- info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
+ if (info->flags & ASYNC_FOURPORT) {
+ if (state->irq == 0)
+ info->MCR |= UART_MCR_OUT1;
} else {
- info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
- info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
+ if (state->irq != 0)
+ info->MCR |= UART_MCR_OUT2;
}
#if defined(__alpha__) && !defined(CONFIG_PCI)
+ /*
+ * DEC did something gratutiously wrong....
+ */
info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
- info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
#endif
- if (state->irq == 0)
- info->MCR = info->MCR_noint;
serial_outp(info, UART_MCR, info->MCR);
/*
@@ -1092,16 +1115,6 @@
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
/*
- * Insert serial port into IRQ chain.
- */
- info->prev_port = 0;
- info->next_port = IRQ_ports[state->irq];
- if (info->next_port)
- info->next_port->prev_port = info;
- IRQ_ports[state->irq] = info;
- figure_IRQ_timeout(state->irq);
-
- /*
* Set up serial timers...
*/
timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
@@ -1183,13 +1196,19 @@
if (info->flags & ASYNC_FOURPORT) {
/* reset interrupts on the AST Fourport board */
(void) inb((info->port & 0xFE0) | 0x01F);
- }
+ info->MCR |= UART_MCR_OUT1;
+ } else
+ info->MCR &= ~UART_MCR_OUT2;
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+ /*
+ * DEC did something gratutiously wrong....
+ */
+ info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
+#endif
- if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
- info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
- }
- serial_outp(info, UART_MCR, info->MCR_noint);
+ serial_outp(info, UART_MCR, info->MCR);
/* disable FIFO's */
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
@@ -1223,13 +1242,36 @@
unsigned short port;
int quot = 0, baud_base;
unsigned cflag, cval, fcr = 0;
- int i;
+ int i, bits;
+ unsigned long flags;
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
if (!(port = info->port))
return;
+
+ /* byte size and parity */
+ switch (cflag & CSIZE) {
+ case CS5: cval = 0x00; bits = 7; break;
+ case CS6: cval = 0x01; bits = 8; break;
+ case CS7: cval = 0x02; bits = 9; break;
+ case CS8: cval = 0x03; bits = 10; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: cval = 0x00; bits = 7; break;
+ }
+ if (cflag & CSTOPB) {
+ cval |= 0x04;
+ bits++;
+ }
+ if (cflag & PARENB) {
+ cval |= UART_LCR_PARITY;
+ bits++;
+ }
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ /* Determine divisor based on baud rate */
i = cflag & CBAUD;
if (i & CBAUDEX) {
i &= ~CBAUDEX;
@@ -1251,42 +1293,20 @@
quot = info->state->custom_divisor;
}
baud_base = info->state->baud_base;
- if (quot) {
- info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
- baud_base) + 2;
- } else if (baud_table[i] == 134) {
- quot = (2*baud_base / 269);
- info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
- } else if (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();
- return;
- }
- /* byte size and parity */
- switch (cflag & CSIZE) {
- case CS5: cval = 0x00; break;
- case CS6: cval = 0x01; break;
- case CS7: cval = 0x02; break;
- case CS8: cval = 0x03; break;
- default: cval = 0x00; break; /* too keep GCC shut... */
- }
- if (cflag & CSTOPB) {
- cval |= 0x04;
+ if (baud_table[i] == 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;
}
- if (cflag & PARENB)
- cval |= UART_LCR_PARITY;
- if (!(cflag & PARODD))
- cval |= UART_LCR_EPAR;
+ info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
+ info->timeout += HZ/50; /* Add .02 seconds of slop */
+
+ /* Set up FIFO's */
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;
@@ -1344,7 +1364,7 @@
*/
if ((cflag & CREAD) == 0)
info->ignore_status_mask |= UART_LSR_DR;
- cli();
+ save_flags(flags); cli();
if (uart_config[info->state->type].flags & UART_STARTECH) {
serial_outp(info, UART_LCR, 0xBF);
serial_outp(info, UART_EFR,
@@ -1357,7 +1377,7 @@
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();
+ restore_flags(flags);
}
static void rs_put_char(struct tty_struct *tty, unsigned char ch)
@@ -1529,10 +1549,9 @@
if (I_IXOFF(tty))
rs_send_xchar(tty, STOP_CHAR(tty));
- if (tty->termios->c_cflag & CRTSCTS) {
+ 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();
@@ -1557,10 +1576,8 @@
else
rs_send_xchar(tty, START_CHAR(tty));
}
- if (tty->termios->c_cflag & CRTSCTS) {
+ 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();
@@ -1685,7 +1702,7 @@
if (!state->port || !state->type)
return 0;
if (state->type != old_state.type)
- state->xmit_fifo_size =
+ info->xmit_fifo_size = state->xmit_fifo_size =
uart_config[state->type].dfl_xmit_fifo_size;
if (state->flags & ASYNC_INITIALIZED) {
if (((old_state.flags & ASYNC_SPD_MASK) !=
@@ -1752,33 +1769,21 @@
return error;
switch (cmd) {
case TIOCMBIS:
- if (arg & TIOCM_RTS) {
+ if (arg & TIOCM_RTS)
info->MCR |= UART_MCR_RTS;
- info->MCR_noint |= UART_MCR_RTS;
- }
- if (arg & TIOCM_DTR) {
+ if (arg & TIOCM_DTR)
info->MCR |= UART_MCR_DTR;
- info->MCR_noint |= UART_MCR_DTR;
- }
break;
case TIOCMBIC:
- if (arg & TIOCM_RTS) {
+ if (arg & TIOCM_RTS)
info->MCR &= ~UART_MCR_RTS;
- info->MCR_noint &= ~UART_MCR_RTS;
- }
- if (arg & TIOCM_DTR) {
+ if (arg & TIOCM_DTR)
info->MCR &= ~UART_MCR_DTR;
- info->MCR_noint &= ~UART_MCR_DTR;
- }
break;
case TIOCMSET:
info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
- info->MCR_noint = ((info->MCR_noint
- & ~(UART_MCR_RTS | UART_MCR_DTR))
- | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
- | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
break;
default:
return -EINVAL;
@@ -1821,11 +1826,17 @@
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
}
/*
@@ -2012,15 +2023,24 @@
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
- if (!arg)
+ if (current->signal & ~current->blocked)
+ return -EINTR;
+ if (!arg) {
send_break(info, HZ/4); /* 1/4 second */
+ if (current->signal & ~current->blocked)
+ 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 (current->signal & ~current->blocked)
+ return -EINTR;
send_break(info, arg ? arg*(HZ/10) : HZ/4);
+ if (current->signal & ~current->blocked)
+ return -EINTR;
return 0;
case TIOCGSOFTCAR:
return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
@@ -2179,14 +2199,29 @@
change_speed(info);
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(tty->termios->c_cflag & CBAUD)) {
+ info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+ cli();
+ serial_out(info, UART_MCR, info->MCR);
+ sti();
+ }
+
+ /* Handle transition away from B0 status */
if (!(old_termios->c_cflag & CBAUD) &&
(tty->termios->c_cflag & CBAUD)) {
info->MCR |= UART_MCR_DTR;
- info->MCR_noint |= UART_MCR_DTR;
+ if (!tty->hw_stopped ||
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ info->MCR |= UART_MCR_RTS;
+ }
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
}
+
+ /* Handle turning off CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
@@ -2293,7 +2328,7 @@
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
- rs_wait_until_sent(tty, HZ);
+ rs_wait_until_sent(tty, info->timeout);
}
shutdown(info);
if (tty->driver.flush_buffer)
@@ -2324,25 +2359,48 @@
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;
+ unsigned long orig_jiffies, char_time;
+ int lsr;
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;
+ /*
+ * Set the check interval to be 1/5 of the estimated time to
+ * send a single character, and make it at least 1. The check
+ * interval should also be less than the timeout.
+ *
+ * Note: we have to use pretty tight timings here to satisfy
+ * the NIST-PCTS.
+ */
+ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+ char_time = char_time / 5;
+ if (char_time == 0)
+ char_time = 1;
+ if (timeout)
+ char_time = MIN(char_time, timeout);
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+ printk("jiff=%lu...", jiffies);
+#endif
+ while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+ current->state = TASK_INTERRUPTIBLE;
+ current->counter = 0; /* make us low-priority */
+ current->timeout = jiffies + char_time;
schedule();
if (current->signal & ~current->blocked)
break;
- if (timeout && ((orig_jiffies + timeout) > jiffies))
- break;
- if (jiffies > timeout)
+ if (timeout && ((orig_jiffies + timeout) < jiffies))
break;
}
current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+ printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
}
/*
@@ -2457,7 +2515,8 @@
info->blocked_open++;
while (1) {
cli();
- if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+ if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (tty->termios->c_cflag & CBAUD))
serial_out(info, UART_MCR,
serial_inp(info, UART_MCR) |
(UART_MCR_DTR | UART_MCR_RTS));
@@ -2751,10 +2810,15 @@
if (!state->port)
return;
+
info = &scr_info; /* This is just for serial_{in,out} */
+
info->magic = SERIAL_MAGIC;
info->port = state->port;
info->flags = state->flags;
+
+ if(check_region(info->port,8))
+ return; /* Area in use */
save_flags(flags); cli();
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov