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

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