patch-2.4.6 linux/drivers/sbus/char/sab82532.c

Next file: linux/drivers/sbus/char/su.c
Previous file: linux/drivers/sbus/char/pcikbd.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.5/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c
@@ -1,8 +1,11 @@
-/* $Id: sab82532.c,v 1.58 2001/04/17 06:30:36 davem Exp $
+/* $Id: sab82532.c,v 1.63 2001/06/29 21:23:44 davem Exp $
  * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
  *
+ * Rewrote buffer handling to use CIRC(Circular Buffer) macros.
+ *   Maxim Krasnyanskiy <maxk@qualcomm.com>
+ *
  */
 
 #include <linux/config.h>
@@ -59,6 +62,7 @@
 #undef SERIAL_DEBUG_WAIT_UNTIL_SENT
 #undef SERIAL_DEBUG_SEND_BREAK
 #undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FIFO
 #define SERIAL_DEBUG_OVERFLOW 1
 
 /* Trace things on serial device, useful for console debugging: */
@@ -209,22 +213,23 @@
 
 	save_flags(flags); cli();
 
-	if (info->xmit_cnt <= 0)
+	if (info->xmit.head == info->xmit.tail)
 		goto out;
 
-	if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW))
+	if (!test_bit(SAB82532_XPR, &info->irqflags))
 		goto out;
 
 	info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
 	writeb(info->interrupt_mask1, &info->regs->w.imr1);
-	info->all_sent = 0;
+	clear_bit(SAB82532_ALLS, &info->irqflags);
 
+	clear_bit(SAB82532_XPR, &info->irqflags);
 	for (i = 0; i < info->xmit_fifo_size; i++) {
-		u8 val = info->xmit_buf[info->xmit_tail++];
-		writeb(val, &info->regs->w.xfifo[i]);
-		info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);
+		writeb(info->xmit.buf[info->xmit.tail],
+		       &info->regs->w.xfifo[i]);
+		info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
 		info->icount.tx++;
-		if (--info->xmit_cnt <= 0)
+		if (info->xmit.head == info->xmit.tail)
 			break;
 	}
 
@@ -397,20 +402,29 @@
 	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
 		info->interrupt_mask1 |= SAB82532_IMR1_ALLS;
 		writeb(info->interrupt_mask1, &info->regs->w.imr1);
-		info->all_sent = 1;
+		set_bit(SAB82532_ALLS, &info->irqflags);
 	}
 
 	if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))
 		return;
 
+	if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW)) {
+#ifdef SERIAL_DEBUG_FIFO
+		printk("%s: XPR, but no XFW (?)\n", __FUNCTION__);
+#endif
+		return;
+	}
+
+	set_bit(SAB82532_XPR, &info->irqflags);
+
 	if (!info->tty) {
 		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
 		writeb(info->interrupt_mask1, &info->regs->w.imr1);
 		return;
 	}
 
-	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
-	    info->tty->hw_stopped) {
+	if ((info->xmit.head == info->xmit.tail) ||
+	    info->tty->stopped || info->tty->hw_stopped) {
 		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
 		writeb(info->interrupt_mask1, &info->regs->w.imr1);
 		return;
@@ -418,15 +432,16 @@
 
 	info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
 	writeb(info->interrupt_mask1, &info->regs->w.imr1);
-	info->all_sent = 0;
+	clear_bit(SAB82532_ALLS, &info->irqflags);
 
 	/* Stuff 32 bytes into Transmit FIFO. */
+	clear_bit(SAB82532_XPR, &info->irqflags);
 	for (i = 0; i < info->xmit_fifo_size; i++) {
-		u8 val = info->xmit_buf[info->xmit_tail++];
-		writeb(val, &info->regs->w.xfifo[i]);
-		info->xmit_tail &= (SERIAL_XMIT_SIZE - 1);
+		writeb(info->xmit.buf[info->xmit.tail],
+		       &info->regs->w.xfifo[i]);
+		info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
 		info->icount.tx++;
-		if (--info->xmit_cnt <= 0)
+		if (info->xmit.head == info->xmit.tail)
 			break;
 	}
 
@@ -434,16 +449,12 @@
 	sab82532_cec_wait(info);
 	writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
 
-	if (info->xmit_cnt < WAKEUP_CHARS)
+	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
 		sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("THRE...");
 #endif
-	if (info->xmit_cnt <= 0) {
-		info->interrupt_mask1 |= SAB82532_IMR1_XPR;
-		writeb(info->interrupt_mask1, &info->regs->w.imr1);
-	}
 }
 
 static void check_status(struct sab82532 *info,
@@ -620,6 +631,7 @@
 		transmit_chars(info, &status);
 
 done:
+	;
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
 #endif
@@ -774,10 +786,10 @@
 		retval = -ENODEV;
 		goto errout;
 	}
-	if (info->xmit_buf)
+	if (info->xmit.buf)
 		free_page(page);
 	else
-		info->xmit_buf = (unsigned char *)page;
+		info->xmit.buf = (unsigned char *)page;
 
 #ifdef SERIAL_DEBUG_OPEN
 	printk("starting up serial port %d...", info->line);
@@ -812,11 +824,13 @@
 				SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
 				SAB82532_IMR1_XPR;
 	writeb(info->interrupt_mask1, &info->regs->w.imr1);
-	info->all_sent = 1;
+	set_bit(SAB82532_ALLS, &info->irqflags);
 
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
-	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	info->xmit.head = info->xmit.tail = 0;
+
+	set_bit(SAB82532_XPR, &info->irqflags);
 
 	/*
 	 * and set the speed of the serial port
@@ -856,9 +870,9 @@
 	 */
 	wake_up_interruptible(&info->delta_msr_wait);
 
-	if (info->xmit_buf) {
-		free_page((unsigned long)info->xmit_buf);
-		info->xmit_buf = 0;
+	if (info->xmit.buf) {
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = 0;
 	}
 
 #ifdef CONFIG_SERIAL_CONSOLE
@@ -886,9 +900,11 @@
 	writeb(info->interrupt_mask1, &info->regs->w.imr1);
 
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-		writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
-		writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-		writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
+		tmp = readb(&info->regs->r.mode);
+		tmp |= (SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
+		writeb(tmp, &info->regs->rw.mode);
+		writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit,
+		       &info->regs->rw.pvr);
 	}
 
 	/* Disable break condition */
@@ -1054,18 +1070,17 @@
 	if (serial_paranoia_check(info, tty->device, "sab82532_put_char"))
 		return;
 
-	if (!tty || !info->xmit_buf)
+	if (!tty || !info->xmit.buf)
 		return;
 
 	save_flags(flags); cli();
-	if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+	if (!CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)) {
 		restore_flags(flags);
 		return;
 	}
 
-	info->xmit_buf[info->xmit_head++] = ch;
-	info->xmit_head &= SERIAL_XMIT_SIZE-1;
-	info->xmit_cnt++;
+	info->xmit.buf[info->xmit.head] = ch;
+	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
 	restore_flags(flags);
 }
 
@@ -1077,8 +1092,8 @@
 	if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
 		return;
 
-	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-	    !info->xmit_buf)
+	if ((info->xmit.head == info->xmit.tail) ||
+	    tty->stopped || tty->hw_stopped || !info->xmit.buf)
 		return;
 
 	save_flags(flags); cli();
@@ -1098,42 +1113,63 @@
 	if (serial_paranoia_check(info, tty->device, "sab82532_write"))
 		return 0;
 
-	if (!tty || !info->xmit_buf || !tmp_buf)
+	if (!tty || !info->xmit.buf || !tmp_buf)
 		return 0;
 	    
-	if (from_user)
-		down(&tmp_buf_sem);
 	save_flags(flags);
-	while (1) {
-		cli();		
-		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				   SERIAL_XMIT_SIZE - info->xmit_head));
-		if (c <= 0)
-			break;
+	if (from_user) {
+		down(&tmp_buf_sem);
+		while (1) {
+			int c1;
+			c = CIRC_SPACE_TO_END(info->xmit.head,
+					      info->xmit.tail,
+					      SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
+			if (c <= 0)
+				break;
 
-		if (from_user) {
 			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);
-		} else
-			memcpy(info->xmit_buf + info->xmit_head, buf, c);
-		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt += c;
+			cli();
+			c1 = CIRC_SPACE_TO_END(info->xmit.head,
+					       info->xmit.tail,
+					       SERIAL_XMIT_SIZE);
+			if (c1 < c)
+				c = c1;
+			memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
+			info->xmit.head = (info->xmit.head + c) & (SERIAL_XMIT_SIZE-1);
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		cli();
+		while (1) {
+			c = CIRC_SPACE_TO_END(info->xmit.head,
+					      info->xmit.tail,
+					      SERIAL_XMIT_SIZE);
+			if (count < c)
+				c = count;
+			if (c <= 0)
+				break;
+			memcpy(info->xmit.buf + info->xmit.head, buf, c);
+			info->xmit.head = (info->xmit.head + c) & (SERIAL_XMIT_SIZE-1);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
 		restore_flags(flags);
-		buf += c;
-		count -= c;
-		ret += c;
 	}
-	if (from_user)
-		up(&tmp_buf_sem);
 
-	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+	if ((info->xmit.head != info->xmit.tail) &&
+	    !tty->stopped && !tty->hw_stopped) {
 		info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
 		writeb(info->interrupt_mask1, &info->regs->w.imr1);
 		sab82532_start_tx(info);
@@ -1146,14 +1182,11 @@
 static int sab82532_write_room(struct tty_struct *tty)
 {
 	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-	int ret;
 
 	if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
 		return 0;
-	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
-	if (ret < 0)
-		ret = 0;
-	return ret;
+
+	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 }
 
 static int sab82532_chars_in_buffer(struct tty_struct *tty)
@@ -1162,18 +1195,22 @@
 				
 	if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
 		return 0;
-	return info->xmit_cnt;
+
+	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 }
 
 static void sab82532_flush_buffer(struct tty_struct *tty)
 {
 	struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
 		return;
-	cli();
-	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-	sti();
+
+	save_flags(flags); cli();
+	info->xmit.head = info->xmit.tail = 0;
+	restore_flags(flags);
+
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
@@ -1221,6 +1258,12 @@
 	
 	if (I_IXOFF(tty))
 		sab82532_send_xchar(tty, STOP_CHAR(tty));
+
+	if (tty->termios->c_cflag & CRTSCTS) {
+		u8 mode = readb(&info->regs->r.mode);
+		mode &= ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
+		writeb(mode, &info->regs->w.mode);
+	}
 }
 
 static void sab82532_unthrottle(struct tty_struct * tty)
@@ -1242,6 +1285,13 @@
 		else
 			sab82532_send_xchar(tty, START_CHAR(tty));
 	}
+
+	if (tty->termios->c_cflag & CRTSCTS) {
+		u8 mode = readb(&info->regs->r.mode);
+		mode &= ~(SAB82532_MODE_RTS);
+		mode |= SAB82532_MODE_FRTS;
+		writeb(mode, &info->regs->w.mode);
+	}
 }
 
 /*
@@ -1295,7 +1345,8 @@
 {
 	unsigned int result;
 
-	result = (!info->xmit_buf && info->all_sent) ? TIOCSER_TEMT : 0;
+	result = (!info->xmit.buf && test_bit(SAB82532_ALLS, &info->irqflags))
+							? TIOCSER_TEMT : 0;
 	return put_user(result, value);
 }
 
@@ -1317,12 +1368,10 @@
 static int set_modem_info(struct sab82532 * info, unsigned int cmd,
 			  unsigned int *value)
 {
-	int error;
 	unsigned int arg;
 
-	error = get_user(arg, value);
-	if (error)
-		return error;
+	if (get_user(arg, value))
+		return -EFAULT;
 	switch (cmd) {
 	case TIOCMBIS: 
 		if (arg & TIOCM_RTS) {
@@ -1391,7 +1440,6 @@
 static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
-	int error;
 	struct sab82532 * info = (struct sab82532 *)tty->driver_data;
 	struct async_icount cprev, cnow;	/* kernel counter temps */
 	struct serial_icounter_struct *p_cuser;	/* user space */
@@ -1411,9 +1459,8 @@
 		case TIOCGSOFTCAR:
 			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
 		case TIOCSSOFTCAR:
-			error = get_user(arg, (unsigned int *) arg);
-			if (error)
-				return error;
+			if (get_user(arg, (unsigned int *) arg))
+				return -EFAULT;
 			tty->termios->c_cflag =
 				((tty->termios->c_cflag & ~CLOCAL) |
 				 (arg ? CLOCAL : 0));
@@ -1483,14 +1530,11 @@
 			cnow = info->icount;
 			sti();
 			p_cuser = (struct serial_icounter_struct *) arg;
-			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;
+			if (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))
+				return -EFAULT;
 			return 0;
 
 		default:
@@ -1523,8 +1567,12 @@
 	if (!(old_termios->c_cflag & CBAUD) &&
 	    (tty->termios->c_cflag & CBAUD)) {
 		writeb(readb(&info->regs->w.pvr) & ~(info->pvr_dtr_bit), &info->regs->w.pvr);
-		if (!tty->hw_stopped ||
-		    !(tty->termios->c_cflag & CRTSCTS)) {
+		if (tty->termios->c_cflag & CRTSCTS) {
+			writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_RTS), &info->regs->w.mode);
+			writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
+		} else if (test_bit(TTY_THROTTLED, &tty->flags)) {
+			writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS), &info->regs->w.mode);
+		} else {
 			writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS), &info->regs->w.mode);
 			writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
 		}
@@ -1654,7 +1702,6 @@
 	if (serial_paranoia_check(info,tty->device,"sab82532_wait_until_sent"))
 		return;
 
-	orig_jiffies = jiffies;
 	/*
 	 * 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
@@ -1670,10 +1717,15 @@
 	if (timeout)
 	  char_time = MIN(char_time, timeout);
 #ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
-	printk("In sab82532_wait_until_sent(%d) check=%lu...", timeout, char_time);
-	printk("jiff=%lu...", jiffies);
+	printk("In sab82532_wait_until_sent(%d) check=%lu "
+	       "xmit_cnt = %ld, alls = %d (jiff=%lu)...\n",
+	       timeout, char_time,
+	       CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
+	       test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
 #endif
-	while (info->xmit_cnt || !info->all_sent) {
+	orig_jiffies = jiffies;
+	while ((info->xmit.head != info->xmit.tail) ||
+	       !test_bit(SAB82532_ALLS, &info->irqflags)) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(char_time);
 		if (signal_pending(current))
@@ -1682,7 +1734,9 @@
 			break;
 	}
 #ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
-	printk("xmit_cnt = %d, alls = %d (jiff=%lu)...done\n", info->xmit_cnt, info->all_sent, jiffies);
+	printk("xmit_cnt = %ld, alls = %d (jiff=%lu)...done\n",
+	       CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
+	       test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
 #endif
 }
 
@@ -2151,7 +2205,7 @@
 
 static inline void __init show_serial_version(void)
 {
-	char *revision = "$Revision: 1.58 $";
+	char *revision = "$Revision: 1.63 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)