📄 riointr.c
字号:
/*** -----------------------------------------------------------------------------**** 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_LABELSstatic char *_riointr_c_sccs_ = "@(#)riointr.c 1.2";#endif#define __NO_VERSION__#include <linux/module.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/tty.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.*/voidriopoll(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 */voidRIOTxEnable(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_dprintk (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_dprintk (RIO_DEBUG_INTR, "rio: 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_dprintk (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_dprintk (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.*/voidriointr(p)struct rio_info * p;{ int host; for ( host=0; host<p->RIONumHosts; host++ ) { struct Host *HostP = &p->RIOHosts[host]; rio_dprintk (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;voidRIOServiceHost(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_dprintk (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 ) ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -