patch-2.4.18 linux/drivers/sbus/char/zs.c
Next file: linux/drivers/sbus/sbus.c
Previous file: linux/drivers/sbus/char/sunserial.c
Back to the patch index
Back to the overall index
- Lines: 442
- Date:
Thu Jan 17 19:37:52 2002
- Orig file:
linux.orig/drivers/sbus/char/zs.c
- Orig date:
Mon Feb 18 20:18:40 2002
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.68 2001/10/25 18:48:03 davem Exp $
+/* $Id: zs.c,v 1.68.2.2 2002/01/12 07:04:33 davem Exp $
* zs.c: Zilog serial port driver for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,10 @@
*
* Fixed to use tty_get_baud_rate().
* Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
+ *
+ * /proc/tty/driver/serial now exists and is readable.
+ * Alex Buell <alex.buell@tahallah.demon.co.uk>, 2001-12-23
+ *
*/
#include <linux/errno.h>
@@ -486,10 +490,19 @@
static void receive_chars(struct sun_serial *info, struct pt_regs *regs)
{
struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
- int do_queue_task = 1;
+ int do_queue_task = 0;
+
+ while (1) {
+ unsigned char ch, r1;
+
+ r1 = read_zsreg(info->zs_channel, R1);
+ if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+ sbus_writeb(ERR_RES, &info->zs_channel->control);
+ ZSDELAY();
+ ZS_WSYNC(info->zs_channel);
+ ZSLOG(REGCTRL, ERR_RES, 1);
+ }
- do {
ch = sbus_readb(&info->zs_channel->data);
ZSLOG(REGDATA, ch, 0);
ch &= info->parity_mask;
@@ -498,17 +511,17 @@
/* If this is the console keyboard, we need to handle
* L1-A's here.
*/
- if(info->cons_keyb) {
- if(ch == SUNKBD_RESET) {
+ if (info->cons_keyb) {
+ if (ch == SUNKBD_RESET) {
l1a_state.kbd_id = 1;
l1a_state.l1_down = 0;
- } else if(l1a_state.kbd_id) {
+ } else if (l1a_state.kbd_id) {
l1a_state.kbd_id = 0;
- } else if(ch == SUNKBD_L1) {
+ } else if (ch == SUNKBD_L1) {
l1a_state.l1_down = 1;
- } else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+ } else if (ch == (SUNKBD_L1|SUNKBD_UP)) {
l1a_state.l1_down = 0;
- } else if(ch == SUNKBD_A && l1a_state.l1_down) {
+ } else if (ch == SUNKBD_A && l1a_state.l1_down) {
/* whee... */
batten_down_hatches();
/* Continue execution... */
@@ -517,16 +530,14 @@
return;
}
sunkbd_inchar(ch, regs);
- do_queue_task = 0;
goto next_char;
}
- if(info->cons_mouse) {
+ if (info->cons_mouse) {
sun_mouse_inbyte(ch, 0);
- do_queue_task = 0;
goto next_char;
}
- if(info->is_cons) {
- if(ch == 0) {
+ if (info->is_cons) {
+ if (ch == 0) {
/* whee, break received */
batten_down_hatches();
/* Continue execution... */
@@ -540,32 +551,42 @@
* documentation for remote target debugging and
* arch/sparc/kernel/sparc-stub.c to see how all this works.
*/
- if((info->kgdb_channel) && (ch =='\003')) {
+ if (info->kgdb_channel && (ch =='\003')) {
breakpoint();
return;
}
#endif
- if(!tty)
+ if (!tty)
return;
+ do_queue_task++;
+
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
tty->flip.count++;
- *tty->flip.flag_buf_ptr++ = 0;
+ if (r1 & PAR_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+ else if (r1 & Rx_OVR)
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+ else if (r1 & CRC_ERR)
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+ else
+ *tty->flip.flag_buf_ptr++ = 0;
*tty->flip.char_buf_ptr++ = ch;
next_char:
- /* Check if we have another character... */
- stat = sbus_readb(&info->zs_channel->control);
- ZSDELAY();
- ZSLOG(REGCTRL, stat, 0);
- if (!(stat & Rx_CH_AV))
- break;
+ {
+ unsigned char stat;
- /* ... and see if it is clean. */
- stat = read_zsreg(info->zs_channel, R1);
- } while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
+ /* Check if we have another character... */
+ stat = sbus_readb(&info->zs_channel->control);
+ ZSDELAY();
+ ZSLOG(REGCTRL, stat, 0);
+ if (!(stat & Rx_CH_AV))
+ break;
+ }
+ }
if (do_queue_task != 0)
queue_task(&tty->flip.tqueue, &tq_timer);
@@ -582,7 +603,7 @@
return;
}
- if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
+ if ((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
/* That's peculiar... */
sbus_writeb(RES_Tx_P, &info->zs_channel->control);
ZSDELAY();
@@ -599,7 +620,7 @@
if (info->xmit_cnt < WAKEUP_CHARS)
zs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
- if(info->xmit_cnt <= 0) {
+ if (info->xmit_cnt <= 0) {
sbus_writeb(RES_Tx_P, &info->zs_channel->control);
ZSDELAY();
ZS_WSYNC(info->zs_channel);
@@ -621,14 +642,14 @@
ZS_WSYNC(info->zs_channel);
ZSLOG(REGCTRL, RES_EXT_INT, 1);
#if 0
- if(status & DCD) {
- if((info->tty->termios->c_cflag & CRTSCTS) &&
- ((info->curregs[3] & AUTO_ENAB)==0)) {
+ if (status & DCD) {
+ if ((info->tty->termios->c_cflag & CRTSCTS) &&
+ ((info->curregs[3] & AUTO_ENAB)==0)) {
info->curregs[3] |= AUTO_ENAB;
write_zsreg(info->zs_channel, 3, info->curregs[3]);
}
} else {
- if((info->curregs[3] & AUTO_ENAB)) {
+ if ((info->curregs[3] & AUTO_ENAB)) {
info->curregs[3] &= ~AUTO_ENAB;
write_zsreg(info->zs_channel, 3, info->curregs[3]);
}
@@ -638,7 +659,7 @@
* 'break asserted' status change interrupt, call
* the boot prom.
*/
- if(status & BRK_ABRT) {
+ if (status & BRK_ABRT) {
if (info->break_abort)
batten_down_hatches();
if (info->cons_mouse)
@@ -651,110 +672,49 @@
return;
}
-static void special_receive(struct sun_serial *info)
-{
- struct tty_struct *tty = info->tty;
- unsigned char ch, stat;
-
- stat = read_zsreg(info->zs_channel, R1);
- if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
- ch = sbus_readb(&info->zs_channel->data);
- ZSDELAY();
- ZSLOG(REGDATA, ch, 0);
- }
-
- if (!tty)
- goto clear;
-
- if (tty->flip.count >= TTY_FLIPBUF_SIZE)
- goto done;
-
- tty->flip.count++;
- if(stat & PAR_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_PARITY;
- else if(stat & Rx_OVR)
- *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
- else if(stat & CRC_ERR)
- *tty->flip.flag_buf_ptr++ = TTY_FRAME;
-
-done:
- queue_task(&tty->flip.tqueue, &tq_timer);
-clear:
- sbus_writeb(ERR_RES, &info->zs_channel->control);
- ZSDELAY();
- ZS_WSYNC(info->zs_channel);
- ZSLOG(REGCTRL, ERR_RES, 1);
-}
-
-
/*
* This is the serial driver's generic interrupt routine
*/
void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
struct sun_serial *info;
- unsigned char zs_intreg;
int i;
info = (struct sun_serial *)dev_id;
ZSLOG(REGIRQ, 0, 0);
for (i = 0; i < NUM_SERIAL; i++) {
- zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
- zs_intreg &= STATUS_MASK;
-
- /* NOTE: The read register 2, which holds the irq status,
- * does so for both channels on each chip. Although
- * the status value itself must be read from the B
- * channel and is only valid when read from channel B.
- * When read from channel A, read register 2 contains
- * the value written to write register 2.
- */
+ unsigned char r3 = read_zsreg(info->zs_channel, 3);
/* Channel A -- /dev/ttya or /dev/kbd, could be the console */
- if (zs_intreg == CHA_Rx_AVAIL) {
- receive_chars(info, regs);
- return;
- }
- if(zs_intreg == CHA_Tx_EMPTY) {
- transmit_chars(info);
- return;
- }
- if (zs_intreg == CHA_EXT_STAT) {
- status_handle(info);
- return;
- }
- if (zs_intreg == CHA_SPECIAL) {
- special_receive(info);
- return;
+ if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+ sbus_writeb(RES_H_IUS, &info->zs_channel->control);
+ ZSDELAY();
+ ZS_WSYNC(info->zs_channel);
+ ZSLOG(REGCTRL, RES_H_IUS, 1);
+ if (r3 & CHARxIP)
+ receive_chars(info, regs);
+ if (r3 & CHAEXT)
+ status_handle(info);
+ if (r3 & CHATxIP)
+ transmit_chars(info);
}
/* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
- if(zs_intreg == CHB_Rx_AVAIL) {
- receive_chars(info->zs_next, regs);
- return;
- }
- if(zs_intreg == CHB_Tx_EMPTY) {
- transmit_chars(info->zs_next);
- return;
- }
- if (zs_intreg == CHB_EXT_STAT) {
- status_handle(info->zs_next);
- return;
+ info = info->zs_next;
+ if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+ sbus_writeb(RES_H_IUS, &info->zs_channel->control);
+ ZSDELAY();
+ ZS_WSYNC(info->zs_channel);
+ ZSLOG(REGCTRL, RES_H_IUS, 1);
+ if (r3 & CHBRxIP)
+ receive_chars(info, regs);
+ if (r3 & CHBEXT)
+ status_handle(info);
+ if (r3 & CHBTxIP)
+ transmit_chars(info);
}
- /* NOTE: The default value for the IRQ status in read register
- * 2 in channel B is CHB_SPECIAL, so we need to look at
- * read register 3 in channel A to check if this is a
- * real interrupt, or just the default value.
- * Yes... broken hardware...
- */
-
- zs_intreg = read_zsreg(info->zs_channel, 3);
- if (zs_intreg & CHBRxIP) {
- special_receive(info->zs_next);
- return;
- }
- info = info->zs_next->zs_next;
+ info = info->zs_next;
}
}
@@ -1699,6 +1659,81 @@
}
/*
+ *
+ * line_info - returns information about each channel
+ *
+ */
+static inline int line_info(char *buf, struct sun_serial *info)
+{
+ unsigned char status;
+ char stat_buf[30];
+ int ret;
+
+ ret = sprintf(buf, "%d: uart:Zilog8530 port:%x irq:%d",
+ info->line, info->port, info->irq);
+
+ cli();
+ status = sbus_readb(&info->zs_channel->control);
+ ZSDELAY();
+ ZSLOG(REGCTRL, status, 0);
+ sti();
+
+ stat_buf[0] = 0;
+ stat_buf[1] = 0;
+ if (info->curregs[5] & RTS)
+ strcat(stat_buf, "|RTS");
+ if (status & CTS)
+ strcat(stat_buf, "|CTS");
+ if (info->curregs[5] & DTR)
+ strcat(stat_buf, "|DTR");
+ if (status & SYNC)
+ strcat(stat_buf, "|DSR");
+ if (status & DCD)
+ strcat(stat_buf, "|CD");
+
+ ret += sprintf(buf + ret, " baud:%d %s\n", info->zs_baud, stat_buf + 1);
+ return ret;
+}
+
+/*
+ *
+ * zs_read_proc() - called when /proc/tty/driver/serial is read.
+ *
+ */
+int zs_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ char *revision = "$Revision: 1.68.2.2 $";
+ char *version, *p;
+ int i, len = 0, l;
+ off_t begin = 0;
+
+ version = strchr(revision, ' ');
+ p = strchr(++version, ' ');
+ *p = '\0';
+ len += sprintf(page, "serinfo:1.0 driver:%s\n", version);
+ *p = ' ';
+
+ for (i = 0; i < NUM_CHANNELS && len < 4000; i++) {
+ l = line_info(page + len, &zs_soft[i]);
+ len += l;
+ if (len+begin > off+count)
+ goto done;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+
+ *eof = 1;
+done:
+ if (off >= len+begin)
+ return 0;
+ *start = page + (off-begin);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
* ------------------------------------------------------------
* zs_open() and friends
* ------------------------------------------------------------
@@ -1933,7 +1968,7 @@
static void show_serial_version(void)
{
- char *revision = "$Revision: 1.68 $";
+ char *revision = "$Revision: 1.68.2.2 $";
char *version, *p;
version = strchr(revision, ' ');
@@ -2444,8 +2479,8 @@
serial_driver.hangup = zs_hangup;
/* I'm too lazy, someone write versions of this for us. -DaveM */
- serial_driver.read_proc = 0;
- serial_driver.proc_entry = 0;
+ /* I just did. :-) -AIB 2001-12-23 */
+ serial_driver.read_proc = zs_read_proc;
/*
* The callout device is just like normal device except for
@@ -2455,6 +2490,8 @@
callout_driver.name = "cua/%d";
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");
@@ -2469,8 +2506,7 @@
/* Grab IRQ line before poking the chips so we do
* not lose any interrupts.
*/
- if (request_irq(zilog_irq, zs_interrupt,
- (SA_INTERRUPT | SA_STATIC_ALLOC),
+ if (request_irq(zilog_irq, zs_interrupt, SA_SHIRQ,
"Zilog8530", zs_chain)) {
prom_printf("Unable to attach zs intr\n");
prom_halt();
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)