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
- Lines: 509
- Date:
Fri Jun 29 19:38:26 2001
- Orig file:
v2.4.5/linux/drivers/sbus/char/sab82532.c
- Orig date:
Thu Apr 19 08:38:49 2001
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)