patch-2.1.28 linux/drivers/char/serial.c
Next file: linux/drivers/char/tty_io.c
Previous file: linux/drivers/char/random.c
Back to the patch index
Back to the overall index
- Lines: 1079
- Date:
Fri Feb 28 15:02:02 1997
- Orig file:
v2.1.27/linux/drivers/char/serial.c
- Orig date:
Sun Feb 2 05:21:13 1997
diff -u --recursive --new-file v2.1.27/linux/drivers/char/serial.c linux/drivers/char/serial.c
@@ -18,11 +18,13 @@
* rs_set_termios fixed to look also for changes of the input
* flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
* Bernd Anhäupl 05/17/96.
+ *
+ * 1/97: Extended dumb serial ports are a config option now.
+ * Saves 4k. Michael A. Griffith <grif@acm.org>
*
* This module exports the following rs232 io functions:
*
* int rs_init(void);
- * int rs_open(struct tty_struct * tty, struct file * filp)
*/
#include <linux/config.h>
@@ -50,17 +52,13 @@
#include <asm/bitops.h>
static char *serial_name = "Serial driver";
-static char *serial_version = "4.22";
+static char *serial_version = "4.23";
-DECLARE_TASK_QUEUE(tq_serial);
+static DECLARE_TASK_QUEUE(tq_serial);
-struct tty_driver serial_driver, callout_driver;
+static struct tty_driver serial_driver, callout_driver;
static int serial_refcount;
-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL 1
-#define SERIAL_TYPE_CALLOUT 2
-
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
@@ -71,6 +69,16 @@
* Enables support for the venerable Bell Technologies
* HUB6 card.
*
+ * CONFIG_SERIAL_MANY_PORTS
+ * Enables support for ports beyond the standard, stupid
+ * COM 1/2/3/4.
+ *
+ * CONFIG_SERIAL_MULTIPORT
+ * Enables support for special multiport board support.
+ *
+ * CONFIG_SERIAL_SHARE_IRQ
+ * Enables support for multiple serial ports on one IRQ
+ *
* SERIAL_PARANOIA_CHECK
* Check the magic number for the async_structure where
* ever possible.
@@ -80,6 +88,34 @@
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART
+#if 0
+/* Normally these defines are controlled by the autoconf.h */
+
+#define CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_MULTIPORT
+#define CONFIG_HUB6
+#endif
+
+/* Sanity checks */
+
+#ifdef CONFIG_SERIAL_MULTIPORT
+#ifndef CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_SHARE_IRQ
+#endif
+#endif
+
+#ifdef CONFIG_HUB6
+#ifndef CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS
+#endif
+#ifndef CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_SHARE_IRQ
+#endif
+#endif
+
+/* Set of debugging defines */
+
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
@@ -105,7 +141,9 @@
*/
static struct async_struct *IRQ_ports[16];
+#ifdef CONFIG_SERIAL_MULTIPORT
static struct rs_multiport_struct rs_multiport[16];
+#endif
static int IRQ_timeout[16];
static volatile int rs_irq_triggered;
static volatile int rs_triggered;
@@ -146,10 +184,13 @@
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
#define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
#define FOURPORT_FLAGS ASYNC_FOURPORT
#define ACCENT_FLAGS 0
#define BOCA_FLAGS 0
#define HUB6_FLAGS 0
+#endif
/*
* The following define the access methods for the HUB6 card. All
@@ -167,13 +208,13 @@
#define C_P(card,port) (((card)<<6|(port)<<3) + 1)
-struct serial_state rs_table[] = {
+static 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 */
{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS }, /* ttyS2 */
{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS }, /* ttyS3 */
-
+#ifdef CONFIG_SERIAL_MANY_PORTS
{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, /* ttyS4 */
{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS }, /* ttyS5 */
{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS }, /* ttyS6 */
@@ -223,6 +264,7 @@
{ 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
+#endif /* CONFIG_SERIAL_MANY_PORTS */
#ifdef CONFIG_MCA
{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
@@ -426,43 +468,79 @@
struct tty_struct *tty = info->tty;
unsigned char ch;
int ignored = 0;
+ struct async_icount *icount;
+ icount = &info->state->icount;
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++;
- if (*status & (UART_LSR_BI)) {
+ *tty->flip.char_buf_ptr = ch;
+ icount->rx++;
+
#ifdef SERIAL_DEBUG_INTR
- printk("handling break....");
+ printk("DR%02x:%02x...", ch, *status);
#endif
- *tty->flip.flag_buf_ptr++ = TTY_BREAK;
- if (info->flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (*status & UART_LSR_PE)
- *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- else if (*status & UART_LSR_FE)
- *tty->flip.flag_buf_ptr++ = TTY_FRAME;
- else if (*status & UART_LSR_OE)
- *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- else
- *tty->flip.flag_buf_ptr++ = 0;
- *tty->flip.char_buf_ptr++ = ch;
+ *tty->flip.flag_buf_ptr = 0;
+ if (*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)) {
+ /*
+ * For statistics only
+ */
+ if (*status & UART_LSR_BI) {
+ *status &= ~(UART_LSR_FE | UART_LSR_PE);
+ icount->brk++;
+ } else if (*status & UART_LSR_PE)
+ icount->parity++;
+ else if (*status & UART_LSR_FE)
+ icount->frame++;
+ if (*status & UART_LSR_OE)
+ icount->overrun++;
+
+ /*
+ * Now check to see if character should be
+ * ignored, and mask off conditions which
+ * should be ignored.
+ */
+ if (*status & info->ignore_status_mask) {
+ if (++ignored > 100)
+ break;
+ goto ignore_char;
+ }
+ *status &= info->read_status_mask;
+
+ if (*status & (UART_LSR_BI)) {
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling break....");
+#endif
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
+ if (info->flags & ASYNC_SAK)
+ do_SAK(tty);
+ } else if (*status & UART_LSR_PE)
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
+ else if (*status & UART_LSR_FE)
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
+ if (*status & UART_LSR_OE) {
+ /*
+ * Overrun is special, since it's
+ * reported immediately, and doesn't
+ * affect the current character
+ */
+ if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+ tty->flip.count++;
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+ }
+ }
+ }
+ tty->flip.flag_buf_ptr++;
+ tty->flip.char_buf_ptr++;
+ tty->flip.count++;
ignore_char:
*status = serial_inp(info, UART_LSR);
} while (*status & UART_LSR_DR);
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#ifdef SERIAL_DEBUG_INTR
- printk("DR...");
-#endif
}
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -471,6 +549,7 @@
if (info->x_char) {
serial_outp(info, UART_TX, info->x_char);
+ info->state->icount.tx++;
info->x_char = 0;
if (intr_done)
*intr_done = 0;
@@ -487,6 +566,7 @@
do {
serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ info->state->icount.tx++;
if (--info->xmit_cnt <= 0)
break;
} while (--count > 0);
@@ -574,6 +654,7 @@
}
}
+#ifdef CONFIG_SERIAL_SHARE_IRQ
/*
* This is the serial driver's generic interrupt routine
*/
@@ -583,8 +664,10 @@
struct async_struct * info;
int pass_counter = 0;
struct async_struct *end_mark = 0;
+#ifdef CONFIG_SERIAL_MULTIPORT
int first_multi = 0;
struct rs_multiport_struct *multi;
+#endif
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt(%d)...", irq);
@@ -594,9 +677,11 @@
if (!info)
return;
+#ifdef CONFIG_SERIAL_MULTIPORT
multi = &rs_multiport[irq];
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
+#endif
do {
if (!info->tty ||
@@ -632,14 +717,18 @@
continue;
}
} while (end_mark != info);
+#ifdef CONFIG_SERIAL_MULTIPORT
if (multi->port_monitor)
printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
info->state->irq, first_multi,
inb(multi->port_monitor));
+#endif
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
+#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */
+
/*
* This is the serial driver's interrupt routine for a single port
@@ -648,9 +737,11 @@
{
int status;
int pass_counter = 0;
- int first_multi = 0;
struct async_struct * info;
+#ifdef CONFIG_SERIAL_MULTIPORT
+ int first_multi = 0;
struct rs_multiport_struct *multi;
+#endif
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt_single(%d)...", irq);
@@ -660,9 +751,11 @@
if (!info || !info->tty)
return;
+#ifdef CONFIG_SERIAL_MULTIPORT
multi = &rs_multiport[irq];
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
+#endif
do {
status = serial_inp(info, UART_LSR);
@@ -682,15 +775,18 @@
}
} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
info->last_active = jiffies;
+#ifdef CONFIG_SERIAL_MULTIPORT
if (multi->port_monitor)
printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
info->state->irq, first_multi,
inb(multi->port_monitor));
+#endif
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
+#ifdef CONFIG_SERIAL_MULTIPORT
/*
* This is the serial driver's for multiport boards
*/
@@ -771,7 +867,7 @@
printk("end.\n");
#endif
}
-
+#endif
/*
* -------------------------------------------------------------------
@@ -851,6 +947,7 @@
if (!info)
continue;
cli();
+#ifdef CONFIG_SERIAL_SHARE_IRQ
if (info->next_port) {
do {
serial_out(info, UART_IER, 0);
@@ -858,11 +955,14 @@
serial_out(info, UART_IER, info->IER);
info = info->next_port;
} while (info);
+#ifdef CONFIG_SERIAL_MULTIPORT
if (rs_multiport[i].port1)
rs_interrupt_multi(i, NULL, NULL);
else
+#endif
rs_interrupt(i, NULL, NULL);
} else
+#endif /* CONFIG_SERIAL_SHARE_IRQ */
rs_interrupt_single(i, NULL, NULL);
sti();
}
@@ -873,7 +973,11 @@
if (IRQ_ports[0]) {
cli();
+#ifdef CONFIG_SERIAL_SHARE_IRQ
rs_interrupt(0, NULL, NULL);
+#else
+ rs_interrupt_single(0, NULL, NULL);
+#endif
sti();
timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
@@ -948,12 +1052,15 @@
static int startup(struct async_struct * info)
{
- unsigned short ICP;
unsigned long flags;
int retval;
void (*handler)(int, void *, struct pt_regs *);
struct serial_state *state= info->state;
unsigned long page;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+ unsigned short ICP;
+#endif
+
page = get_free_page(GFP_KERNEL);
if (!page)
@@ -1026,11 +1133,17 @@
if (state->irq && (!IRQ_ports[state->irq] ||
!IRQ_ports[state->irq]->next_port)) {
if (IRQ_ports[state->irq]) {
+#ifdef CONFIG_SERIAL_SHARE_IRQ
free_irq(state->irq, NULL);
+#ifdef CONFIG_SERIAL_MULTIPORT
if (rs_multiport[state->irq].port1)
handler = rs_interrupt_multi;
else
+#endif
handler = rs_interrupt;
+#else
+ return -EBUSY;
+#endif /* CONFIG_SERIAL_SHARE_IRQ */
} else
handler = rs_interrupt_single;
@@ -1074,10 +1187,13 @@
info->MCR = 0;
if (info->tty->termios->c_cflag & CBAUD)
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
if (state->irq == 0)
info->MCR |= UART_MCR_OUT1;
- } else {
+ } else
+#endif
+ {
if (state->irq != 0)
info->MCR |= UART_MCR_OUT2;
}
@@ -1095,12 +1211,14 @@
info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
serial_outp(info, UART_IER, info->IER); /* enable interrupts */
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
/* Enable interrupts on the AST Fourport board */
ICP = (info->port & 0xFE0) | 0x01F;
outb_p(0x80, ICP);
(void) inb_p(ICP);
}
+#endif
/*
* And clear the interrupt registers again for luck.
@@ -1193,11 +1311,13 @@
info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
/* reset interrupts on the AST Fourport board */
(void) inb((info->port & 0xFE0) | 0x01F);
info->MCR |= UART_MCR_OUT1;
} else
+#endif
info->MCR &= ~UART_MCR_OUT2;
#if defined(__alpha__) && !defined(CONFIG_PCI)
/*
@@ -1270,6 +1390,10 @@
}
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ cval |= UART_LCR_SPAR;
+#endif
/* Determine divisor based on baud rate */
i = cflag & CBAUD;
@@ -1303,6 +1427,7 @@
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 */
@@ -1376,7 +1501,8 @@
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 */
+ if (info->state->type != PORT_16750)
+ serial_outp(info, UART_FCR, fcr); /* set fcr */
restore_flags(flags);
}
@@ -1424,7 +1550,7 @@
static int rs_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
- int c, total = 0;
+ int c, ret = 0;
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
@@ -1445,7 +1571,12 @@
break;
if (from_user) {
- copy_from_user(tmp_buf, buf, c);
+ c -= copy_from_user(tmp_buf, buf, c);
+ if (!c) {
+ if (!ret)
+ ret = -EFAULT;
+ break;
+ }
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
@@ -1456,7 +1587,7 @@
restore_flags(flags);
buf += c;
count -= c;
- total += c;
+ ret += c;
}
if (from_user)
up(&tmp_buf_sem);
@@ -1466,7 +1597,7 @@
serial_out(info, UART_IER, info->IER);
}
restore_flags(flags);
- return total;
+ return ret;
}
static int rs_write_room(struct tty_struct *tty)
@@ -1510,7 +1641,7 @@
* 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)
+static void rs_send_xchar(struct tty_struct *tty, char ch)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
@@ -1609,7 +1740,8 @@
tmp.closing_wait = state->closing_wait;
tmp.custom_divisor = state->custom_divisor;
tmp.hub6 = state->hub6;
- copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+ if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+ return -EFAULT;
return 0;
}
@@ -1621,9 +1753,8 @@
unsigned int i,change_irq,change_port;
int retval = 0;
- if (!new_info)
+ if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
- copy_from_user(&new_serial,new_info,sizeof(new_serial));
state = info->state;
old_state = *state;
@@ -1734,8 +1865,7 @@
status = serial_in(info, UART_LSR);
sti();
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- put_user(result,value);
- return 0;
+ return put_user(result,value);
}
@@ -1750,12 +1880,15 @@
sti();
result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+#ifdef TIOCM_OUT1
+ | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
+ | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
+#endif
| ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
| ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
| ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
| ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- put_user(result,value);
- return 0;
+ return put_user(result,value);
}
static int set_modem_info(struct async_struct * info, unsigned int cmd,
@@ -1773,16 +1906,37 @@
info->MCR |= UART_MCR_RTS;
if (arg & TIOCM_DTR)
info->MCR |= UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+ if (arg & TIOCM_OUT1)
+ info->MCR |= UART_MCR_OUT1;
+ if (arg & TIOCM_OUT2)
+ info->MCR |= UART_MCR_OUT2;
+#endif
break;
case TIOCMBIC:
if (arg & TIOCM_RTS)
info->MCR &= ~UART_MCR_RTS;
if (arg & TIOCM_DTR)
info->MCR &= ~UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+ if (arg & TIOCM_OUT1)
+ info->MCR &= ~UART_MCR_OUT1;
+ if (arg & TIOCM_OUT2)
+ info->MCR &= ~UART_MCR_OUT2;
+#endif
break;
case TIOCMSET:
- info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+ info->MCR = ((info->MCR & ~(UART_MCR_RTS |
+#ifdef TIOCM_OUT1
+ UART_MCR_OUT1 |
+ UART_MCR_OUT2 |
+#endif
+ UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+#ifdef TIOCM_OUT1
+ | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
+ | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
+#endif
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
break;
default:
@@ -1883,6 +2037,7 @@
return wild_interrupts;
}
+#ifdef CONFIG_SERIAL_MULTIPORT
static int get_multiport_struct(struct async_struct * info,
struct serial_multiport_struct *retinfo)
{
@@ -1911,9 +2066,9 @@
ret.irq = info->state->irq;
- copy_to_user(retinfo,&ret,sizeof(*retinfo));
+ if (copy_to_user(retinfo,&ret,sizeof(*retinfo)))
+ return -EFAULT;
return 0;
-
}
static int set_multiport_struct(struct async_struct * info,
@@ -1928,13 +2083,12 @@
if (!suser())
return -EPERM;
- if (!in_multi)
- return -EFAULT;
state = info->state;
- copy_from_user(&new_multi, in_multi,
- sizeof(struct serial_multiport_struct));
-
+ if (copy_from_user(&new_multi, in_multi,
+ sizeof(struct serial_multiport_struct)))
+ return -EFAULT;
+
if (new_multi.irq != state->irq || state->irq == 0 ||
!IRQ_ports[state->irq])
return -EINVAL;
@@ -1996,6 +2150,7 @@
return 0;
}
+#endif
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
@@ -2053,82 +2208,52 @@
(arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
return set_modem_info(info, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
- error = verify_area(VERIFY_READ, (void *) arg,
- sizeof(struct serial_struct));
- if (error)
- return error;
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERCONFIG:
return do_autoconfig(info);
case TIOCSERGWILD:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(int));
- if (error)
- return error;
- put_user(rs_wild_int_mask, (unsigned int *) arg);
- return 0;
-
+ return put_user(rs_wild_int_mask,
+ (unsigned int *) arg);
case TIOCSERGETLSR: /* Get line status register */
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
- if (error)
- return error;
- else
- return get_lsr_info(info, (unsigned int *) arg);
+ return get_lsr_info(info, (unsigned int *) arg);
case TIOCSERSWILD:
if (!suser())
return -EPERM;
- error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
+ error = get_user(rs_wild_int_mask,
+ (unsigned int *) arg);
if (error)
return error;
- get_user(rs_wild_int_mask, (unsigned int *) arg);
if (rs_wild_int_mask < 0)
rs_wild_int_mask = check_wild_interrupts(0);
return 0;
case TIOCSERGSTRUCT:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct async_struct));
- if (error)
- return error;
- copy_to_user((struct async_struct *) arg,
- info, sizeof(struct async_struct));
+ if (copy_to_user((struct async_struct *) arg,
+ info, sizeof(struct async_struct)))
+ return -EFAULT;
return 0;
-
+
+#ifdef CONFIG_SERIAL_MULTIPORT
case TIOCSERGETMULTI:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_multiport_struct));
- if (error)
- return error;
return get_multiport_struct(info,
(struct serial_multiport_struct *) arg);
case TIOCSERSETMULTI:
- error = verify_area(VERIFY_READ, (void *) arg,
- sizeof(struct serial_multiport_struct));
- if (error)
- return error;
return set_multiport_struct(info,
(struct serial_multiport_struct *) arg);
+#endif
+
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
@@ -2168,18 +2293,18 @@
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
- error = verify_area(VERIFY_WRITE, (void *) arg,
- sizeof(struct serial_icounter_struct));
- if (error)
- return error;
cli();
cnow = info->state->icount;
sti();
p_cuser = (struct serial_icounter_struct *) arg;
- put_user(cnow.cts, &p_cuser->cts);
- put_user(cnow.dsr, &p_cuser->dsr);
- put_user(cnow.rng, &p_cuser->rng);
- put_user(cnow.dcd, &p_cuser->dcd);
+ error = put_user(cnow.cts, &p_cuser->cts);
+ if (error) return error;
+ error = put_user(cnow.dsr, &p_cuser->dsr);
+ if (error) return error;
+ error = put_user(cnow.rng, &p_cuser->rng);
+ if (error) return error;
+ error = put_user(cnow.dcd, &p_cuser->dcd);
+ if (error) return error;
return 0;
default:
@@ -2409,7 +2534,7 @@
/*
* rs_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
-void rs_hangup(struct tty_struct *tty)
+static void rs_hangup(struct tty_struct *tty)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
struct serial_state *state = info->state;
@@ -2567,7 +2692,7 @@
return 0;
}
-int get_async_struct(int line, struct async_struct **ret_info)
+static int get_async_struct(int line, struct async_struct **ret_info)
{
struct async_struct *info;
struct serial_state *sstate;
@@ -2609,7 +2734,7 @@
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
-int rs_open(struct tty_struct *tty, struct file * filp)
+static int rs_open(struct tty_struct *tty, struct file * filp)
{
struct async_struct *info;
int retval, line;
@@ -2677,6 +2802,106 @@
}
/*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct serial_state *state)
+{
+ struct async_struct *info = state->info, scr_info;
+ char stat_buf[30], control, status;
+ int ret;
+
+ ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
+ state->line, uart_config[state->type].name,
+ state->port, state->irq);
+
+ if (!state->port || (state->type == PORT_UNKNOWN)) {
+ ret += sprintf(buf+ret, "\n");
+ return ret;
+ }
+
+ /*
+ * Figure out the current RS-232 lines
+ */
+ if (!info) {
+ info = &scr_info; /* This is just for serial_{in,out} */
+
+ info->magic = SERIAL_MAGIC;
+ info->port = state->port;
+ info->flags = state->flags;
+ info->quot = 0;
+ info->tty = 0;
+ }
+ cli();
+ status = serial_in(info, UART_MSR);
+ control = info ? info->MCR : serial_in(info, UART_MCR);
+ sti();
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if (control & UART_MCR_RTS)
+ strcat(stat_buf, "|RTS");
+ if (status & UART_MSR_CTS)
+ strcat(stat_buf, "|CTS");
+ if (control & UART_MCR_DTR)
+ strcat(stat_buf, "|DTR");
+ if (status & UART_MSR_DSR)
+ strcat(stat_buf, "|DSR");
+ if (status & UART_MSR_DCD)
+ strcat(stat_buf, "|CD");
+ if (status & UART_MSR_RI)
+ strcat(stat_buf, "|RI");
+
+ if (info->quot) {
+ ret += sprintf(buf+ret, " baud:%d",
+ state->baud_base / info->quot);
+ }
+
+ ret += sprintf(buf+ret, " tx:%d rx:%d",
+ state->icount.tx, state->icount.rx);
+
+ if (state->icount.frame)
+ ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+
+ if (state->icount.parity)
+ ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+
+ if (state->icount.brk)
+ ret += sprintf(buf+ret, " brk:%d", state->icount.brk);
+
+ if (state->icount.overrun)
+ ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+ /*
+ * Last thing is the RS-232 status lines
+ */
+ ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+ return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count, void
+ *data)
+{
+ int i, len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+ for (i = 0; i < NR_PORTS && len < 4000; i++) {
+ len += line_info(page + len, &rs_table[i]);
+ if (len+begin > off+count)
+ break;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ if (off >= len+begin)
+ return 0;
+ *start = page + (begin-off);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
* ---------------------------------------------------------------------
* rs_init() and friends
*
@@ -2711,9 +2936,14 @@
*/
static int get_auto_irq(struct async_struct *info)
{
- unsigned char save_MCR, save_IER, save_ICP=0;
- unsigned short ICP=0, port = info->port;
+
+ unsigned char save_MCR, save_IER;
unsigned long timeout;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+ unsigned char save_ICP=0;
+ unsigned short ICP=0, port = info->port;
+#endif
+
/*
* Enable interrupts and see who answers
@@ -2722,6 +2952,7 @@
cli();
save_IER = serial_inp(info, UART_IER);
save_MCR = serial_inp(info, UART_MCR);
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT) {
serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
@@ -2729,7 +2960,9 @@
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
(void) inb_p(ICP);
- } else {
+ } else
+#endif
+ {
serial_outp(info, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
@@ -2754,8 +2987,10 @@
cli();
serial_outp(info, UART_IER, save_IER);
serial_outp(info, UART_MCR, save_MCR);
+#ifdef CONFIG_SERIAL_MANY_PORTS
if (info->flags & ASYNC_FOURPORT)
outb_p(save_ICP, ICP);
+#endif
sti();
return(rs_irq_triggered);
}
@@ -2912,7 +3147,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 == 7)
+ if (scratch == 6)
state->type = PORT_16750;
}
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
@@ -2981,7 +3216,10 @@
for (i = 0; i < 16; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
- memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
+#ifdef CONFIG_SERIAL_MULTIPORT
+ memset(&rs_multiport[i], 0,
+ sizeof(struct rs_multiport_struct));
+#endif
}
show_serial_version();
@@ -2990,6 +3228,7 @@
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
+ serial_driver.driver_name = "serial";
serial_driver.name = "ttyS";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
@@ -3022,7 +3261,8 @@
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
serial_driver.wait_until_sent = rs_wait_until_sent;
-
+ serial_driver.read_proc = rs_read_proc;
+
/*
* The callout device is just like normal device except for
* major number and the subtype code.
@@ -3031,6 +3271,8 @@
callout_driver.name = "cua";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ callout_driver.read_proc = 0;
+ callout_driver.proc_entry = 0;
if (tty_register_driver(&serial_driver))
panic("Couldn't register serial driver\n");
@@ -3048,22 +3290,26 @@
state->normal_termios = serial_driver.init_termios;
state->icount.cts = state->icount.dsr =
state->icount.rng = state->icount.dcd = 0;
+ state->icount.rx = state->icount.tx = 0;
+ state->icount.frame = state->icount.parity = 0;
+ state->icount.overrun = state->icount.brk = 0;
if (state->irq == 2)
state->irq = 9;
if (state->type == PORT_UNKNOWN) {
if (!(state->flags & ASYNC_BOOT_AUTOCONF))
continue;
+ if (check_region(state->port,8))
+ continue;
autoconfig(state);
if (state->type == PORT_UNKNOWN)
continue;
}
- printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d) is a %s\n",
+ printk(KERN_INFO "ttyS%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);
}
-
return 0;
}
@@ -3161,5 +3407,10 @@
if (rs_table[i].type != PORT_UNKNOWN)
release_region(rs_table[i].port, 8);
}
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
}
#endif /* MODULE */
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov