patch-2.3.99-pre7 linux/drivers/char/rio/riointr.c
Next file: linux/drivers/char/rio/rioioctl.h
Previous file: linux/drivers/char/rio/rioinit.c
Back to the patch index
Back to the overall index
- Lines: 1113
- Date:
Wed May 10 16:56:44 2000
- Orig file:
v2.3.99-pre6/linux/drivers/char/rio/riointr.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.99-pre6/linux/drivers/char/rio/riointr.c linux/drivers/char/rio/riointr.c
@@ -0,0 +1,1112 @@
+/*
+** -----------------------------------------------------------------------------
+**
+** Perle Specialix driver for Linux
+** Ported from existing RIO Driver for SCO sources.
+ *
+ * (C) 1990 - 2000 Specialix International Ltd., Byfleet, Surrey, UK.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** Module : riointr.c
+** SID : 1.2
+** Last Modified : 11/6/98 10:33:44
+** Retrieved : 11/6/98 10:33:49
+**
+** ident @(#)riointr.c 1.2
+**
+** -----------------------------------------------------------------------------
+*/
+#ifdef SCCS_LABELS
+static char *_riointr_c_sccs_ = "@(#)riointr.c 1.2";
+#endif
+
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/string.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include <linux/termios.h>
+#include <linux/serial.h>
+
+#include <linux/compatmac.h>
+#include <linux/generic_serial.h>
+
+#include <linux/delay.h>
+
+#include "linux_compat.h"
+#include "rio_linux.h"
+#include "typdef.h"
+#include "pkt.h"
+#include "daemon.h"
+#include "rio.h"
+#include "riospace.h"
+#include "top.h"
+#include "cmdpkt.h"
+#include "map.h"
+#include "riotypes.h"
+#include "rup.h"
+#include "port.h"
+#include "riodrvr.h"
+#include "rioinfo.h"
+#include "func.h"
+#include "errors.h"
+#include "pci.h"
+
+#include "parmmap.h"
+#include "unixrup.h"
+#include "board.h"
+#include "host.h"
+#include "error.h"
+#include "phb.h"
+#include "link.h"
+#include "cmdblk.h"
+#include "route.h"
+#include "control.h"
+#include "cirrus.h"
+#include "rioioctl.h"
+
+
+
+
+/*
+** riopoll is called every clock tick. Once the /dev/rio device has been
+** opened, and polldistributed( ) has been called, this routine is called
+** every clock tick *by every cpu*. The 'interesting' piece of code that
+** manipulates 'RIONumCpus' and 'RIOCpuCountdown' is used to fair-share
+** the work between the CPUs. If there are 'N' cpus, then each poll time
+** we increment a counter, modulo 'N-1'. When this counter is 0, we call
+** the interrupt handler. This has the effect that polls are serviced
+** by processor 'N', 'N-1', 'N-2', ... '0', round and round. Neat.
+*/
+void
+riopoll(p)
+struct rio_info * p;
+{
+ int host;
+
+ /*
+ ** Here's the deal. We try to fair share as much as possible amongst
+ ** all the processors that are available. Since each processor
+ ** should generate HZ ticks per second and since we only need HZ ticks
+ ** in total for proper operation we simply attempt to cycle round each
+ ** processor in turn, using RIOCpuCountdown to decide whether to call
+ ** the interrupt routine. ( In fact the count zeroes when it reaches
+ ** one less than the total number of processors - so e.g. on a two
+ ** processor system RIOService will get called 2*HZ times per second. )
+ ** this_cpu (cur_cpu()) tells us the number of the current processor
+ ** as follows:
+ **
+ ** 0 - default CPU
+ ** 1 - first extra CPU
+ ** 2 - second extra CPU
+ ** etc.
+ */
+
+ /*
+ ** okay, we've got a cpu that hasn't had a go recently
+ ** - lets check to see what needs doing.
+ */
+ for ( host=0; host<p->RIONumHosts; host++ ) {
+ struct Host *HostP = &p->RIOHosts[host];
+
+ rio_spin_lock( &HostP->HostLock );
+
+ if ( ( (HostP->Flags & RUN_STATE) != RC_RUNNING ) ||
+ HostP->InIntr ) {
+ rio_spin_unlock (&HostP->HostLock);
+ continue;
+ }
+
+ if ( RWORD( HostP->ParmMapP->rup_intr ) ||
+ RWORD( HostP->ParmMapP->rx_intr ) ||
+ RWORD( HostP->ParmMapP->tx_intr ) ) {
+ HostP->InIntr = 1;
+
+#ifdef FUTURE_RELEASE
+ if( HostP->Type == RIO_EISA )
+ INBZ( HostP->Slot, EISA_INTERRUPT_RESET );
+ else
+#endif
+ WBYTE( HostP->ResetInt , 0xff );
+
+ rio_spin_lock(&HostP->HostLock);
+
+ p->_RIO_Polled++;
+ RIOServiceHost(p, HostP, 'p' );
+ rio_spin_lock( &HostP->HostLock);
+ HostP->InIntr = 0;
+ rio_spin_unlock (&HostP->HostLock);
+ }
+ }
+ rio_spin_unlock (&p->RIOIntrSem);
+}
+
+
+char *firstchars (char *p, int nch)
+{
+ static char buf[2][128];
+ static int t=0;
+ t = ! t;
+ memcpy (buf[t], p, nch);
+ buf[t][nch] = 0;
+ return buf[t];
+}
+
+
+#define INCR( P, I ) ((P) = (((P)+(I)) & p->RIOBufferMask))
+/* Enable and start the transmission of packets */
+void
+RIOTxEnable(en)
+char * en;
+{
+ struct Port * PortP;
+ struct rio_info *p;
+ struct tty_struct* tty;
+ int c;
+ struct PKT * PacketP;
+ unsigned long flags;
+
+ PortP = (struct Port *)en;
+ p = (struct rio_info *)PortP->p;
+ tty = PortP->gs.tty;
+
+
+ rio_dprint (RIO_DEBUG_INTR, ("tx port %d: %d chars queued.\n",
+ PortP->PortNum, PortP->gs.xmit_cnt));
+
+ if (!PortP->gs.xmit_cnt) return;
+
+
+ /* This routine is an order of magnitude simpler than the specialix
+ version. One of the disadvantages is that this version will send
+ an incomplete packet (usually 64 bytes instead of 72) once for
+ every 4k worth of data. Let's just say that this won't influence
+ performance significantly..... */
+
+ rio_spin_lock_irqsave(&PortP->portSem, flags);
+
+ while (can_add_transmit( &PacketP, PortP )) {
+ c = PortP->gs.xmit_cnt;
+ if (c > PKT_MAX_DATA_LEN) c = PKT_MAX_DATA_LEN;
+
+ /* Don't copy past the end of the source buffer */
+ if (c > SERIAL_XMIT_SIZE - PortP->gs.xmit_tail)
+ c = SERIAL_XMIT_SIZE - PortP->gs.xmit_tail;
+
+ { int t;
+ t = (c > 10)?10:c;
+
+ rio_dprint (RIO_DEBUG_INTR, ("tx port %d: copying %d chars: %s - %s\n",
+ PortP->PortNum, c,
+ firstchars (PortP->gs.xmit_buf + PortP->gs.xmit_tail , t),
+ firstchars (PortP->gs.xmit_buf + PortP->gs.xmit_tail + c-t, t)));
+ }
+ /* If for one reason or another, we can't copy more data,
+ we're done! */
+ if (c == 0) break;
+
+ rio_memcpy_toio (PortP->HostP->Caddr, (caddr_t)PacketP->data,
+ PortP->gs.xmit_buf + PortP->gs.xmit_tail, c);
+ /* udelay (1); */
+
+ writeb (c, &(PacketP->len));
+ if (!( PortP->State & RIO_DELETED ) ) {
+ add_transmit ( PortP );
+ /*
+ ** Count chars tx'd for port statistics reporting
+ */
+ if ( PortP->statsGather )
+ PortP->txchars += c;
+ }
+ PortP->gs.xmit_tail = (PortP->gs.xmit_tail + c) & (SERIAL_XMIT_SIZE-1);
+ PortP->gs.xmit_cnt -= c;
+ }
+
+ rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+
+ if (PortP->gs.xmit_cnt <= (PortP->gs.wakeup_chars + 2*PKT_MAX_DATA_LEN)) {
+ rio_dprint (RIO_DEBUG_INTR, ("Waking up.... ldisc:%d (%d/%d)....",
+ (int)(PortP->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)),
+ PortP->gs.wakeup_chars, PortP->gs.xmit_cnt));
+ if ((PortP->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ PortP->gs.tty->ldisc.write_wakeup)
+ (PortP->gs.tty->ldisc.write_wakeup)(PortP->gs.tty);
+ rio_dprint (RIO_DEBUG_INTR, ("(%d/%d)\n",
+ PortP->gs.wakeup_chars, PortP->gs.xmit_cnt));
+ wake_up_interruptible(&PortP->gs.tty->write_wait);
+ }
+
+}
+
+
+/*
+** When a real-life interrupt comes in here, we try to find out
+** which host card it belongs to, and then service only that host
+** Notice the cunning way that, once we've found a candidate, we
+** continue just in case we are sharing interrupts.
+*/
+void
+riointr(p)
+struct rio_info * p;
+{
+ int host;
+
+ for ( host=0; host<p->RIONumHosts; host++ ) {
+ struct Host *HostP = &p->RIOHosts[host];
+
+ rio_dprint(RIO_DEBUG_INTR, ("riointr() doing host %d type %d\n", host, HostP->Type ) );
+
+ switch( HostP->Type ) {
+ case RIO_AT:
+ case RIO_MCA:
+ case RIO_PCI:
+ rio_spin_lock(&HostP->HostLock);
+ WBYTE(HostP->ResetInt , 0xff);
+ if ( !HostP->InIntr ) {
+ HostP->InIntr = 1;
+ rio_spin_unlock (&HostP->HostLock);
+ p->_RIO_Interrupted++;
+ RIOServiceHost(p, HostP, 'i');
+ rio_spin_lock(&HostP->HostLock);
+ HostP->InIntr = 0;
+ }
+ rio_spin_unlock(&HostP->HostLock);
+ break;
+#ifdef FUTURE_RELEASE
+ case RIO_EISA:
+ if ( ivec == HostP->Ivec )
+ {
+ OldSpl = LOCKB( &HostP->HostLock );
+ INBZ( HostP->Slot, EISA_INTERRUPT_RESET );
+ if ( !HostP->InIntr )
+ {
+ HostP->InIntr = 1;
+ UNLOCKB( &HostP->HostLock, OldSpl );
+ if ( this_cpu < RIO_CPU_LIMIT )
+ {
+ int intrSpl = LOCKB( &RIOIntrLock );
+ UNLOCKB( &RIOIntrLock, intrSpl );
+ }
+ p->_RIO_Interrupted++;
+ RIOServiceHost( HostP, 'i' );
+ OldSpl = LOCKB( &HostP->HostLock );
+ HostP->InIntr = 0;
+ }
+ UNLOCKB( &HostP->HostLock, OldSpl );
+ done++;
+ }
+ break;
+#endif
+ }
+
+ HostP->IntSrvDone++;
+ }
+
+#ifdef FUTURE_RELEASE
+ if ( !done )
+ {
+ cmn_err( CE_WARN, "RIO: Interrupt received with vector 0x%x\n", ivec );
+ cmn_err( CE_CONT, " Valid vectors are:\n");
+ for ( host=0; host<RIONumHosts; host++ )
+ {
+ switch( RIOHosts[host].Type )
+ {
+ case RIO_AT:
+ case RIO_MCA:
+ case RIO_EISA:
+ cmn_err( CE_CONT, "0x%x ", RIOHosts[host].Ivec );
+ break;
+ case RIO_PCI:
+ cmn_err( CE_CONT, "0x%x ", get_intr_arg( RIOHosts[host].PciDevInfo.busnum, IDIST_PCI_IRQ( RIOHosts[host].PciDevInfo.slotnum, RIOHosts[host].PciDevInfo.funcnum ) ));
+ break;
+ }
+ }
+ cmn_err( CE_CONT, "\n" );
+ }
+#endif
+}
+
+/*
+** RIO Host Service routine. Does all the work traditionally associated with an
+** interrupt.
+*/
+static int RupIntr;
+static int RxIntr;
+static int TxIntr;
+void
+RIOServiceHost(p, HostP, From)
+struct rio_info * p;
+struct Host *HostP;
+int From;
+{
+ rio_spin_lock (&HostP->HostLock);
+ if ( (HostP->Flags & RUN_STATE) != RC_RUNNING ) {
+ static int t =0;
+ rio_spin_unlock (&HostP->HostLock);
+ if ((t++ % 200) == 0)
+ rio_dprint(RIO_DEBUG_INTR, ("Interrupt but host not running. flags=%x.\n", (int)HostP->Flags));
+ return;
+ }
+ rio_spin_unlock (&HostP->HostLock);
+
+ if ( RWORD( HostP->ParmMapP->rup_intr ) ) {
+ WWORD( HostP->ParmMapP->rup_intr , 0 );
+ p->RIORupCount++;
+ RupIntr++;
+ rio_dprint(RIO_DEBUG_INTR, ("RUP interrupt on host %d\n", HostP-p->RIOHosts ));
+ RIOPollHostCommands(p, HostP );
+ }
+
+ if ( RWORD( HostP->ParmMapP->rx_intr ) ) {
+ int port;
+
+ WWORD( HostP->ParmMapP->rx_intr , 0 );
+ p->RIORxCount++;
+ RxIntr++;
+
+ rio_dprint(RIO_DEBUG_INTR, ("RX interrupt on host %d\n", HostP-p->RIOHosts));
+ /*
+ ** Loop through every port. If the port is mapped into
+ ** the system ( i.e. has /dev/ttyXXXX associated ) then it is
+ ** worth checking. If the port isn't open, grab any packets
+ ** hanging on its receive queue and stuff them on the free
+ ** list; check for commands on the way.
+ */
+ for ( port=p->RIOFirstPortsBooted;
+ port<p->RIOLastPortsBooted+PORTS_PER_RTA; port++ ) {
+ struct Port *PortP = p->RIOPortp[port];
+ struct tty_struct *ttyP;
+ struct PKT *PacketP;
+
+ /*
+ ** not mapped in - most of the RIOPortp[] information
+ ** has not been set up!
+ ** Optimise: ports come in bundles of eight.
+ */
+ if ( !PortP->Mapped ) {
+ port += 7;
+ continue; /* with the next port */
+ }
+
+ /*
+ ** If the host board isn't THIS host board, check the next one.
+ ** optimise: ports come in bundles of eight.
+ */
+ if ( PortP->HostP != HostP ) {
+ port += 7;
+ continue;
+ }
+
+ /*
+ ** Let us see - is the port open? If not, then don't service it.
+ */
+ if ( !( PortP->PortState & PORT_ISOPEN ) ) {
+ continue;
+ }
+
+ /*
+ ** find corresponding tty structure. The process of mapping
+ ** the ports puts these here.
+ */
+ ttyP = PortP->gs.tty;
+
+ /*
+ ** Lock the port before we begin working on it.
+ */
+ rio_spin_lock(&PortP->portSem);
+
+ /*
+ ** Process received data if there is any.
+ */
+ if ( can_remove_receive( &PacketP, PortP ) )
+ RIOReceive(p, PortP);
+
+ /*
+ ** If there is no data left to be read from the port, and
+ ** it's handshake bit is set, then we must clear the handshake,
+ ** so that that downstream RTA is re-enabled.
+ */
+ if ( !can_remove_receive( &PacketP, PortP ) &&
+ ( RWORD( PortP->PhbP->handshake )==PHB_HANDSHAKE_SET ) ) {
+ /*
+ ** MAGIC! ( Basically, handshake the RX buffer, so that
+ ** the RTAs upstream can be re-enabled. )
+ */
+ rio_dprint(RIO_DEBUG_INTR, ("Set RX handshake bit\n" ));
+ WWORD( PortP->PhbP->handshake,
+ PHB_HANDSHAKE_SET|PHB_HANDSHAKE_RESET );
+ }
+ rio_spin_unlock(&PortP->portSem);
+ }
+ }
+
+ if ( RWORD( HostP->ParmMapP->tx_intr ) ) {
+ int port;
+
+ WWORD( HostP->ParmMapP->tx_intr , 0);
+
+ p->RIOTxCount++;
+ TxIntr++;
+ rio_dprint(RIO_DEBUG_INTR, ("TX interrupt on host %d\n", HostP-p->RIOHosts));
+
+ /*
+ ** Loop through every port.
+ ** If the port is mapped into the system ( i.e. has /dev/ttyXXXX
+ ** associated ) then it is worth checking.
+ */
+ for ( port=p->RIOFirstPortsBooted;
+ port<p->RIOLastPortsBooted+PORTS_PER_RTA; port++ ) {
+ struct Port *PortP = p->RIOPortp[port];
+ struct tty_struct *ttyP;
+ struct PKT *PacketP;
+
+ /*
+ ** not mapped in - most of the RIOPortp[] information
+ ** has not been set up!
+ */
+ if ( !PortP->Mapped ) {
+ port += 7;
+ continue; /* with the next port */
+ }
+
+ /*
+ ** If the host board isn't running, then its data structures
+ ** are no use to us - continue quietly.
+ */
+ if ( PortP->HostP != HostP ) {
+ port += 7;
+ continue; /* with the next port */
+ }
+
+ /*
+ ** Let us see - is the port open? If not, then don't service it.
+ */
+ if ( !( PortP->PortState & PORT_ISOPEN ) ) {
+ continue;
+ }
+
+ rio_dprint (RIO_DEBUG_INTR, ("Looking into port %d.\n", port));
+ /*
+ ** Lock the port before we begin working on it.
+ */
+ rio_spin_lock(&PortP->portSem);
+
+ /*
+ ** If we can't add anything to the transmit queue, then
+ ** we need do none of this processing.
+ */
+ if ( !can_add_transmit( &PacketP, PortP ) ) {
+ rio_dprint (RIO_DEBUG_INTR, ("Can't add to port, so skipping.\n"));
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+
+ /*
+ ** find corresponding tty structure. The process of mapping
+ ** the ports puts these here.
+ */
+ ttyP = PortP->gs.tty;
+ /* If ttyP is NULL, the port is getting closed. Forget about it. */
+ if (!ttyP) {
+ rio_dprint (RIO_DEBUG_INTR, ("no tty, so skipping.\n"));
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+ /*
+ ** If there is more room available we start up the transmit
+ ** data process again. This can be direct I/O, if the cookmode
+ ** is set to COOK_RAW or COOK_MEDIUM, or will be a call to the
+ ** riotproc( T_OUTPUT ) if we are in COOK_WELL mode, to fetch
+ ** characters via the line discipline. We must always call
+ ** the line discipline,
+ ** so that user input characters can be echoed correctly.
+ **
+ ** ++++ Update +++++
+ ** With the advent of double buffering, we now see if
+ ** TxBufferOut-In is non-zero. If so, then we copy a packet
+ ** to the output place, and set it going. If this empties
+ ** the buffer, then we must issue a wakeup( ) on OUT.
+ ** If it frees space in the buffer then we must issue
+ ** a wakeup( ) on IN.
+ **
+ ** ++++ Extra! Extra! If PortP->WflushFlag is set, then we
+ ** have to send a WFLUSH command down the PHB, to mark the
+ ** end point of a WFLUSH. We also need to clear out any
+ ** data from the double buffer! ( note that WflushFlag is a
+ ** *count* of the number of WFLUSH commands outstanding! )
+ **
+ ** ++++ And there's more!
+ ** If an RTA is powered off, then on again, and rebooted,
+ ** whilst it has ports open, then we need to re-open the ports.
+ ** ( reasonable enough ). We can't do this when we spot the
+ ** re-boot, in interrupt time, because the queue is probably
+ ** full. So, when we come in here, we need to test if any
+ ** ports are in this condition, and re-open the port before
+ ** we try to send any more data to it. Now, the re-booted
+ ** RTA will be discarding packets from the PHB until it
+ ** receives this open packet, but don't worry tooo much
+ ** about that. The one thing that is interesting is the
+ ** combination of this effect and the WFLUSH effect!
+ */
+ /* For now don't handle RTA reboots. -- REW.
+ Reenabled. Otherwise RTA reboots didn't work. Duh. -- REW */
+ if ( PortP->MagicFlags ) {
+#if 1
+ if ( PortP->MagicFlags & MAGIC_REBOOT ) {
+ /*
+ ** well, the RTA has been rebooted, and there is room
+ ** on its queue to add the open packet that is required.
+ **
+ ** The messy part of this line is trying to decide if
+ ** we need to call the Param function as a tty or as
+ ** a modem.
+ ** DONT USE CLOCAL AS A TEST FOR THIS!
+ **
+ ** If we can't param the port, then move on to the
+ ** next port.
+ */
+ PortP->InUse = NOT_INUSE;
+
+ rio_spin_unlock(&PortP->portSem);
+ if ( RIOParam(PortP, OPEN, ((PortP->Cor2Copy &
+ (COR2_RTSFLOW|COR2_CTSFLOW ) )==
+ (COR2_RTSFLOW|COR2_CTSFLOW ) ) ?
+ TRUE : FALSE, DONT_SLEEP ) == RIO_FAIL ) {
+ continue; /* with next port */
+ }
+ rio_spin_lock(&PortP->portSem);
+ PortP->MagicFlags &= ~MAGIC_REBOOT;
+ }
+#endif
+
+ /*
+ ** As mentioned above, this is a tacky hack to cope
+ ** with WFLUSH
+ */
+ if ( PortP->WflushFlag ) {
+ rio_dprint(RIO_DEBUG_INTR, ("Want to WFLUSH mark this port\n"));
+
+ if ( PortP->InUse )
+ rio_dprint(RIO_DEBUG_INTR, ("FAILS - PORT IS IN USE\n"));
+ }
+
+ while ( PortP->WflushFlag &&
+ can_add_transmit( &PacketP, PortP ) &&
+ ( PortP->InUse == NOT_INUSE ) ) {
+ int p;
+ struct PktCmd *PktCmdP;
+
+ rio_dprint(RIO_DEBUG_INTR, ("Add WFLUSH marker to data queue\n"));
+ /*
+ ** make it look just like a WFLUSH command
+ */
+ PktCmdP = ( struct PktCmd * )&PacketP->data[0];
+
+ WBYTE( PktCmdP->Command , WFLUSH );
+
+ p = PortP->HostPort % ( ushort )PORTS_PER_RTA;
+
+ /*
+ ** If second block of ports for 16 port RTA, add 8
+ ** to index 8-15.
+ */
+ if ( PortP->SecondBlock )
+ p += PORTS_PER_RTA;
+
+ WBYTE( PktCmdP->PhbNum, p );
+
+ /*
+ ** to make debuggery easier
+ */
+ WBYTE( PacketP->data[ 2], 'W' );
+ WBYTE( PacketP->data[ 3], 'F' );
+ WBYTE( PacketP->data[ 4], 'L' );
+ WBYTE( PacketP->data[ 5], 'U' );
+ WBYTE( PacketP->data[ 6], 'S' );
+ WBYTE( PacketP->data[ 7], 'H' );
+ WBYTE( PacketP->data[ 8], ' ' );
+ WBYTE( PacketP->data[ 9], '0'+PortP->WflushFlag );
+ WBYTE( PacketP->data[10], ' ' );
+ WBYTE( PacketP->data[11], ' ' );
+ WBYTE( PacketP->data[12], '\0' );
+
+ /*
+ ** its two bytes long!
+ */
+ WBYTE( PacketP->len , PKT_CMD_BIT | 2 );
+
+ /*
+ ** queue it!
+ */
+ if ( !( PortP->State & RIO_DELETED ) ) {
+ add_transmit( PortP );
+ /*
+ ** Count chars tx'd for port statistics reporting
+ */
+ if ( PortP->statsGather )
+ PortP->txchars += 2;
+ }
+
+ if ( --( PortP->WflushFlag ) == 0 ) {
+ PortP->MagicFlags &= ~MAGIC_FLUSH;
+ }
+
+ rio_dprint(RIO_DEBUG_INTR, ("Wflush count now stands at %d\n",
+ PortP->WflushFlag));
+ }
+ if ( PortP->MagicFlags & MORE_OUTPUT_EYGOR ) {
+ if ( PortP->MagicFlags & MAGIC_FLUSH ) {
+ PortP->MagicFlags |= MORE_OUTPUT_EYGOR;
+ }
+ else {
+ if ( !can_add_transmit( &PacketP, PortP ) ) {
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+ rio_spin_unlock(&PortP->portSem);
+ RIOTxEnable((char *)PortP);
+ rio_spin_lock(&PortP->portSem);
+ PortP->MagicFlags &= ~MORE_OUTPUT_EYGOR;
+ }
+ }
+ }
+
+
+ /*
+ ** If we can't add anything to the transmit queue, then
+ ** we need do none of the remaining processing.
+ */
+ if (!can_add_transmit( &PacketP, PortP ) ) {
+ rio_spin_unlock(&PortP->portSem);
+ continue;
+ }
+
+ rio_spin_unlock(&PortP->portSem);
+ RIOTxEnable((char *)PortP);
+ }
+ }
+}
+
+/*
+** Routine for handling received data for clist drivers.
+** NB: Called with the tty locked. The spl from the lockb( ) is passed.
+** we return the ttySpl level that we re-locked at.
+*/
+void
+RIOReceive(p, PortP)
+struct rio_info * p;
+struct Port * PortP;
+{
+ struct tty_struct *TtyP;
+ register ushort transCount;
+ struct PKT *PacketP;
+ register uint DataCnt;
+ uchar * ptr;
+ int copied =0;
+
+ static int intCount, RxIntCnt;
+
+ /*
+ ** The receive data process is to remove packets from the
+ ** PHB until there aren't any more or the current cblock
+ ** is full. When this occurs, there will be some left over
+ ** data in the packet, that we must do something with.
+ ** As we haven't unhooked the packet from the read list
+ ** yet, we can just leave the packet there, having first
+ ** made a note of how far we got. This means that we need
+ ** a pointer per port saying where we start taking the
+ ** data from - this will normally be zero, but when we
+ ** run out of space it will be set to the offset of the
+ ** next byte to copy from the packet data area. The packet
+ ** length field is decremented by the number of bytes that
+ ** we succesfully removed from the packet. When this reaches
+ ** zero, we reset the offset pointer to be zero, and free
+ ** the packet from the front of the queue.
+ */
+
+ intCount++;
+
+ TtyP = PortP->gs.tty;
+ if (!TtyP) {
+ rio_dprint (RIO_DEBUG_INTR, ("RIOReceive: tty is null. \n"));
+ return;
+ }
+
+ if (PortP->State & RIO_THROTTLE_RX) {
+ rio_dprint (RIO_DEBUG_INTR, ("RIOReceive: Throttled. Can't handle more input.\n"));
+ return;
+ }
+
+ if ( PortP->State & RIO_DELETED )
+ {
+ while ( can_remove_receive( &PacketP, PortP ) )
+ {
+ remove_receive( PortP );
+ put_free_end( PortP->HostP, PacketP );
+ }
+ }
+ else
+ {
+ /*
+ ** loop, just so long as:
+ ** i ) there's some data ( i.e. can_remove_receive )
+ ** ii ) we haven't been blocked
+ ** iii ) there's somewhere to put the data
+ ** iv ) we haven't outstayed our welcome
+ */
+ transCount = 1;
+ while ( can_remove_receive(&PacketP, PortP)
+ && transCount)
+ {
+#ifdef STATS
+ PortP->Stat.RxIntCnt++;
+#endif /* STATS */
+ RxIntCnt++;
+
+ /*
+ ** check that it is not a command!
+ */
+ if ( PacketP->len & PKT_CMD_BIT ) {
+ rio_dprint(RIO_DEBUG_INTR, ("RIO: unexpected command packet received on PHB\n"));
+ /* rio_dprint(RIO_DEBUG_INTR, (" sysport = %d\n", p->RIOPortp->PortNum)); */
+ rio_dprint(RIO_DEBUG_INTR, (" dest_unit = %d\n", PacketP->dest_unit));
+ rio_dprint(RIO_DEBUG_INTR, (" dest_port = %d\n", PacketP->dest_port));
+ rio_dprint(RIO_DEBUG_INTR, (" src_unit = %d\n", PacketP->src_unit));
+ rio_dprint(RIO_DEBUG_INTR, (" src_port = %d\n", PacketP->src_port));
+ rio_dprint(RIO_DEBUG_INTR, (" len = %d\n", PacketP->len));
+ rio_dprint(RIO_DEBUG_INTR, (" control = %d\n", PacketP->control));
+ rio_dprint(RIO_DEBUG_INTR, (" csum = %d\n", PacketP->csum));
+ rio_dprint(RIO_DEBUG_INTR, (" data bytes: "));
+ for ( DataCnt=0; DataCnt<PKT_MAX_DATA_LEN; DataCnt++ )
+ rio_dprint(RIO_DEBUG_INTR, ("%d\n", PacketP->data[DataCnt]));
+ remove_receive( PortP );
+ put_free_end( PortP->HostP, PacketP );
+ continue; /* with next packet */
+ }
+
+ /*
+ ** How many characters can we move 'upstream' ?
+ **
+ ** Determine the minimum of the amount of data
+ ** available and the amount of space in which to
+ ** put it.
+ **
+ ** 1. Get the packet length by masking 'len'
+ ** for only the length bits.
+ ** 2. Available space is [buffer size] - [space used]
+ **
+ ** Transfer count is the minimum of packet length
+ ** and available space.
+ */
+
+ transCount = min(PacketP->len & PKT_LEN_MASK,
+ TTY_FLIPBUF_SIZE - TtyP->flip.count);
+ rio_dprint(RIO_DEBUG_REC, ("port %d: Copy %d bytes\n",
+ PortP->PortNum, transCount ) );
+ /*
+ ** To use the following 'kkprintfs' for debugging - change the '#undef'
+ ** to '#define', (this is the only place ___DEBUG_IT___ occurs in the
+ ** driver).
+ */
+#undef ___DEBUG_IT___
+#ifdef ___DEBUG_IT___
+ kkprintf("I:%d R:%d P:%d Q:%d C:%d F:%x ",
+ intCount,
+ RxIntCnt,
+ PortP->PortNum,
+ TtyP->rxqueue.count,
+ transCount,
+ TtyP->flags );
+#endif
+ ptr = (uchar *) PacketP->data + PortP->RxDataStart;
+
+ rio_memcpy_fromio (TtyP->flip.char_buf_ptr, ptr, transCount);
+ memset(TtyP->flip.flag_buf_ptr, TTY_NORMAL, transCount);
+
+#ifdef STATS
+ /*
+ ** keep a count for statistical purposes
+ */
+ PortP->Stat.RxCharCnt += transCount;
+#endif
+ PortP->RxDataStart += transCount;
+ PacketP->len -= transCount;
+ copied += transCount;
+ TtyP->flip.count += transCount;
+ TtyP->flip.char_buf_ptr += transCount;
+ TtyP->flip.flag_buf_ptr += transCount;
+
+
+#ifdef ___DEBUG_IT___
+ kkprintf("T:%d L:%d\n", DataCnt, PacketP->len );
+#endif
+
+ if ( PacketP->len == 0 )
+ {
+ /*
+ ** If we have emptied the packet, then we can
+ ** free it, and reset the start pointer for
+ ** the next packet.
+ */
+ remove_receive( PortP );
+ put_free_end( PortP->HostP, PacketP );
+ PortP->RxDataStart = 0;
+#ifdef STATS
+ /*
+ ** more lies ( oops, I mean statistics )
+ */
+ PortP->Stat.RxPktCnt++;
+#endif /* STATS */
+ }
+ }
+ }
+ if (copied) {
+ rio_dprint ( RIO_DEBUG_REC, ("port %d: pushing tty flip buffer: %d total bytes copied.\n", PortP->PortNum, copied));
+ tty_flip_buffer_push (TtyP);
+ }
+
+ return;
+}
+
+#ifdef FUTURE_RELEASE
+/*
+** The proc routine called by the line discipline to do the work for it.
+** The proc routine works hand in hand with the interrupt routine.
+*/
+int
+riotproc(p, tp, cmd, port)
+struct rio_info * p;
+register struct ttystatics *tp;
+int cmd;
+int port;
+{
+ register struct Port *PortP;
+ int SysPort;
+ struct PKT *PacketP;
+
+ SysPort = port; /* Believe me, it works. */
+
+ if ( SysPort < 0 || SysPort >= RIO_PORTS ) {
+ rio_dprint(RIO_DEBUG_INTR, ("Illegal port %d derived from TTY in riotproc()\n",SysPort));
+ return 0;
+ }
+ PortP = p->RIOPortp[SysPort];
+
+ if ((uint)PortP->PhbP < (uint)PortP->Caddr ||
+ (uint)PortP->PhbP >= (uint)PortP->Caddr+SIXTY_FOUR_K ) {
+ rio_dprint(RIO_DEBUG_INTR, ("RIO: NULL or BAD PhbP on sys port %d in proc routine\n",
+ SysPort));
+ rio_dprint(RIO_DEBUG_INTR, (" PortP = 0x%x\n",PortP));
+ rio_dprint(RIO_DEBUG_INTR, (" PortP->PhbP = 0x%x\n",PortP->PhbP));
+ rio_dprint(RIO_DEBUG_INTR, (" PortP->Caddr = 0x%x\n",PortP->PhbP));
+ rio_dprint(RIO_DEBUG_INTR, (" PortP->HostPort = 0x%x\n",PortP->HostPort));
+ return 0;
+ }
+
+ switch(cmd) {
+ case T_WFLUSH:
+ rio_dprint(RIO_DEBUG_INTR, "T_WFLUSH\n");
+ /*
+ ** Because of the spooky way the RIO works, we don't need
+ ** to issue a flush command on any of the SET*F commands,
+ ** as that causes trouble with getty and login, which issue
+ ** these commands to incur a READ flush, and rely on the fact
+ ** that the line discipline does a wait for drain for them.
+ ** As the rio doesn't wait for drain, the write flush would
+ ** destroy the Password: prompt. This isn't very friendly, so
+ ** here we only issue a WFLUSH command if we are in the interrupt
+ ** routine, or we aren't executing a SET*F command.
+ */
+ if ( PortP->HostP->InIntr || !PortP->FlushCmdBodge ) {
+ /*
+ ** form a wflush packet - 1 byte long, no data
+ */
+ if ( PortP->State & RIO_DELETED ) {
+ rio_dprint(RIO_DEBUG_INTR, ("WFLUSH on deleted RTA\n"));
+ }
+ else {
+ if ( RIOPreemptiveCmd(p, PortP, WFLUSH ) == RIO_FAIL ) {
+ rio_dprint(RIO_DEBUG_INTR, ("T_WFLUSH Command failed\n"));
+ }
+ else
+ rio_dprint(RIO_DEBUG_INTR, ("T_WFLUSH Command\n"));
+ }
+ /*
+ ** WFLUSH operation - flush the data!
+ */
+ PortP->TxBufferIn = PortP->TxBufferOut = 0;
+ }
+ else {
+ rio_dprint(RIO_DEBUG_INTR, ("T_WFLUSH Command ignored\n"));
+ }
+ /*
+ ** sort out the line discipline
+ */
+ if (PortP->CookMode == COOK_WELL)
+ goto start;
+ break;
+
+ case T_RESUME:
+ rio_dprint(RIO_DEBUG_INTR, ("T_RESUME\n"));
+ /*
+ ** send pre-emptive resume packet
+ */
+ if ( PortP->State & RIO_DELETED ) {
+ rio_dprint(RIO_DEBUG_INTR, ("RESUME on deleted RTA\n"));
+ }
+ else {
+ if ( RIOPreemptiveCmd(p, PortP, RESUME ) == RIO_FAIL ) {
+ rio_dprint(RIO_DEBUG_INTR, ("T_RESUME Command failed\n"));
+ }
+ }
+ /*
+ ** and re-start the sender software!
+ */
+ if (PortP->CookMode == COOK_WELL)
+ goto start;
+ break;
+
+ case T_TIME:
+ rio_dprint(RIO_DEBUG_INTR, ("T_TIME\n"));
+ /*
+ ** T_TIME is called when xDLY is set in oflags and
+ ** the line discipline timeout has expired. It's
+ ** function in life is to clear the TIMEOUT flag
+ ** and to re-start output to the port.
+ */
+ /*
+ ** Fall through and re-start output
+ */
+ case T_OUTPUT:
+start:
+ if ( PortP->MagicFlags & MAGIC_FLUSH ) {
+ PortP->MagicFlags |= MORE_OUTPUT_EYGOR;
+ return 0;
+ }
+ RIOTxEnable((char *)PortP);
+ PortP->MagicFlags &= ~MORE_OUTPUT_EYGOR;
+ /*rio_dprint(RIO_DEBUG_INTR, PortP,DBG_PROC,"T_OUTPUT finished\n");*/
+ break;
+
+ case T_SUSPEND:
+ rio_dprint(RIO_DEBUG_INTR, ("T_SUSPEND\n"));
+ /*
+ ** send a suspend pre-emptive packet.
+ */
+ if ( PortP->State & RIO_DELETED ) {
+ rio_dprint(RIO_DEBUG_INTR, ("SUSPEND deleted RTA\n"));
+ }
+ else {
+ if ( RIOPreemptiveCmd(p, PortP, SUSPEND ) == RIO_FAIL ) {
+ rio_dprint(RIO_DEBUG_INTR, ("T_SUSPEND Command failed\n"));
+ }
+ }
+ /*
+ ** done!
+ */
+ break;
+
+ case T_BLOCK:
+ rio_dprint(RIO_DEBUG_INTR, ("T_BLOCK\n"));
+ break;
+
+ case T_RFLUSH:
+ rio_dprint(RIO_DEBUG_INTR, ("T_RFLUSH\n"));
+ if ( PortP->State & RIO_DELETED ) {
+ rio_dprint(RIO_DEBUG_INTR, ("RFLUSH on deleted RTA\n"));
+ PortP->RxDataStart = 0;
+ }
+ else {
+ if ( RIOPreemptiveCmd( p, PortP, RFLUSH ) == RIO_FAIL ) {
+ rio_dprint(RIO_DEBUG_INTR, ("T_RFLUSH Command failed\n"));
+ return 0;
+ }
+ PortP->RxDataStart = 0;
+ while ( can_remove_receive(&PacketP, PortP) ) {
+ remove_receive(PortP);
+ ShowPacket(DBG_PROC, PacketP );
+ put_free_end(PortP->HostP, PacketP );
+ }
+ if ( PortP->PhbP->handshake == PHB_HANDSHAKE_SET ) {
+ /*
+ ** MAGIC!
+ */
+ rio_dprint(RIO_DEBUG_INTR, ("Set receive handshake bit\n"));
+ PortP->PhbP->handshake |= PHB_HANDSHAKE_RESET;
+ }
+ }
+ break;
+ /* FALLTHROUGH */
+ case T_UNBLOCK:
+ rio_dprint(RIO_DEBUG_INTR, ("T_UNBLOCK\n"));
+ /*
+ ** If there is any data to receive set a timeout to service it.
+ */
+ RIOReceive(p, PortP);
+ break;
+
+ case T_BREAK:
+ rio_dprint(RIO_DEBUG_INTR, ("T_BREAK\n"));
+ /*
+ ** Send a break command. For Sys V
+ ** this is a timed break, so we
+ ** send a SBREAK[time] packet
+ */
+ /*
+ ** Build a BREAK command
+ */
+ if ( PortP->State & RIO_DELETED ) {
+ rio_dprint(RIO_DEBUG_INTR, ("BREAK on deleted RTA\n"));
+ }
+ else {
+ if (RIOShortCommand(PortP,SBREAK,2,
+ p->RIOConf.BreakInterval)==RIO_FAIL) {
+ rio_dprint(RIO_DEBUG_INTR, ("SBREAK RIOShortCommand failed\n"));
+ }
+ }
+
+ /*
+ ** done!
+ */
+ break;
+
+ case T_INPUT:
+ rio_dprint(RIO_DEBUG_INTR, ("Proc T_INPUT called - I don't know what to do!\n"));
+ break;
+ case T_PARM:
+ rio_dprint(RIO_DEBUG_INTR, ("Proc T_PARM called - I don't know what to do!\n"));
+ break;
+
+ case T_SWTCH:
+ rio_dprint(RIO_DEBUG_INTR, ("Proc T_SWTCH called - I don't know what to do!\n"));
+ break;
+
+ default:
+ rio_dprint(RIO_DEBUG_INTR, ("Proc UNKNOWN command %d\n",cmd));
+ }
+ /*
+ ** T_OUTPUT returns without passing through this point!
+ */
+ /*rio_dprint(RIO_DEBUG_INTR, PortP,DBG_PROC,"riotproc done\n");*/
+ return(0);
+}
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)