patch-2.4.19 linux-2.4.19/drivers/net/wan/8253x/8253xtty.c
Next file: linux-2.4.19/drivers/net/wan/8253x/8253xutl.c
Previous file: linux-2.4.19/drivers/net/wan/8253x/8253xsyn.c
Back to the patch index
Back to the overall index
- Lines: 2926
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/net/wan/8253x/8253xtty.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN linux-2.4.18/drivers/net/wan/8253x/8253xtty.c linux-2.4.19/drivers/net/wan/8253x/8253xtty.c
@@ -0,0 +1,2925 @@
+/* -*- linux-c -*- */
+/* $Id: 8253xtty.c,v 1.23 2002/02/10 22:17:25 martillo Exp $
+ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+ *
+ * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Modified by Francois Wautier 2000 (fw@auroratech.com)
+ *
+ * Extended extensively by Joachim Martillo 2001 (Telford002@aol.com)
+ * to provide synchronous/asynchronous TTY/Callout/character/network device
+ * capabilities.
+ *
+ * Modifications and extensions
+ * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+
+/* Standard in kernel modules */
+#define DEFINE_VARIABLE
+#include <linux/module.h> /* Specifically, a module */
+#include <asm/io.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/version.h>
+#include <asm/uaccess.h>
+#include <linux/pci.h>
+#include "8253xctl.h"
+#include "sp502.h"
+
+DECLARE_TASK_QUEUE(tq_8253x_serial); /* this just initializes a list head called */
+ /* tq_8253x_serial*/
+
+struct tty_driver sab8253x_serial_driver, sab8253x_callout_driver, sync_sab8253x_serial_driver;
+int sab8253x_refcount;
+
+/* Trace things on serial device, useful for console debugging: */
+#undef SERIAL_LOG_DEVICE
+
+#ifdef SERIAL_LOG_DEVICE
+static void dprint_init(int tty);
+#endif
+
+static void sab8253x_change_speed(struct sab_port *port);
+
+static struct tty_struct **sab8253x_tableASY = 0; /* make dynamic */
+static struct tty_struct **sab8253x_tableCUA = 0; /* make dynamic */
+static struct tty_struct **sab8253x_tableSYN = 0; /* make dynamic */
+static struct termios **sab8253x_termios = 0 ;
+static struct termios **sab8253x_termios_locked = 0;
+
+#ifdef MODULE
+#undef XCONFIG_SERIAL_CONSOLE /* leaving out CONFIG_SERIAL_CONSOLE for now */
+#endif
+
+#ifdef XCONFIG_SERIAL_CONSOLE /* not really implemented yet */
+extern int serial_console;
+struct console sab8253x_console;
+int sab8253x_console_init(void);
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+char sab8253x_serial_version[16];
+
+static void sab8253x_flush_to_ldisc(void *private_)
+{
+ struct tty_struct *tty = (struct tty_struct *) private_;
+ unsigned char *cp;
+ char *fp;
+ int count;
+ struct sab_port *port;
+ struct sk_buff *skb;
+
+ if(tty)
+ {
+ port = (struct sab_port *)tty->driver_data; /* probably a silly check */
+ }
+ else
+ {
+ return;
+ }
+
+ if(!port)
+ {
+ return;
+ }
+
+ if (test_bit(TTY_DONT_FLIP, &tty->flags))
+ {
+ queue_task(&tty->flip.tqueue, &tq_timer);
+ return;
+ }
+ /* note that a hangup may have occurred -- perhaps should check for that */
+ port->DoingInterrupt = 1;
+ while(port->sab8253xc_rcvbuflist && (skb_queue_len(port->sab8253xc_rcvbuflist) > 0))
+ {
+ skb = skb_dequeue(port->sab8253xc_rcvbuflist);
+ count = skb->data_len;
+ cp = skb->data;
+ fp = skb->data + (count/2);
+ (*tty->ldisc.receive_buf)(tty, cp, fp, count/2);
+ dev_kfree_skb_any(skb);
+ }
+ port->DoingInterrupt = 0;
+}
+
+/* only used asynchronously */
+static void inline sab8253x_tec_wait(struct sab_port *port)
+{
+ int count = port->tec_timeout;
+
+ while((READB(port, star) & SAB82532_STAR_TEC) && --count)
+ {
+ udelay(1);
+ }
+}
+
+void sab8253x_start_tx(struct sab_port *port)
+{
+ unsigned long flags;
+ register int count;
+ register int total;
+ register int offset;
+ char temporary[32];
+ register unsigned int slopspace;
+ register int sendsize;
+ unsigned int totaltransmit;
+ unsigned fifospace;
+ unsigned loadedcount;
+ struct tty_struct *tty = port->tty;
+
+ fifospace = port->xmit_fifo_size;
+ loadedcount = 0;
+
+ if(port->sabnext2.transmit == NULL)
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+
+
+ while(count = port->sabnext2.transmit->Count, (count & OWNER) == OWN_SAB)
+ {
+ count &= ~OWN_SAB; /* OWN_SAB is really 0 but cannot guarantee in the future */
+
+ if(port->sabnext2.transmit->HostVaddr)
+ {
+ total = (port->sabnext2.transmit->HostVaddr->tail -
+ port->sabnext2.transmit->HostVaddr->data); /* packet size */
+ }
+ else
+ {
+ total = 0; /* the data is only in the crc/trailer */
+ }
+
+ if(tty && (tty->stopped || tty->hw_stopped))
+ { /* works for frame that only has a trailer (crc) */
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ restore_flags(flags); /* can't send */
+ return;
+ }
+
+ offset = (total - count); /* offset to data still to send */
+
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
+ WRITEB(port, imr1, port->interrupt_mask1);
+ port->all_sent = 0;
+
+
+ if(READB(port,star) & SAB82532_STAR_XFW)
+ {
+ if(count <= fifospace)
+ {
+ port->xmit_cnt = count;
+ slopspace = 0;
+ sendsize = 0;
+ if(port->sabnext2.transmit->sendcrc)
+ /* obviously should not happen for async but might use for
+ priority transmission */
+ {
+ slopspace = fifospace - count;
+ }
+ if(slopspace)
+ {
+ if(count)
+ {
+ memcpy(temporary, &port->sabnext2.transmit->HostVaddr->data[offset],
+ count);
+ }
+ sendsize = MIN(slopspace, (4 - port->sabnext2.transmit->crcindex));
+ /* how many bytes to send */
+ memcpy(&temporary[count],
+ &((unsigned char*)(&port->sabnext2.transmit->crc))
+ [port->sabnext2.transmit->crcindex],
+ sendsize);
+ port->sabnext2.transmit->crcindex += sendsize;
+ if(port->sabnext2.transmit->crcindex >= 4)
+ {
+ port->sabnext2.transmit->sendcrc = 0;
+ }
+ port->xmit_buf = temporary;
+ }
+ else
+ {
+ port->xmit_buf = /* set up wrifefifo variables */
+ &port->sabnext2.transmit->HostVaddr->data[offset];
+ }
+ port->xmit_cnt += sendsize;
+ count = 0;
+ }
+ else
+ {
+ count -= fifospace;
+ port->xmit_cnt = fifospace;
+ port->xmit_buf = /* set up wrifefifo variables */
+ &port->sabnext2.transmit->HostVaddr->data[offset];
+
+ }
+ port->xmit_tail= 0;
+ loadedcount = port->xmit_cnt;
+ (*port->writefifo)(port);
+ totaltransmit = Sab8253xCountTransmitDescriptors(port);
+ if((sab8253xt_listsize - totaltransmit) > 2)
+ {
+ sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
+ }
+
+ if((sab8253xt_listsize - totaltransmit) > (sab8253xt_listsize/2))
+ {
+ port->buffergreedy = 0;
+ }
+ else
+ {
+ port->buffergreedy = 1;
+ }
+
+ port->xmit_buf = NULL; /* this var is used to indicate whether to call kfree */
+
+ fifospace -= loadedcount;
+
+ if ((count <= 0) && (port->sabnext2.transmit->sendcrc == 0))
+ {
+ port->sabnext2.transmit->Count = OWN_DRIVER;
+#ifdef FREEININTERRUPT /* treat this routine as if taking place in interrupt */
+ if(port->sabnext2.transmit->HostVaddr)
+ {
+ skb_unlink(port->sabnext2.transmit->HostVaddr);
+ dev_kfree_skb_any(port->sabnext2.transmit->HostVaddr);
+ port->sabnext2.transmit->HostVaddr = 0; /* no skb */
+ }
+ port->sabnext2.transmit->crcindex = 0; /* no single byte */
+#endif
+ port->sabnext2.transmit = port->sabnext2.transmit->VNext;
+ if((port->sabnext2.transmit->Count & OWNER) == OWN_SAB)
+ {
+ if(fifospace > 0)
+ {
+ continue; /* the only place where this code really loops */
+ }
+ if(fifospace < 0)
+ {
+ printk(KERN_ALERT "sab8253x: bad math in interrupt handler.\n");
+ }
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ else
+ {
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ }
+ sab8253x_cec_wait(port);
+ /* Issue a Transmit Frame command. */
+ WRITEB(port, cmdr, SAB82532_CMDR_XF);
+ /* This could be optimized to load from next skbuff */
+ /* SAB82532_CMDR_XF is the same as SAB82532_CMDR_XTF */
+ restore_flags(flags);
+ return;
+ }
+ sab8253x_cec_wait(port);
+ /* Issue a Transmit Frame command. */
+ WRITEB(port, cmdr, SAB82532_CMDR_XF); /* same as SAB82532_CMDR_XTF */
+ port->sabnext2.transmit->Count = (count|OWN_SAB);
+ }
+ port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
+ WRITEB(port, imr1, port->interrupt_mask1);
+ restore_flags(flags);
+ return;
+ }
+ /* The While loop only exits via return*/
+ /* we get here by skipping the loop */
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ restore_flags(flags);
+ return;
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_stop() and sab8253x_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+
+static void sab8253x_stop(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_stop"))
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli(); /* maybe should turn off ALLS as well
+ but the stop flags are checked
+ so ALLS is probably harmless
+ and I have seen too much evil
+ associated with that interrupt*/
+ port->interrupt_mask1 |= SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ restore_flags(flags);
+}
+
+static void sab8253x_start(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_start"))
+ {
+ return;
+ }
+
+ sab8253x_start_tx(port);
+}
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+/* no obvious changes for sync tty */
+
+static void sab8253x_receive_chars(struct sab_port *port,
+ union sab8253x_irq_status *stat)
+{
+ struct tty_struct *tty = port->tty;
+ unsigned char buf[32];
+ unsigned char reordered[32];
+ unsigned char status;
+ int free_fifo = 0;
+ int i, count = 0;
+ struct sk_buff *skb;
+
+ /* Read number of BYTES (Character + Status) available. */
+ if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF)
+ {
+ count = port->recv_fifo_size;
+ free_fifo++;
+ }
+
+ if (stat->images[ISR0_IDX] & SAB82532_ISR0_TCD)
+ {
+ count = READB(port,rbcl) & (port->recv_fifo_size - 1);
+ free_fifo++;
+ }
+
+ /* Issue a FIFO read command in case we where idle. */
+ if (stat->sreg.isr0 & SAB82532_ISR0_TIME)
+ {
+ sab8253x_cec_wait(port);
+ WRITEB(port, cmdr, SAB82532_CMDR_RFRD);
+ }
+
+ if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO)
+ { /* FIFO overflow */
+ free_fifo++;
+ }
+
+ /* Read the FIFO. */
+ (*port->readfifo)(port, buf, count);
+
+ /* Issue Receive Message Complete command. */
+ if (free_fifo)
+ {
+ sab8253x_cec_wait(port);
+ WRITEB(port, cmdr, SAB82532_CMDR_RMC);
+ }
+
+#ifdef CONSOLE_SUPPORT
+ if (port->is_console)
+ {
+ wake_up(&keypress_wait);
+ }
+#endif
+ if (!tty)
+ {
+ return;
+ }
+
+ if(!count)
+ {
+ return;
+ }
+
+ for(i = 0; i < count; i += 2)
+ {
+ reordered[i/2] = buf[i];
+ status = buf[i+1];
+ if (status & SAB82532_RSTAT_PE)
+ {
+ status = TTY_PARITY;
+ port->icount.parity++;
+ }
+ else if (status & SAB82532_RSTAT_FE)
+ {
+ status = TTY_FRAME;
+ port->icount.frame++;
+ }
+ else
+ {
+ status = TTY_NORMAL;
+ }
+ reordered[(count+i)/2] = status;
+ }
+
+ if(port->active2.receive == NULL)
+ {
+ return;
+ }
+
+ memcpy(port->active2.receive->HostVaddr->tail, reordered, count);
+ port->active2.receive->HostVaddr->tail += count;
+ port->active2.receive->HostVaddr->data_len = count;
+ port->active2.receive->HostVaddr->len = count;
+ if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL) /* use dev_alloc_skb because at int
+ there is header space but so what*/
+ {
+ port->icount.buf_overrun++;
+ port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */
+ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+ port->active2.receive->HostVaddr->data_len = 0;
+ port->active2.receive->HostVaddr->len = 0;
+ }
+ else
+ {
+ skb_unlink(port->active2.receive->HostVaddr);
+ skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
+ skb_queue_head(port->sab8253xbuflist, skb);
+ port->active2.receive->HostVaddr = skb;
+ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+ }
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+static void sab8253x_transmit_chars(struct sab_port *port,
+ union sab8253x_irq_status *stat)
+{
+
+ if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) /* got an all sent int? */
+ {
+ port->interrupt_mask1 |= SAB82532_IMR1_ALLS;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ port->all_sent = 1; /* not much else to do */
+ } /* a very weird chip -- this int only indicates this int */
+
+ sab8253x_start_tx(port);
+}
+
+static void sab8253x_check_status(struct sab_port *port,
+ union sab8253x_irq_status *stat)
+{
+ struct tty_struct *tty = port->tty;
+ int modem_change = 0;
+ mctlsig_t *sig;
+ struct sk_buff *skb;
+
+ if (!tty)
+ {
+ return;
+ }
+
+ if(port->active2.receive == NULL)
+ {
+ goto check_modem;
+ }
+
+ if (stat->images[ISR1_IDX] & SAB82532_ISR1_BRK)
+ {
+#ifdef XCONFIG_SERIAL_CONSOLE
+ if (port->is_console)
+ {
+ batten_down_hatches(info); /* need to add this function */
+ return;
+ }
+#endif
+
+ port->active2.receive->HostVaddr->tail[0] = 0;
+ port->active2.receive->HostVaddr->tail[1] = TTY_PARITY;
+ port->active2.receive->HostVaddr->tail += 2;
+ port->active2.receive->HostVaddr->data_len = 2;
+ port->active2.receive->HostVaddr->len = 2;
+
+ if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL)
+ {
+ port->icount.buf_overrun++;
+ port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data;
+ /* clear the buffer */
+ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+ port->active2.receive->HostVaddr->data_len = 0;
+ port->active2.receive->HostVaddr->len = 0;
+ }
+ else
+ {
+ skb_unlink(port->active2.receive->HostVaddr);
+ skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
+ skb_queue_head(port->sab8253xbuflist, skb);
+ port->active2.receive->HostVaddr = skb;
+ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+ }
+ queue_task(&tty->flip.tqueue, &tq_timer);
+ port->icount.brk++;
+ }
+
+ if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO)
+ {
+ port->active2.receive->HostVaddr->tail[0] = 0;
+ port->active2.receive->HostVaddr->tail[1] = TTY_PARITY;
+ port->active2.receive->HostVaddr->tail += 2;
+ port->active2.receive->HostVaddr->data_len = 2;
+ port->active2.receive->HostVaddr->len = 2;
+ if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL)
+ {
+ port->icount.buf_overrun++;
+ port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data;
+ /* clear the buffer */
+ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+ port->active2.receive->HostVaddr->data_len = 0;
+ port->active2.receive->HostVaddr->len = 0;
+ }
+ else
+ {
+ skb_unlink(port->active2.receive->HostVaddr);
+ skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr);
+ skb_queue_head(port->sab8253xbuflist, skb);
+ port->active2.receive->HostVaddr = skb;
+ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB);
+ }
+ queue_task(&tty->flip.tqueue, &tq_timer);
+ port->icount.overrun++;
+ }
+
+ check_modem:
+ /* Checking DCD */
+ sig = &port->dcd;
+ if (stat->images[sig->irq] & sig->irqmask)
+ {
+ sig->val = ISON(port,dcd);
+ port->icount.dcd++;
+ modem_change++;
+ }
+ /* Checking CTS */
+ sig = &port->cts;
+ if (stat->images[sig->irq] & sig->irqmask)
+ {
+ sig->val = ISON(port,cts);
+ port->icount.cts++;
+ modem_change++;
+ }
+ /* Checking DSR */
+ sig = &port->dsr;
+ if (stat->images[sig->irq] & sig->irqmask)
+ {
+ sig->val = ISON(port,dsr);
+ port->icount.dsr++;
+ modem_change++;
+ }
+ if (modem_change)
+ {
+ wake_up_interruptible(&port->delta_msr_wait); /* incase kernel proc level was waiting on modem change */
+ }
+
+ sig = &port->dcd;
+ if ((port->flags & FLAG8253X_CHECK_CD) &&
+ (stat->images[sig->irq] & sig->irqmask))
+ {
+
+ if (sig->val)
+ {
+ wake_up_interruptible(&port->open_wait); /* in case waiting in block_til_ready */
+ }
+ else if (!((port->flags & FLAG8253X_CALLOUT_ACTIVE) &&
+ (port->flags & FLAG8253X_CALLOUT_NOHUP)))
+ {
+
+ MOD_INC_USE_COUNT; /* in case a close is already in progress
+ don't want structures to vanish during
+ late processing of hangup */
+ if (schedule_task(&port->tqueue_hangup) == 0)
+ {
+ MOD_DEC_USE_COUNT; /* task schedule failed */
+ }
+ }
+ }
+
+ sig = &port->cts;
+ if (port->flags & FLAG8253X_CTS_FLOW)
+ {
+ if (port->tty->hw_stopped)
+ {
+ if (sig->val)
+ {
+
+ port->tty->hw_stopped = 0;
+ sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP);
+ sab8253x_start_tx(port);
+ }
+ }
+ else
+ {
+ if (!(sig->val))
+ {
+ port->tty->hw_stopped = 1;
+ }
+ }
+ }
+}
+
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * sab8253x_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using sab8253x_sched_event(), and they get done here.
+ */
+ /* The following routine is installed */
+ /* in the bottom half -- just search */
+ /* for the init_bh() call */
+ /* The logic: sab8253x_sched_event() */
+ /* enqueues the tqueue port entry on */
+ /* the tq_8253x_serial task list -- */
+ /* whenever the bottom half is run */
+ /* sab8253x_do_softint is invoked for */
+ /* every port that has invoked the bottom */
+ /* half via sab8253x_sched_event(). */
+ /* currently only a write wakeevent */
+ /* wakeup is scheduled -- to tell the */
+ /* tty driver to send more chars */
+ /* down to the serial driver.*/
+
+static void sab8253x_do_serial_bh(void)
+{
+ run_task_queue(&tq_8253x_serial);
+}
+
+ /* I believe the reason for the */
+ /* bottom half processing below is */
+ /* the length of time needed to transfer */
+ /* characters to the TTY driver. */
+
+static void sab8253x_do_softint(void *private_)
+{
+ struct sab_port *port = (struct sab_port *)private_;
+ struct tty_struct *tty;
+
+ tty = port->tty;
+ if (!tty)
+ {
+ return;
+ }
+
+ port->DoingInterrupt = 1;
+ if (test_and_clear_bit(SAB8253X_EVENT_WRITE_WAKEUP, &port->event))
+ {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait); /* in case tty driver waiting on write */
+ }
+ port->DoingInterrupt = 0;
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_serial_hangup() -> tty->hangup() -> sab8253x_hangup()
+ *
+ */
+/* This logic takes place at kernel */
+/* process context through the scheduler*/
+/* schedule_task(tqueue_hangup) */
+/* takes place in the interrupt handler*/
+static void sab8253x_do_serial_hangup(void *private_)
+{
+ struct sab_port *port = (struct sab_port *) private_;
+ struct tty_struct *tty;
+
+ tty = port->tty;
+ if (tty)
+ {
+ tty_hangup(tty);
+ }
+ MOD_DEC_USE_COUNT; /* in case busy waiting to unload module */
+}
+
+static void
+sab8253x_init_line(struct sab_port *port)
+{
+ unsigned char stat;
+
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ aura_sp502_program(port, SP502_OFF_MODE);
+ }
+ }
+
+ /*
+ * Wait for any commands or immediate characters
+ */
+ sab8253x_cec_wait(port);
+ sab8253x_tec_wait(port);
+
+ /*
+ * Clear the FIFO buffers.
+ */
+
+ WRITEB(port, cmdr, SAB82532_CMDR_RRES);
+ sab8253x_cec_wait(port);
+ WRITEB(port, cmdr, SAB82532_CMDR_XRES);
+
+
+ /*
+ * Clear the interrupt registers.
+ */
+ stat = READB(port, isr0);
+ stat = READB(port, isr1);
+
+ /*
+ * Now, initialize the UART
+ */
+ WRITEB(port, ccr0, 0); /* power-down */
+ WRITEB(port, ccr0,
+ SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ | SAB82532_CCR0_SM_ASYNC);
+ WRITEB(port, ccr1,
+ SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7);
+ WRITEB(port, ccr2,
+ SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL | SAB82532_CCR2_TOE);
+ WRITEB(port, ccr3, 0);
+ WRITEB(port, ccr4,
+ SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG);
+ WRITEB(port, mode,
+ SAB82532_MODE_RTS | SAB82532_MODE_FCTS | SAB82532_MODE_RAC);
+ WRITEB(port, rfc,
+ SAB82532_RFC_DPS | SAB82532_RFC_RFDF);
+ switch (port->recv_fifo_size)
+ {
+ case 1:
+ SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_1);
+ break;
+ case 4:
+ SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_4);
+ break;
+ case 16:
+ SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_16);
+ break;
+ default:
+ port->recv_fifo_size = 32;
+ case 32:
+ SET_REG_BIT(port,rfc,SAB82532_RFC_RFTH_32);
+ break;
+ }
+ /* power-up */
+ SET_REG_BIT(port, ccr0, SAB82532_CCR0_PU);
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ aura_sp502_program(port, port->sigmode);
+ }
+ }
+}
+
+
+static int sab8253x_startup(struct sab_port *port)
+{
+ unsigned long flags;
+
+ int retval = 0;
+
+ save_flags(flags); cli();
+
+ if (port->flags & FLAG8253X_INITIALIZED)
+ {
+ goto errout;
+ }
+
+ port->msgbufindex = 0;
+ port->xmit_buf = NULL;
+ port->buffergreedy = 0;
+
+ if (!port->regs)
+ {
+ if (port->tty)
+ {
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
+ }
+ retval = -ENODEV;
+ goto errout;
+ }
+
+ /*
+ * Initialize the Hardware
+ */
+ sab8253x_init_line(port);
+
+ if (port->tty->termios->c_cflag & CBAUD)
+ {
+ /* Activate RTS */
+ RAISE(port,rts);
+
+ /* Activate DTR */
+ RAISE(port,dtr);
+ }
+
+ /*
+ * Initialize the modem signals values
+ */
+ port->dcd.val=ISON(port,dcd);
+ port->cts.val=ISON(port,cts);
+ port->dsr.val=ISON(port,dsr);
+
+ /*
+ * Finally, enable interrupts
+ */
+
+ port->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+ SAB82532_IMR0_PLLA;
+ WRITEB(port, imr0, port->interrupt_mask0);
+ port->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_XOFF |
+ SAB82532_IMR1_TIN | SAB82532_IMR1_XON |
+ SAB82532_IMR1_XPR;
+ WRITEB(port, imr1, port->interrupt_mask1);
+ port->all_sent = 1;
+
+ if (port->tty)
+ {
+ clear_bit(TTY_IO_ERROR, &port->tty->flags);
+ }
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+
+ /*
+ * and set the speed of the serial port
+ */
+ sab8253x_change_speed(port);
+
+ port->flags |= FLAG8253X_INITIALIZED;
+ port->receive_chars = sab8253x_receive_chars;
+ port->transmit_chars = sab8253x_transmit_chars;
+ port->check_status = sab8253x_check_status;
+ port->receive_test = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
+ port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR);
+ port->check_status_test = SAB82532_ISR1_BRK;
+
+ restore_flags(flags);
+ return 0;
+
+ errout:
+ restore_flags(flags);
+ return retval;
+}
+
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void sab8253x_shutdown(struct sab_port *port)
+{
+ unsigned long flags;
+
+ if (!(port->flags & FLAG8253X_INITIALIZED))
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+ wake_up_interruptible(&port->delta_msr_wait); /* shutting down port modem status is pointless */
+
+ if (port->xmit_buf)
+ {
+ port->xmit_buf = NULL;
+ }
+
+#ifdef XCONFIG_SERIAL_CONSOLE
+ if (port->is_console)
+ {
+ port->interrupt_mask0 =
+ SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+ /*SAB82532_IMR0_TIME |*/
+ SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
+ WRITEB(port,imr0,port->interrupt_mask0);
+ port->interrupt_mask1 =
+ SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+ SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+ SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+ SAB82532_IMR1_XPR;
+ WRITEB(port,imr1,port->interrupt_mask1);
+ if (port->tty)
+ {
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
+ }
+ port->flags &= ~FLAG8253X_INITIALIZED;
+ restore_flags(flags);
+ return;
+ }
+#endif
+
+ /* Disable Interrupts */
+
+ port->interrupt_mask0 = 0xff;
+ WRITEB(port, imr0, port->interrupt_mask0);
+ port->interrupt_mask1 = 0xff;
+ WRITEB(port, imr1, port->interrupt_mask1);
+
+ if (!port->tty || (port->tty->termios->c_cflag & HUPCL))
+ {
+ LOWER(port,rts);
+ LOWER(port,dtr);
+ }
+
+ /* Disable break condition */
+ CLEAR_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
+
+ /* Disable Receiver */
+ CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC);
+
+ /* Power Down */
+ CLEAR_REG_BIT(port,ccr0,SAB82532_CCR0_PU);
+
+ if (port->tty)
+ {
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
+ }
+
+ port->flags &= ~FLAG8253X_INITIALIZED;
+ restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void sab8253x_change_speed(struct sab_port *port)
+{
+ unsigned long flags,baud;
+ tcflag_t cflag;
+ u8 dafo,ccr2=0,ccr4=0,ebrg=0,mode;
+ int i, bits;
+#ifdef DEBUGGING
+ printk("Change speed! ");
+#endif
+ if (!port->tty || !port->tty->termios)
+ {
+#ifdef DEBUGGING
+ printk("NOT!\n");
+#endif
+ return;
+ }
+
+#ifdef DEBUGGING
+ printk(" for real.\n");
+#endif
+
+ cflag = port->tty->termios->c_cflag;
+
+ /* Byte size and parity */
+ switch (cflag & CSIZE)
+ {
+ case CS5:
+ dafo = SAB82532_DAFO_CHL5;
+ bits = 7;
+ break;
+ case CS6:
+ dafo = SAB82532_DAFO_CHL6;
+ bits = 8;
+ break;
+ case CS7:
+ dafo = SAB82532_DAFO_CHL7;
+ bits = 9;
+ break;
+ default:
+ case CS8:
+ dafo = SAB82532_DAFO_CHL8;
+ bits = 10;
+ break;
+ }
+
+ if (cflag & CSTOPB)
+ {
+ dafo |= SAB82532_DAFO_STOP;
+ bits++;
+ }
+
+ if (cflag & PARENB)
+ {
+ dafo |= SAB82532_DAFO_PARE;
+ bits++;
+ }
+
+ if (cflag & PARODD)
+ {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_MARK;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_ODD;
+ }
+ else
+ {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_SPACE;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_EVEN;
+ }
+
+ /* Determine EBRG values based on the "encoded"baud rate */
+ i = cflag & CBAUD;
+ switch(i)
+ {
+ case B0:
+ baud=0;
+ break;
+ case B50:
+ baud=100;
+ break;
+ case B75:
+ baud=150;
+ break;
+ case B110:
+ baud=220;
+ break;
+ case B134:
+ baud=269;
+ break;
+ case B150:
+ baud=300;
+ break;
+ case B200:
+ baud=400;
+ break;
+ case B300:
+ baud=600;
+ break;
+ case B600:
+ baud=1200;
+ break;
+ case B1200:
+ baud=2400;
+ break;
+ case B1800:
+ baud=3600;
+ break;
+ case B2400:
+ baud=4800;
+ break;
+ case B4800:
+ baud=9600;
+ break;
+ case B9600:
+ baud=19200;
+ break;
+ case B19200:
+ baud=38400;
+ break;
+ case B38400:
+ if(port->custspeed)
+ {
+ baud=port->custspeed<<1;
+ }
+ else
+ {
+ baud=76800;
+ }
+ break;
+ case B57600:
+ baud=115200;
+ break;
+#ifdef SKIPTHIS
+ case B76800:
+ baud=153600;
+ break;
+ case B153600:
+ baud=307200;
+ break;
+#endif
+ case B230400:
+ baud=460800;
+ break;
+ case B460800:
+ baud=921600;
+ break;
+ case B115200:
+ default:
+ baud=230400;
+ break;
+ }
+
+ if(!sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud)))
+ {
+ printk("Aurora Warning. baudrate %ld could not be set! Using 115200",baud);
+ baud=230400;
+ sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud));
+ }
+
+ if (port->baud)
+ port->timeout = (port->xmit_fifo_size * HZ * bits) / port->baud;
+ else
+ port->timeout = 0;
+ port->timeout += HZ / 50; /* Add .02 seconds of slop */
+
+ /* CTS flow control flags */
+ if (cflag & CRTSCTS)
+ port->flags |= FLAG8253X_CTS_FLOW;
+ else
+ port->flags &= ~(FLAG8253X_CTS_FLOW);
+
+ if (cflag & CLOCAL)
+ port->flags &= ~(FLAG8253X_CHECK_CD);
+ else
+ port->flags |= FLAG8253X_CHECK_CD;
+ if (port->tty)
+ port->tty->hw_stopped = 0;
+
+ /*
+ * Set up parity check flag
+ * XXX: not implemented, yet.
+ */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ /*
+ * Characters to ignore
+ * XXX: not implemented, yet.
+ */
+
+ /*
+ * !!! ignore all characters if CREAD is not set
+ * XXX: not implemented, yet.
+ */
+ if ((cflag & CREAD) == 0)
+ port->ignore_status_mask |= SAB82532_ISR0_RPF |
+ /* SAB82532_ISR0_TIME |*/
+ SAB82532_ISR0_TCD ;
+
+ save_flags(flags);
+ cli();
+ sab8253x_cec_wait(port);
+ sab8253x_tec_wait(port);
+ WRITEB(port,dafo,dafo);
+ WRITEB(port,bgr,ebrg);
+ ccr2 |= READB(port,ccr2) & ~(0xc0);
+ WRITEB(port,ccr2,ccr2);
+ ccr4 |= READB(port,ccr4) & ~(SAB82532_CCR4_EBRG);
+ WRITEB(port,ccr4,ccr4);
+
+ if (port->flags & FLAG8253X_CTS_FLOW)
+ {
+ mode = READB(port,mode) & ~(SAB82532_MODE_RTS);
+ mode |= SAB82532_MODE_FRTS;
+ mode &= ~(SAB82532_MODE_FCTS);
+ }
+ else
+ {
+ mode = READB(port,mode) & ~(SAB82532_MODE_FRTS);
+ mode |= SAB82532_MODE_RTS;
+ mode |= SAB82532_MODE_FCTS;
+ }
+ WRITEB(port,mode,mode);
+ mode |= SAB82532_MODE_RAC;
+ WRITEB(port,mode,mode);
+ restore_flags(flags);
+}
+
+static void sab8253x_flush_chars(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_flush_chars"))
+ {
+ return;
+ }
+
+ if ((Sab8253xCountTransmit(port) <= 0) || tty->stopped || tty->hw_stopped)
+ {
+ return;
+ }
+
+ sab8253x_start_tx(port);
+}
+
+static int sab8253x_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ struct sk_buff *skb = NULL;
+ int truelength = 0;
+ int do_queue = 1;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_write"))
+ {
+ return 0;
+ }
+
+ if(count == 0)
+ {
+ return 0;
+ }
+
+ if(port->active2.transmit == NULL)
+ {
+ return 0;
+ }
+
+ if((port->active2.transmit->Count & OWNER) == OWN_SAB)
+ {
+ sab8253x_start_tx(port);
+ return 0;
+ }
+
+#ifndef FREEININTERRUPT
+ skb = port->active2.transmit->HostVaddr; /* current slot value */
+
+ if(port->buffergreedy == 0) /* are we avoiding buffer free's */
+ { /* no */
+ if((skb != NULL) || /* not OWN_SAB from above */
+ (port->active2.transmit->crcindex != 0))
+ {
+ register RING_DESCRIPTOR *freeme;
+
+ freeme = port->active2.transmit;
+ do
+ {
+ if((freeme->crcindex == 0) && (freeme->HostVaddr == NULL))
+ {
+ break;
+ }
+ if(freeme->HostVaddr)
+ {
+ skb_unlink((struct sk_buff*)freeme->HostVaddr);
+ dev_kfree_skb_any((struct sk_buff*)freeme->HostVaddr);
+ freeme->HostVaddr = NULL;
+ }
+ freeme->sendcrc = 0;
+ freeme->crcindex = 0;
+ freeme = (RING_DESCRIPTOR*) freeme->VNext;
+ }
+ while((freeme->Count & OWNER) != OWN_SAB);
+ }
+ skb = NULL; /* buffer was freed */
+ }
+
+ if(skb != NULL) /* potentially useful */
+ {
+ truelength = (skb->end - skb->head);
+ if(truelength >= count)
+ {
+ skb->data = skb->head; /* this buffer is already queued */
+ skb->tail = skb->head;
+ do_queue = 0;
+ }
+ else
+ {
+ skb_unlink(skb);
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+ port->active2.transmit->HostVaddr = NULL;
+ }
+ }
+ /* in all cases the following is allowed */
+ port->active2.transmit->sendcrc = 0;
+ port->active2.transmit->crcindex = 0;
+#endif
+
+ if(skb == NULL)
+ {
+ if(port->DoingInterrupt)
+ {
+ skb = alloc_skb(count, GFP_ATOMIC);
+ }
+ else
+ {
+ skb = alloc_skb(count, GFP_KERNEL);
+ }
+ }
+
+ if(skb == NULL)
+ {
+ printk(KERN_ALERT "sab8253xt: no skbuffs available.\n");
+ return 0;
+ }
+ if(from_user)
+ {
+ copy_from_user(skb->data, buf, count);
+ }
+ else
+ {
+ memcpy(skb->data, buf, count);
+ }
+ skb->tail = (skb->data + count);
+ skb->data_len = count;
+ skb->len = count;
+
+ if(do_queue)
+ {
+ skb_queue_head(port->sab8253xbuflist, skb);
+ }
+
+ port->active2.transmit->HostVaddr = skb;
+ port->active2.transmit->sendcrc = 0;
+ port->active2.transmit->crcindex = 0;
+ port->active2.transmit->Count = (OWN_SAB|count);
+ port->active2.transmit = port->active2.transmit->VNext;
+
+ sab8253x_start_tx(port);
+ return count;
+}
+
+static int sab8253x_write_room(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if(sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_write_room"))
+ {
+ return 0;
+ }
+
+ if(port->active2.transmit == NULL)
+ {
+ return 0;
+ }
+
+ if((port->active2.transmit->Count & OWNER) == OWN_SAB)
+ {
+ return 0;
+ }
+ return ((sab8253xt_rbufsize) * /* really should not send buffs bigger than 32 I guess */
+ (sab8253xt_listsize -
+ Sab8253xCountTransmitDescriptors(port)));
+}
+
+static int sab8253x_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_chars_in_bufferS"))
+ {
+ return 0;
+ }
+
+ return Sab8253xCountTransmit(port);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void sab8253x_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_send_xchar"))
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ sab8253x_tec_wait(port);
+ WRITEB(port, tic, ch);
+ restore_flags(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void sab8253x_throttle(struct tty_struct * tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_throttle"))
+ {
+ return;
+ }
+
+ if (I_IXOFF(tty))
+ {
+ sab8253x_send_xchar(tty, STOP_CHAR(tty));
+ }
+}
+
+static void sab8253x_unthrottle(struct tty_struct * tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_unthrottle"))
+ {
+ return;
+ }
+
+ if (I_IXOFF(tty))
+ {
+ if (port->x_char)
+ {
+ port->x_char = 0;
+ }
+ else
+ {
+ sab8253x_send_xchar(tty, START_CHAR(tty));
+ }
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int sab8253x_get_serial_info(struct sab_port *port,
+ struct serial_struct *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (!retinfo)
+ {
+ return -EFAULT;
+ }
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = port->type;
+ tmp.line = port->line;
+ tmp.port = (unsigned long)port->regs;
+ tmp.irq = port->irq;
+ tmp.flags = port->flags;
+ tmp.xmit_fifo_size = port->xmit_fifo_size;
+ tmp.baud_base = 0;
+ tmp.close_delay = port->close_delay;
+ tmp.closing_wait = port->closing_wait;
+ tmp.custom_divisor = port->custom_divisor;
+ tmp.hub6 = 0;
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ {
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int sab8253x_set_serial_info(struct sab_port *port,
+ struct serial_struct *new_info)
+{
+ return 0;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ */
+static int sab8253x_get_lsr_info(struct sab_port * port, unsigned int *value)
+{
+ unsigned int result;
+
+ result = (((Sab8253xCountTransmit(port) <= 0) && port->all_sent) ? TIOCSER_TEMT : 0);
+ return put_user(result, value);
+}
+
+
+static int sab8253x_get_modem_info(struct sab_port * port, unsigned int *value)
+{
+ unsigned int result;
+
+ /* Using the cached values !! After all when changed int occurs
+ and the cache is updated */
+ result=
+ ((port->dtr.val) ? TIOCM_DTR : 0)
+ | ((port->rts.val) ? TIOCM_RTS : 0)
+ | ((port->cts.val) ? TIOCM_CTS : 0)
+ | ((port->dsr.val) ? TIOCM_DSR : 0)
+ | ((port->dcd.val) ? TIOCM_CAR : 0);
+
+ return put_user(result,value);
+}
+
+static int sab8253x_set_modem_info(struct sab_port * port, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+ unsigned long flags;
+
+ error = get_user(arg, value);
+ if (error)
+ {
+ return error;
+ }
+
+ save_flags(flags);
+ cli();
+ switch (cmd)
+ {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ {
+ RAISE(port, rts);
+ }
+ if (arg & TIOCM_DTR)
+ {
+ RAISE(port, dtr);
+ }
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ {
+ LOWER(port,rts);
+ }
+ if (arg & TIOCM_DTR)
+ {
+ LOWER(port,dtr);
+ }
+ break;
+ case TIOCMSET:
+ if (arg & TIOCM_RTS)
+ {
+ RAISE(port, rts);
+ }
+ else
+ {
+ LOWER(port,rts);
+ }
+ if (arg & TIOCM_DTR)
+ {
+ RAISE(port, dtr);
+ }
+ else
+ {
+ LOWER(port,dtr);
+ }
+ break;
+ default:
+ restore_flags(flags);
+ return -EINVAL;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void sab8253x_break(struct tty_struct *tty, int break_state)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_break"))
+ {
+ return;
+ }
+
+ if (!port->regs)
+ {
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ if (break_state == -1)
+ {
+ SET_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
+ }
+ else
+ {
+ CLEAR_REG_BIT(port,dafo,SAB82532_DAFO_XBRK);
+ }
+ restore_flags(flags);
+}
+
+static int sab8253x_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error;
+ unsigned int wordindex;
+ unsigned short *wordptr;
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ struct async_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct *p_cuser; /* user space */
+ SAB_BOARD *bptr;
+ unsigned long flags;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_ioctl"))
+ {
+ return -ENODEV;
+ }
+
+ if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+ (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
+ (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+ (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT))
+ {
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ {
+ return -EIO;
+ }
+ }
+
+ switch (cmd)
+ {
+ case ATIS_IOCSPARAMS:
+ copy_from_user(&port->ccontrol, (struct channelcontrol*)arg , sizeof(struct channelcontrol));
+ break;
+ case ATIS_IOCGPARAMS:
+ copy_to_user((struct channelcontrol*) arg, &port->ccontrol, sizeof(struct channelcontrol));
+ break;
+
+ case ATIS_IOCSSPEED:
+ copy_from_user(&port->custspeed, (unsigned long*)arg , sizeof(unsigned long));
+ break;
+ case ATIS_IOCGSPEED:
+ copy_to_user((unsigned long*) arg, &port->custspeed, sizeof(unsigned long));
+ break;
+
+ case ATIS_IOCSSEP9050:
+ bptr = port->board;
+ if(bptr->b_type == BD_WANMCS)
+ {
+ return -EINVAL;
+ }
+ copy_from_user((unsigned char*) bptr->b_eprom, (unsigned char*) arg , sizeof(struct sep9050));
+
+ wordptr = (unsigned short*) bptr->b_eprom;
+ plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
+ NM93_WENCMD, NM93_WENADDR, 0);
+ for(wordindex = 0; wordindex < EPROM9050_SIZE; ++wordindex)
+ {
+ plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
+ NM93_WRITECMD,
+ wordindex, wordptr[wordindex]);
+ }
+ plx9050_eprom_cmd(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
+ NM93_WDSCMD, NM93_WDSADDR, 0);
+ break;
+ case ATIS_IOCGSEP9050:
+ bptr = port->board;
+ if(bptr->b_type == BD_WANMCS)
+ {
+ return -EINVAL;
+ }
+ if (!plx9050_eprom_read(&((PLX9050*)(bptr->virtbaseaddress0))->ctrl,
+ (unsigned short*) bptr->b_eprom,
+ (unsigned char) 0, EPROM9050_SIZE))
+ {
+ printk(KERN_ALERT "auraXX20n: Could not read serial eprom.\n");
+ return -EIO;
+ }
+ copy_to_user((unsigned char*) arg, (unsigned char*) bptr->b_eprom, sizeof(struct sep9050));
+ break;
+
+ 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;
+ }
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ return sab8253x_get_modem_info(port, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return sab8253x_set_modem_info(port, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return sab8253x_get_serial_info(port,
+ (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return sab8253x_set_serial_info(port,
+ (struct serial_struct *) arg);
+
+ case TIOCSERGETLSR: /* Get line status register */
+ return sab8253x_get_lsr_info(port, (unsigned int *) arg);
+
+ case TIOCSERGSTRUCT:
+ if (copy_to_user((struct sab_port *) arg,
+ port, sizeof(struct sab_port)))
+ return -EFAULT;
+ return 0;
+
+ /*
+ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ */
+ case TIOCMIWAIT:
+ save_flags(flags);
+ cli();
+ /* note the counters on entry */
+ cprev = port->icount;
+ restore_flags(flags);
+ while (1)
+ {
+ interruptible_sleep_on(&port->delta_msr_wait); /* waits for a modem signal change */
+ /* see if a signal did it */
+ if (signal_pending(current))
+ {
+ return -ERESTARTSYS;
+ }
+ save_flags(flags);
+ cli();
+ cnow = port->icount; /* atomic copy */
+ restore_flags(flags);
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ {
+ return -EIO; /* no change => error */
+ }
+ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+ {
+ return 0;
+ }
+ }
+ cprev = cnow;
+ }
+ /* NOTREACHED */
+ break;
+
+ /*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ * RI where only 0->1 is counted.
+ */
+ case TIOCGICOUNT:
+ save_flags(flags);
+ cli();
+ cnow = port->icount;
+ restore_flags(flags);
+ 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;
+ }
+ return 0;
+
+ case ATIS_IOCSSIGMODE:
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ copy_from_user(&port->sigmode, (unsigned int*)arg , sizeof(unsigned int));
+ return 0;
+ }
+ }
+ return -EINVAL;
+
+ case ATIS_IOCGSIGMODE:
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ copy_to_user((unsigned int*) arg, &port->sigmode, sizeof(unsigned int));
+ return 0;
+ }
+ }
+ return -EINVAL;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void sab8253x_set_termios(struct tty_struct *tty,
+ struct termios *old_termios)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if((tty->termios->c_cflag == old_termios->c_cflag) &&
+ (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag)))
+ {
+ return;
+ }
+ if(!port)
+ {
+ return;
+ }
+ sab8253x_change_speed(port);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) &&
+ !(tty->termios->c_cflag & CBAUD))
+ {
+ LOWER(port,rts);
+ LOWER(port,dtr);
+ }
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) &&
+ (tty->termios->c_cflag & CBAUD))
+ {
+ RAISE(port, dtr);
+ if (!tty->hw_stopped ||
+ !(tty->termios->c_cflag & CRTSCTS))
+ {
+ RAISE(port, rts);
+ }
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS))
+ {
+ tty->hw_stopped = 0;
+ sab8253x_start(tty);
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * sab8253x_close()
+ *
+ * This routine is called when the serial port gets closed. First, we
+ * wait for the last remaining data to be sent. Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void sab8253x_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+ unsigned long flags;
+
+ MOD_DEC_USE_COUNT;
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_close"))
+ {
+ return;
+ }
+ if(port->open_type == OPEN_SYNC_NET)
+ { /* port->tty field should already be NULL */
+ /* port count was not incremented */
+ return;
+ }
+
+ --(port->count); /* have a valid port */
+ if (tty_hung_up_p(filp))
+ {
+
+ if(port->count == 0) /* shutdown took place in hangup context */
+ {
+ port->open_type = OPEN_NOT;
+ }
+ else if(port->count < 0)
+ {
+ printk(KERN_ALERT "XX20: port->count went negative.\n");
+ port->count = 0;
+ port->open_type = OPEN_NOT;
+ }
+ return;
+ }
+
+ if (port->count < 0)
+ {
+ printk(KERN_ALERT "sab8253x_close: bad serial port count for ttys%d: %d\n",
+ port->line, port->count);
+ port->count = 0;
+ }
+
+ if (port->count)
+ {
+ return;
+ }
+
+ port->flags |= FLAG8253X_CLOSING;
+
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (port->flags & FLAG8253X_NORMAL_ACTIVE)
+ {
+ port->normal_termios = *tty->termios;
+ }
+ if (port->flags & FLAG8253X_CALLOUT_ACTIVE)
+ {
+ port->callout_termios = *tty->termios;
+ }
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (port->closing_wait != SAB8253X_CLOSING_WAIT_NONE)
+ {
+ tty_wait_until_sent(tty, port->closing_wait); /* wait for drain */
+ }
+
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and turn off
+ * the receiver.
+ */
+
+ save_flags(flags);
+ cli();
+ port->interrupt_mask0 |= SAB82532_IMR0_TCD;
+ WRITEB(port,imr0,port->interrupt_mask0);
+
+ CLEAR_REG_BIT(port,mode,SAB82532_MODE_RAC); /* ??????? */
+ restore_flags(flags);
+
+ if (port->flags & FLAG8253X_INITIALIZED)
+ {
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ sab8253x_wait_until_sent(tty, port->timeout);
+ }
+ sab8253x_shutdown(port); /* no more ints on port */
+ Sab8253xCleanUpTransceiveN(port); /* should be okay */
+ if (tty->driver.flush_buffer)
+ {
+ tty->driver.flush_buffer(tty);
+ }
+ if (tty->ldisc.flush_buffer)
+ {
+ tty->ldisc.flush_buffer(tty);
+ }
+ tty->closing = 0;
+ port->event = 0;
+ port->tty = 0;
+ if (port->blocked_open)
+ {
+ if (port->close_delay)
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(port->close_delay);
+ }
+ wake_up_interruptible(&port->open_wait); /* deal with open blocks */
+ }
+
+ if((port->flags & (FLAG8253X_CALLOUT_ACTIVE | FLAG8253X_NETWORK)) ==
+ (FLAG8253X_CALLOUT_ACTIVE | FLAG8253X_NETWORK) &&
+ port->dev)
+ {
+ port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE|
+ FLAG8253X_CLOSING); /* leave network set */
+ netif_carrier_off(port->dev);
+ port->open_type = OPEN_SYNC_NET;
+ sab8253x_startupN(port);
+ }
+ else
+ {
+ port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE|
+ FLAG8253X_CLOSING);
+ wake_up_interruptible(&port->close_wait);
+ port->open_type = OPEN_NOT;
+ }
+}
+
+/*
+ * sab8253x_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void sab8253x_hangup(struct tty_struct *tty)
+{
+ struct sab_port *port = (struct sab_port *)tty->driver_data;
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_hangup"))
+ {
+ return;
+ }
+
+#ifdef XCONFIG_SERIAL_CONSOLE
+ if (port->is_console)
+ {
+ return;
+ }
+#endif
+
+ sab8253x_flush_buffer(tty);
+ if(port)
+ {
+ sab8253x_shutdown(port);
+ Sab8253xCleanUpTransceiveN(port); /* this logic is a bit contorted
+ Are we cleaning up the lists
+ because we are waking up a
+ blocked open? There is possibly
+ an order problem here perhaps the
+ open count should have increased in the
+ int handler so that it could decrease here*/
+ port->event = 0;
+ port->flags &= ~(FLAG8253X_NORMAL_ACTIVE|FLAG8253X_CALLOUT_ACTIVE);
+ port->tty = 0;
+ wake_up_interruptible(&port->open_wait); /* deal with blocking open */
+ }
+}
+/*
+ * ------------------------------------------------------------
+ * sab8253x_open() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+
+static int sab8253x_open(struct tty_struct *tty, struct file * filp)
+{
+ struct sab_port *port;
+ int retval, line;
+ int counter;
+ unsigned long flags;
+
+ MOD_INC_USE_COUNT;
+ line = MINOR(tty->device) - tty->driver.minor_start;
+
+ for(counter = 0, port = AuraPortRoot;
+ (counter < line) && (port != NULL);
+ ++counter)
+ {
+ port = port->next;
+ }
+
+ if (!port)
+ {
+ printk(KERN_ALERT "sab8253x_open: can't find structure for line %d\n",
+ line);
+ return -ENODEV;
+ }
+
+ save_flags(flags); /* Need to protect the port->tty field */
+ cli();
+
+ if(port->tty == NULL)
+ {
+ port->tty = tty; /* may be a standard tty waiting on a call out device */
+ tty->flip.tqueue.routine = sab8253x_flush_to_ldisc;
+ }
+
+ tty->driver_data = port; /* but the tty devices are unique for each type of open */
+
+ if(port->function == FUNCTION_NA)
+ { /* port 2 on 1020s and 1520s */
+ ++(port->count);
+ restore_flags(flags);
+ return -ENODEV;
+ }
+
+ /* Check whether or not the port is open in SYNC mode */
+ if(port->open_type == OPEN_SYNC_NET)
+ {
+ if(port->dev && netif_carrier_ok(port->dev));
+ {
+ port->tty= NULL; /* Don't bother with open counting here
+ but make sure the tty field is NULL*/
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ sab8253x_flush_buffer(tty); /* don't restore flags here */
+ sab8253x_shutdownN(port);
+ }
+ else if (port->open_type > OPEN_ASYNC) /* can't have a callout or async line
+ * if already open in some sync mode */
+ {
+ ++(port->count);
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ restore_flags(flags);
+
+ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_open"))
+ {
+ ++(port->count);
+ return -ENODEV;
+ }
+
+#ifdef DEBUG_OPEN
+ printk("sab8253x_open %s%d, count = %d\n", tty->driver.name, port->line,
+ port->count);
+#endif
+
+ /*
+ * If the port is in the middle of closing, bail out now.
+ */
+ if (tty_hung_up_p(filp) ||
+ (port->flags & FLAG8253X_CLOSING))
+ {
+
+ if (port->flags & FLAG8253X_CLOSING)
+ {
+ interruptible_sleep_on(&port->close_wait);
+ }
+#ifdef SERIAL_DO_RESTART
+ ++(port->count);
+ return ((port->flags & FLAG8253X_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+#else
+ ++(port->count);
+ return -EAGAIN;
+#endif
+ }
+
+ if(Sab8253xSetUpLists(port))
+ {
+ ++(port->count);
+ return -ENODEV;
+ }
+ if(Sab8253xInitDescriptors2(port, sab8253xt_listsize, sab8253xt_rbufsize))
+ {
+ ++(port->count);
+ return -ENODEV;
+ }
+
+ retval = sab8253x_startup(port);
+ if (retval)
+ {
+ ++(port->count);
+ return retval;
+ }
+
+ retval = sab8253x_block_til_ready(tty, filp, port);
+ ++(port->count);
+ if (retval)
+ {
+ return retval;
+ }
+
+ port->tty = tty; /* may change here once through the block */
+ /* because now the port belongs to an new tty */
+ tty->flip.tqueue.routine = sab8253x_flush_to_ldisc; /* in case it was changed */
+
+ if(Sab8253xSetUpLists(port))
+ {
+ return -ENODEV;
+ }
+ if(Sab8253xInitDescriptors2(port, sab8253xt_listsize, sab8253xt_rbufsize))
+ {
+ Sab8253xCleanUpTransceiveN(port); /* the network functions should be okay -- only difference */
+ /* is the crc32 that is appended */
+ return -ENODEV;
+ }
+
+ /*
+ * Start up serial port
+ */
+ retval = sab8253x_startup(port); /* just in case closing the cu dev
+ * shutdown the port (but left CD) */
+ if (retval)
+ {
+ return retval;
+ }
+
+ if ((port->count == 1) && /* first open */
+ (port->flags & FLAG8253X_SPLIT_TERMIOS))
+ {
+ if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+ {
+ *tty->termios = port->normal_termios;
+ }
+ else
+ {
+ *tty->termios = port->callout_termios;
+ }
+ sab8253x_change_speed(port);
+ }
+
+
+#ifdef XCONFIG_SERIAL_CONSOLE
+ if (sab8253x_console.cflag && sab8253x_console.index == line)
+ {
+ tty->termios->c_cflag = sab8253x_console.cflag;
+ sab8253x_console.cflag = 0;
+ sab8253x_change_speed(port);
+ }
+#endif
+
+ port->session = current->session;
+ port->pgrp = current->pgrp;
+ port->open_type = OPEN_ASYNC;
+ return 0;
+}
+
+static char *signaling[] =
+{
+ "OFF ",
+ "RS232",
+ "RS422",
+ "RS485",
+ "RS449",
+ "RS530",
+ "V.35 "
+};
+
+static int sab8253x_read_proc(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ extern struct sab_port * AuraPortRoot;
+ struct sab_port *port = AuraPortRoot;
+ extern char *board_type[];
+ off_t begin = 0;
+ int len = 0;
+ int portno;
+ unsigned int typeno;
+ extern int sab8253x_rebootflag;
+
+#ifdef FREEININTERRUPT
+ len += sprintf(page, "serinfo:2.01I driver:%s\n", sab8253x_serial_version);
+#else
+ len += sprintf(page, "serinfo:2.01N driver:%s\n", sab8253x_serial_version);
+#endif
+ if(sab8253x_rebootflag)
+ {
+ len += sprintf(page+len,
+ "WARNING: Found %d cards that required reprogramming. Reboot machine!.\n",
+ sab8253x_rebootflag);
+ }
+ len += sprintf(page+len, "TTY MAJOR = %d, CUA MAJOR = %d, STTY MAJOR = %d.\n",
+ sab8253x_serial_driver.major, sab8253x_callout_driver.major,
+ sync_sab8253x_serial_driver.major);
+ for (portno = 0; port != NULL; port = port->next, ++portno)
+ {
+ typeno = port->board->b_type;
+ if(typeno > BD_8520P)
+ {
+ typeno = 0;
+ }
+ len += sprintf(page+len,
+ "%d: port %d: %s: v%d: chip %d: ATI %s: bus %d: slot %d: %s: ",
+ sab8253x_serial_driver.minor_start + portno,
+ port->portno,
+ (port->chip->chip_type == ESCC2) ? "sab82532" : "sab82538",
+ port->type,
+ port->chip->c_chipno,
+ board_type[port->board->b_type],
+ port->board->b_dev.bus->number,
+ PCI_SLOT(port->board->b_dev.devfn),
+ aura_functionality[((port->function > FUNCTION_UN) ? FUNCTION_UN : port->function)]);
+ switch(port->open_type)
+ {
+ case OPEN_ASYNC:
+ len += sprintf(page+len, "openA");
+ break;
+ case OPEN_SYNC:
+ len += sprintf(page+len, "openS");
+ break;
+ case OPEN_SYNC_NET:
+ len += sprintf(page+len, "openN");
+ break;
+ case OPEN_SYNC_CHAR:
+ len += sprintf(page+len, "openC");
+ break;
+ case OPEN_NOT:
+ len += sprintf(page+len, "close");
+ break;
+ default:
+ len += sprintf(page+len, "open?");
+ break;
+ }
+ if(port->chip->c_cim)
+ {
+ if(port->chip->c_cim->ci_type == CIM_SP502)
+ {
+ len += sprintf(page+len, ": %s\n", signaling[port->sigmode]);
+ }
+ else
+ {
+ len += sprintf(page+len, ": NOPRG\n");
+ }
+ }
+ else
+ {
+ len += sprintf(page+len, ": NOPRG\n");
+ }
+
+ 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);
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * sab8253x_init() and friends
+ *
+ * sab8253x_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+static void inline show_aurora_version(void)
+{
+ char *revision = "$Revision: 1.23 $";
+ char *version, *p;
+
+ version = strchr(revision, ' ');
+ strcpy(sab8253x_serial_version, ++version);
+ p = strchr(sab8253x_serial_version, ' ');
+ *p = '\0';
+ printk("Aurora serial driver version %s\n", sab8253x_serial_version);
+}
+
+#ifndef MODULE
+static int GetMinorStart(void)
+{
+ struct tty_driver *ttydriver;
+ int minor_start = 0;
+ kdev_t device;
+
+ device = MKDEV(TTY_MAJOR, minor_start);
+ while(ttydriver = get_tty_driver(device), ttydriver != NULL)
+ {
+ minor_start += ttydriver->num;
+ device = MKDEV(TTY_MAJOR, minor_start);
+ }
+ return minor_start;
+
+}
+#endif
+
+int finish_sab8253x_setup_ttydriver(void)
+{
+ extern unsigned int NumSab8253xPorts;
+
+ sab8253x_tableASY = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
+ if(sab8253x_tableASY == NULL)
+ {
+ printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_tableASY.\n");
+ return -1;
+ }
+ memset(sab8253x_tableASY, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
+ sab8253x_tableCUA = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
+ if(sab8253x_tableCUA == NULL)
+ {
+ printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_tableCUA.\n");
+ return -1;
+ }
+ memset(sab8253x_tableCUA, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
+ sab8253x_tableSYN = (struct tty_struct **) kmalloc(NumSab8253xPorts*sizeof(struct tty_struct *), GFP_KERNEL);
+ if(sab8253x_tableSYN == NULL)
+ {
+ printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_tableSYN.\n");
+ return -1;
+ }
+ memset(sab8253x_tableSYN, 0, NumSab8253xPorts*sizeof(struct tty_struct *));
+
+ sab8253x_termios = (struct termios **) kmalloc(NumSab8253xPorts*sizeof(struct termios *), GFP_KERNEL);
+ if(sab8253x_termios == NULL)
+ {
+ printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_termios.\n");
+ return -1;
+ }
+ memset(sab8253x_termios, 0, NumSab8253xPorts*sizeof(struct termios *));
+ sab8253x_termios_locked = (struct termios **) kmalloc(NumSab8253xPorts*sizeof(struct termios *), GFP_KERNEL);
+ if(sab8253x_termios_locked == NULL)
+ {
+ printk(KERN_ALERT "auraXX20: Could not allocate memory for sab8253x_termios_locked.\n");
+ return -1;
+ }
+ memset(sab8253x_termios_locked, 0, NumSab8253xPorts*sizeof(struct termios *));
+ sync_sab8253x_serial_driver.num = sab8253x_callout_driver.num = sab8253x_serial_driver.num = NumSab8253xPorts;
+ sab8253x_serial_driver.table = sab8253x_tableASY;
+ sab8253x_callout_driver.table = sab8253x_tableCUA;
+ sync_sab8253x_serial_driver.table = sab8253x_tableSYN;
+ sync_sab8253x_serial_driver.termios = sab8253x_callout_driver.termios = sab8253x_serial_driver.termios =
+ sab8253x_termios;
+ sync_sab8253x_serial_driver.termios_locked = sab8253x_callout_driver.termios_locked =
+ sab8253x_serial_driver.termios_locked = sab8253x_termios_locked;
+
+ if (tty_register_driver(&sab8253x_serial_driver) < 0)
+ {
+ printk(KERN_ALERT "auraXX20: Could not register serial driver.\n");
+ return -1;
+ }
+ if (tty_register_driver(&sab8253x_callout_driver) < 0)
+ {
+ printk(KERN_ALERT "auraXX20: Could not register call out device.\n");
+ return -1;
+ }
+ if (tty_register_driver(&sync_sab8253x_serial_driver) < 0)
+ {
+ printk(KERN_ALERT "auraXX20: Could not register sync serial device.\n");
+ return -1;
+ }
+ return 0;
+
+}
+
+void sab8253x_setup_ttydriver(void)
+{
+#ifdef MODULE
+ extern int xx20_minorstart;
+#endif
+ init_bh(AURORA_BH, sab8253x_do_serial_bh);
+
+ show_aurora_version();
+
+ /* Initialize the tty_driver structure */
+
+ memset(&sab8253x_serial_driver, 0, sizeof(struct tty_driver));
+ sab8253x_serial_driver.magic = TTY_DRIVER_MAGIC;
+ sab8253x_serial_driver.driver_name = "auraserial";
+ sab8253x_serial_driver.name = "ttyS";
+ sab8253x_serial_driver.major = TTY_MAJOR;
+#ifdef MODULE
+ sab8253x_serial_driver.minor_start = xx20_minorstart;
+#else
+ sab8253x_serial_driver.minor_start = GetMinorStart();
+#endif
+ sab8253x_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ sab8253x_serial_driver.subtype = SERIAL_TYPE_NORMAL;
+ sab8253x_serial_driver.init_termios = tty_std_termios;
+ sab8253x_serial_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ sab8253x_serial_driver.flags = TTY_DRIVER_REAL_RAW;
+ sab8253x_serial_driver.refcount = &sab8253x_refcount;
+
+ sab8253x_serial_driver.open = sab8253x_open;
+ sab8253x_serial_driver.close = sab8253x_close;
+ sab8253x_serial_driver.write = sab8253x_write;
+ sab8253x_serial_driver.put_char = NULL; /*sab8253x_put_char is evil.*/
+ sab8253x_serial_driver.flush_chars = sab8253x_flush_chars;
+ sab8253x_serial_driver.write_room = sab8253x_write_room;
+ sab8253x_serial_driver.chars_in_buffer = sab8253x_chars_in_buffer;
+ sab8253x_serial_driver.flush_buffer = sab8253x_flush_buffer;
+ sab8253x_serial_driver.ioctl = sab8253x_ioctl;
+ sab8253x_serial_driver.throttle = sab8253x_throttle;
+ sab8253x_serial_driver.unthrottle = sab8253x_unthrottle;
+ sab8253x_serial_driver.send_xchar = sab8253x_send_xchar;
+ sab8253x_serial_driver.set_termios = sab8253x_set_termios;
+ sab8253x_serial_driver.stop = sab8253x_stop;
+ sab8253x_serial_driver.start = sab8253x_start;
+ sab8253x_serial_driver.hangup = sab8253x_hangup;
+ sab8253x_serial_driver.break_ctl = sab8253x_break;
+ sab8253x_serial_driver.wait_until_sent = sab8253x_wait_until_sent;
+ sab8253x_serial_driver.read_proc = sab8253x_read_proc;
+
+ /*
+ * The callout device is just like normal device except for
+ * major number and the subtype code.
+ */
+ sab8253x_callout_driver = sab8253x_serial_driver;
+ sab8253x_callout_driver.name = "cua";
+ sab8253x_callout_driver.major = TTYAUX_MAJOR;
+ sab8253x_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+ sab8253x_callout_driver.read_proc = 0;
+ sab8253x_callout_driver.proc_entry = 0;
+
+ sync_sab8253x_serial_driver = sab8253x_serial_driver;
+ sync_sab8253x_serial_driver.name = "sttyS";
+ sync_sab8253x_serial_driver.major = 0;
+ sync_sab8253x_serial_driver.subtype = SERIAL_TYPE_SYNCTTY;
+
+ sync_sab8253x_serial_driver.open = sab8253x_openS;
+ sync_sab8253x_serial_driver.close = sab8253x_closeS;
+ sync_sab8253x_serial_driver.write = sab8253x_writeS;
+ sync_sab8253x_serial_driver.put_char = NULL; /*sab8253x_put_char logic is evil*/
+ sync_sab8253x_serial_driver.flush_chars = sab8253x_flush_charsS;
+ sync_sab8253x_serial_driver.write_room = sab8253x_write_room;
+ sync_sab8253x_serial_driver.chars_in_buffer = sab8253x_chars_in_buffer;
+ sync_sab8253x_serial_driver.flush_buffer = sab8253x_flush_buffer;
+ sync_sab8253x_serial_driver.ioctl = sab8253x_ioctl;
+ sync_sab8253x_serial_driver.throttle = sab8253x_throttleS;
+ sync_sab8253x_serial_driver.unthrottle = sab8253x_unthrottleS;
+ sync_sab8253x_serial_driver.send_xchar = sab8253x_send_xcharS;
+ sync_sab8253x_serial_driver.set_termios = sab8253x_set_termiosS;
+ sync_sab8253x_serial_driver.stop = sab8253x_stopS;
+ sync_sab8253x_serial_driver.start = sab8253x_startS;
+ sync_sab8253x_serial_driver.hangup = sab8253x_hangupS;
+ sync_sab8253x_serial_driver.break_ctl = sab8253x_breakS;
+ sync_sab8253x_serial_driver.wait_until_sent = sab8253x_wait_until_sent;
+ sync_sab8253x_serial_driver.read_proc = 0;
+ sync_sab8253x_serial_driver.proc_entry = 0;
+}
+
+void sab8253x_setup_ttyport(struct sab_port *p_port)
+{
+ p_port->magic = SAB_MAGIC;
+ p_port->custom_divisor = 16;
+ p_port->close_delay = 5*HZ/10;
+ p_port->closing_wait = 30*HZ;
+ p_port->tec_timeout = SAB8253X_MAX_TEC_DELAY;
+ p_port->cec_timeout = SAB8253X_MAX_CEC_DELAY;
+ p_port->x_char = 0;
+ p_port->event = 0;
+ p_port->flags= FLAG8253X_BOOT_AUTOCONF | FLAG8253X_SKIP_TEST;
+ p_port->blocked_open = 0;
+
+ p_port->all_sent = 1; /* probably not needed */
+
+ p_port->tqueue.sync = 0; /* for later */
+ p_port->tqueue.routine = sab8253x_do_softint;
+ p_port->tqueue.data = p_port;
+ p_port->tqueue_hangup.sync = 0; /* for later */
+ p_port->tqueue_hangup.routine = sab8253x_do_serial_hangup;
+ p_port->tqueue_hangup.data = p_port;
+ p_port->callout_termios = sab8253x_callout_driver.init_termios;
+ p_port->normal_termios = sab8253x_serial_driver.init_termios; /* these are being shared */
+ /* between asynchronous and */
+ /* asynchronous ttys */
+ init_waitqueue_head(&p_port->open_wait);
+ init_waitqueue_head(&p_port->close_wait);
+ init_waitqueue_head(&p_port->delta_msr_wait);
+ init_waitqueue_head(&p_port->write_wait);
+ init_waitqueue_head(&p_port->read_wait);
+
+ p_port->count = 0; /* maybe not needed */
+ p_port->icount.cts = p_port->icount.dsr =
+ p_port->icount.rng = p_port->icount.dcd = 0;
+ p_port->cts.val = p_port->dsr.val =
+ p_port->dcd.val = 0;
+ p_port->icount.rx = p_port->icount.tx = 0;
+ p_port->icount.frame = p_port->icount.parity = 0;
+ p_port->icount.overrun = p_port->icount.brk = 0;
+
+ p_port->xmit_fifo_size = 32;
+ p_port->recv_fifo_size = 32;
+ p_port->xmit_buf = NULL;
+ p_port->receive_chars = sab8253x_receive_chars;
+ p_port->transmit_chars = sab8253x_transmit_chars;
+ p_port->check_status = sab8253x_check_status;
+ p_port->receive_test = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+ SAB82532_ISR0_RFO | SAB82532_ISR0_RPF);
+ p_port->transmit_test = (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR);
+ p_port->check_status_test = SAB82532_ISR1_BRK;
+
+ /* put in default sync control channel values
+ * not needed for async -- I think these work
+ * for bisync*/
+ p_port->ccontrol.ccr0 = DEFAULT_CCR0;
+ p_port->ccontrol.ccr1 = DEFAULT_CCR1;
+ p_port->ccontrol.ccr2 = DEFAULT_CCR2;
+ p_port->ccontrol.ccr3 = DEFAULT_CCR3;
+ p_port->ccontrol.ccr4 = DEFAULT_CCR4;
+ p_port->ccontrol.mode = DEFAULT_MODE;
+ p_port->ccontrol.rlcr = DEFAULT_RLCR;
+}
+
+void sab8253x_cleanup_ttydriver(void)
+{
+ unsigned long flags;
+ int e1, e2;
+
+ save_flags(flags);
+ cli();
+
+ if(sab8253x_tableASY)
+ kfree(sab8253x_tableASY);
+ if(sab8253x_tableCUA)
+ kfree(sab8253x_tableCUA);
+ if(sab8253x_tableSYN)
+ kfree(sab8253x_tableSYN);
+ if(sab8253x_termios)
+ kfree(sab8253x_termios);
+ if(sab8253x_termios_locked)
+ kfree(sab8253x_termios_locked);
+
+ remove_bh(AURORA_BH);
+ if ((e1 = tty_unregister_driver(&sab8253x_serial_driver)))
+ {
+ printk("SERIAL: failed to unregister serial driver (%d)\n",
+ e1);
+ }
+ if ((e2 = tty_unregister_driver(&sab8253x_callout_driver)))
+ {
+ printk("SERIAL: failed to unregister callout driver (%d)\n",
+ e2);
+ }
+ if ((e2 = tty_unregister_driver(&sync_sab8253x_serial_driver)))
+ {
+ printk("SERIAL: failed to unregister callout driver (%d)\n",
+ e2);
+ }
+ restore_flags(flags);
+}
+
+/* THE CODE BELOW HAS NOT YET BEEN MODIFIED!!!! FW */
+#ifdef XCONFIG_SERIAL_CONSOLE
+
+static inline void
+sab8253x_console_putchar(struct sab8253x *info, char c)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ sab8253x_tec_wait(info);
+ WRITEB(port,tic,c);
+ restore_flags(flags);
+}
+
+static void
+sab8253x_console_write(struct console *con, const char *s, unsigned n)
+{
+ struct sab8253x *info;
+ int i;
+
+ info = sab8253x_chain;
+ for (i = con->index; i; i--) {
+ info = info->next;
+ if (!info)
+ return;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (*s == '\n')
+ sab8253x_console_putchar(info, '\r');
+ sab8253x_console_putchar(info, *s++);
+ }
+ sab8253x_tec_wait(info);
+}
+
+static int
+sab8253x_console_wait_key(struct console *con)
+{
+ sleep_on(&keypress_wait);
+ return 0;
+}
+
+static kdev_t
+sab8253x_console_device(struct console *con)
+{
+ return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+static int
+sab8253x_console_setup(struct console *con, char *options)
+{
+ struct sab8253x *info;
+ unsigned int ebrg;
+ tcflag_t cflag;
+ unsigned char dafo;
+ int i, bits;
+ unsigned long flags;
+
+ info = sab8253x_chain;
+ for (i = con->index; i; i--)
+ {
+ info = info->next;
+ if (!info)
+ return -ENODEV;
+ }
+ info->is_console = 1;
+
+ /*
+ * Initialize the hardware
+ */
+ sab8253x_init_line(info);
+
+ /*
+ * Finally, enable interrupts
+ */
+ info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+ /*SAB82532_IMR0_TIME*/ | SAB82532_IMR0_PLLA/*| SAB82532_IMR0_CDSC*/;
+ WRITEB(port,imr0,info->interrupt_mask0);
+ info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+ SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+ SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+ SAB82532_IMR1_XPR;
+ WRITEB(port,imr1,info->interrupt_mask1);
+
+ printk("Console: ttyS%d (SAB82532)\n", info->line);
+
+ sunserial_console_termios(con);
+ cflag = con->cflag;
+
+ /* Byte size and parity */
+ switch (cflag & CSIZE)
+ {
+ case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+ case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
+ case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
+ case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
+ /* Never happens, but GCC is too dumb to figure it out */
+ default: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+ }
+
+ if (cflag & CSTOPB)
+ {
+ dafo |= SAB82532_DAFO_STOP;
+ bits++;
+ }
+
+ if (cflag & PARENB)
+ {
+ dafo |= SAB82532_DAFO_PARE;
+ bits++;
+ }
+
+ if (cflag & PARODD)
+ {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_MARK;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_ODD;
+ }
+ else
+ {
+#ifdef CMSPAR
+ if (cflag & CMSPAR)
+ dafo |= SAB82532_DAFO_PAR_SPACE;
+ else
+#endif
+ dafo |= SAB82532_DAFO_PAR_EVEN;
+ }
+
+ /* Determine EBRG values based on baud rate */
+ i = cflag & CBAUD;
+ if (i & CBAUDEX)
+ {
+ i &= ~(CBAUDEX);
+ if ((i < 1) || ((i + 15) >= NR_EBRG_VALUES))
+ cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ ebrg = ebrg_tabl[i].n;
+ ebrg |= (ebrg_table[i].m << 6);
+
+ info->baud = ebrg_table[i].baud;
+ if (info->baud)
+ info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
+ else
+ info->timeout = 0;
+ info->timeout += HZ / 50; /* Add .02 seconds of slop */
+
+ /* CTS flow control flags */
+ if (cflag & CRTSCTS)
+ info->flags |= FLAG8253X_CTS_FLOW;
+ else
+ info->flags &= ~(FLAG8253X_CTS_FLOW);
+
+ if (cflag & CLOCAL)
+ info->flags &= ~(FLAG8253X_CHECK_CD);
+ else
+ info->flags |= FLAG8253X_CHECK_CD;
+
+ save_flags(flags); cli();
+ sab8253x_cec_wait(info);
+ sab8253x_tec_wait(info);
+ WRITEB(port,dafo,dafo);
+ WRITEB(port,bgr,ebrg & 0xff);
+ info->regs->rw.ccr2 &= ~(0xc0);
+ info->regs->rw.ccr2 |= (ebrg >> 2) & 0xc0;
+ if (info->flags & FLAG8253X_CTS_FLOW)
+ {
+ info->regs->rw.mode &= ~(SAB82532_MODE_RTS);
+ info->regs->rw.mode |= SAB82532_MODE_FRTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_FCTS);
+ }
+ else
+ {
+ info->regs->rw.mode |= SAB82532_MODE_RTS;
+ info->regs->rw.mode &= ~(SAB82532_MODE_FRTS);
+ info->regs->rw.mode |= SAB82532_MODE_FCTS;
+ }
+ info->regs->rw.pvr &= ~(info->pvr_dtr_bit);
+ info->regs->rw.mode |= SAB82532_MODE_RAC;
+ restore_flags(flags);
+
+ return 0;
+}
+
+static struct console sab8253x_console =
+{
+ "ttyS",
+ sab8253x_console_write,
+ NULL,
+ sab8253x_console_device,
+ sab8253x_console_wait_key,
+ NULL,
+ sab8253x_console_setup,
+ CON_PRINTBUFFER,
+ -1,
+ 0,
+ NULL
+};
+
+int sab8253x_console_init(void)
+{
+ extern int con_is_present(void);
+ extern int su_console_registered;
+
+ if (con_is_present() || su_console_registered)
+ return 0;
+
+ if (!sab8253x_chain)
+ {
+ prom_printf("sab8253x_console_setup: can't get SAB8253X chain");
+ prom_halt();
+ }
+
+ sab8253x_console.index = serial_console - 1;
+ register_console(&sab8253x_console);
+ return 0;
+}
+
+#ifdef SERIAL_LOG_DEVICE
+
+static int serial_log_device = 0;
+
+static void
+dprint_init(int tty)
+{
+ serial_console = tty + 1;
+ sab8253x_console.index = tty;
+ sab8253x_console_setup(&sab8253x_console, "");
+ serial_console = 0;
+ serial_log_device = tty + 1;
+}
+
+int
+dprintf(const char *fmt, ...)
+{
+ static char buffer[4096];
+ va_list args;
+ int i;
+
+ if (!serial_log_device)
+ return 0;
+
+ va_start(args, fmt);
+ i = vsprintf(buffer, fmt, args);
+ va_end(args);
+ sab8253x_console.write(&sab8253x_console, buffer, i);
+ return i;
+}
+
+#endif /* SERIAL_LOG_DEVICE */
+#endif /* XCONFIG_SERIAL_CONSOLE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)