Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2030 lines
71 KiB

/*-------------------------------------------------------------------
| ssci.c - low level interface routines to rocketport hardware.
03-16-98, add sModemSendROW for RocketModem - jl
02-05-98, add sModemReset for RocketModem - jl
10-22-96, add ReadAiopID to PCI case as hardware verification. - kpb
Copyright 1993-98 Comtrol Corporation. All rights reserved.
|--------------------------------------------------------------------*/
#include "precomp.h"
#define ONE_SECOND 10
#define TWO_SECONDS (2 * ONE_SECOND)
#define THREE_SECONDS (3 * ONE_SECOND)
#define FOUR_SECONDS (4 * ONE_SECOND)
#define FIVE_SECONDS (5 * ONE_SECOND)
#define TENTH_SECOND (ONE_SECOND / 10)
#define HALF_SECOND (ONE_SECOND / 2)
// #define DUMPDATA 1
#ifdef DUMPDATA
// in case of changed responses from modem, the following allows
// the unrecognized responses to be dumped to log...
void DumpResponseByte(char buffer);
char DumpArray[512];
int DumpIndex = 0;
#endif
#ifdef PPC
// #define INTEL_ORDER 1
#endif
#ifdef ALPHA
#define INTEL_ORDER 1
#define WORD_ALIGN 1
#endif
#ifdef i386
#define INTEL_ORDER 1
#endif
#ifdef MIPS
// #define INTEL_ORDER 1
#endif
/* Master copy of AIOP microcode. Organized as DWORDs. The 1st word of each
DWORD holds the microcode index, the second word holds the microcode
data. */
unsigned char MasterMCode1[MCODE1_SIZE] =
{
/* indl indh dlo dhi */
0x00, 0x09, 0xf6, 0x82,
0x02, 0x09, 0x86, 0xfb,
0x04, 0x09, 0x00, 0x0a,
0x06, 0x09, 0x01, 0x0a,
0x08, 0x09, 0x8a, 0x13,
0x0a, 0x09, 0xc5, 0x11,
0x0c, 0x09, 0x86, 0x85,
0x0e, 0x09, 0x20, 0x0a,
0x10, 0x09, 0x21, 0x0a,
0x12, 0x09, 0x41, 0xff,
0x14, 0x09, 0x82, 0x00,
0x16, 0x09, 0x82, 0x7b,
0x18, 0x09, 0x8a, 0x7d,
0x1a, 0x09, 0x88, 0x81,
0x1c, 0x09, 0x86, 0x7a,
0x1e, 0x09, 0x84, 0x81,
0x20, 0x09, 0x82, 0x7c,
0x22, 0x09, 0x0a, 0x0a
};
/* Registers within microcode. Organized as DWORDs. The 1st word of each
DWORD holds the microcode index of that register, the 2nd DWORD holds
the current contents of that register. */
unsigned char MCode1Reg[MCODE1REG_SIZE] =
{
/* indl indh dlo dhi */
0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */
0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */
0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */
0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */
0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */
0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */
0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */
0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */
0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */
0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */
0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */
0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */
0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */
};
/* Controller structures */
/* IRQ number to MUDBAC register 2 mapping */
unsigned char sIRQMap[16] =
{
0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80
};
//unsigned char sBitMapClrTbl[8] =
// 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f
//unsigned char sBitMapSetTbl[8] =
// 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
/***************************************************************************
Function: sInitController
Purpose: Initialization of controller global registers and controller
structure.
Call: **** This version of the call for all except Windows NT ****
sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize,
IRQNum,Frequency,PeriodicOnly)
Call: **** This version of the call for Windows NT ***
sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,PhyAiopIOList,
AiopIOListSize,IRQNum,Frequency,PeriodicOnly)
CONTROLLER_T *CtlP; Ptr to controller structure
int CtlNum; Controller number
BIOA_T MudbacIO; Mudbac base I/O address. For Win NT this
is the TranslatedAddress returned by HalTranslateBusAddress().
BIOA_T *AiopIOList; List of I/O addresses for each AIOP.
This list must be in the order the AIOPs will be found on the
controller. Once an AIOP in the list is not found, it is
assumed that there are no more AIOPs on the controller.
For Win NT these are the TranslatedAddresses returned by
HalTranslateBusAddress().
unsigned int *PhyAiopIOList; List of physical I/O addresses for
each AIOP, used by Win NT only. These are the physical
addresses corresponding to the TranslatedAddresses in
AiopIOList.
int AiopIOListSize; Number of addresses in AiopIOList
int IRQNum; Interrupt Request number. Can be any of the following:
0: Disable global interrupts
3: IRQ 3
4: IRQ 4
5: IRQ 5
9: IRQ 9
10: IRQ 10
11: IRQ 11
12: IRQ 12
15: IRQ 15
unsigned char Frequency: A flag identifying the frequency
of the periodic interrupt, can be any one of the following:
FREQ_DIS - periodic interrupt disabled
FREQ_137HZ - 137 Hertz
FREQ_69HZ - 69 Hertz
FREQ_34HZ - 34 Hertz
FREQ_17HZ - 17 Hertz
FREQ_9HZ - 9 Hertz
FREQ_4HZ - 4 Hertz
If IRQNum is set to 0 the Frequency parameter is
overidden, it is forced to a value of FREQ_DIS.
int PeriodicOnly: TRUE if all interrupts except the periodic
interrupt are to be blocked.
FALSE is both the periodic interrupt and
other channel interrupts are allowed.
If IRQNum is set to 0 the PeriodicOnly parameter is
overidden, it is forced to a value of FALSE.
Return: int: 0 on success, errcode on failure
Comments: This function must be called immediately after sSetChannelDefaults()
for each controller in the system.
If periodic interrupts are to be disabled but AIOP interrupts
are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE.
If interrupts are to be completely disabled set IRQNum to 0.
Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an
invalid combination.
This function performs initialization of global interrupt modes,
but it does not actually enable global interrupts. To enable
and disable global interrupts use functions sEnGlobalInt() and
sDisGlobalInt(). Enabling of global interrupts is normally not
done until all other initializations are complete.
Even if interrupts are globally enabled, they must also be
individually enabled for each channel that is to generate
interrupts.
Warnings: No range checking on any of the parameters is done.
No context switches are allowed while executing this function.
After this function all AIOPs on the controller are disabled,
they can be enabled with sEnAiop().
*/
int sInitController(CONTROLLER_T *CtlP,
// int CtlNum,
BIOA_T MudbacIO,
BIOA_T *AiopIOList,
unsigned int *PhyAiopIOList,
int AiopIOListSize,
int IRQNum,
unsigned char Frequency,
int PeriodicOnly,
int BusType,
int prescaler)
{
// unsigned char MudbacID; /* MUDBAC ID byte*/
int i;
BIOA_T io; /* an I/O address */
unsigned int pio; /* physical I/O address for Win NT */
WIOA_T IoIndexAddr;
WIOA_T IoIndexData;
// IoIndexAddr=(PUSHORT)((PUCHAR)io+_INDX_ADDR);
// IoIndexData=(PUSHORT)((PUCHAR)io+_INDX_DATA);
//CtlP->CtlNum = CtlNum;
CtlP->BusType = BusType;
CtlP->PortsPerAiop = 8;
if (CtlP->BusType == Isa)
{
MyKdPrint(D_Ssci,("One ISA ROCKET \n"))
CtlP->CtlID = CTLID_0001; /* controller release 1 */
if (AiopIOListSize == 0)
AiopIOListSize = 32; // we figure out
/* If we get here controller found, init MUDBAC and controller struct */
CtlP->MBaseIO = MudbacIO;
CtlP->MReg1IO = MudbacIO + 1;
CtlP->MReg2IO = MudbacIO + 2;
CtlP->MReg3IO = MudbacIO + 3;
if (IRQNum > 15) IRQNum = 0; // limit
if (sIRQMap[IRQNum] == 0) // interrupts globally disabled
{
MyKdPrint(D_Ssci,("No IRQ\n"))
CtlP->MReg2 = 0; // interrupt disable
CtlP->MReg3 = 0; // no periodic interrupts
}
else
{
MyKdPrint(D_Ssci,("IRQ used:%d\n",IRQNum))
CtlP->MReg2 = sIRQMap[IRQNum]; // set IRQ number
CtlP->MReg3 = Frequency; // set frequency
if(PeriodicOnly) // periodic interrupt only
{
CtlP->MReg3 |= PERIODIC_ONLY;
}
}
sOutB(CtlP->MReg2IO,CtlP->MReg2);
sOutB(CtlP->MReg3IO,CtlP->MReg3);
sControllerEOI(CtlP); /* clear EOI if warm init */
sDisGlobalInt(CtlP);
MyKdPrint(D_Ssci,("Disabled ISA interrupts Mreg2:%x := %x\n",
CtlP->MReg2IO,CtlP->MReg2))
/* Init AIOPs */
CtlP->NumAiop = 0;
for(i = 0;i < AiopIOListSize;i++)
{
io = AiopIOList[i];
IoIndexAddr=(PUSHORT)(io+_INDX_ADDR);
IoIndexData=(PUSHORT)(io+_INDX_DATA);
pio = PhyAiopIOList[i]; /* io points to port, pio is the adrs */
MyKdPrint(D_Ssci,("io=%xH pio=%xH\n", (unsigned int)io,
(unsigned int)pio))
CtlP->AiopIO[i] = (WIOA_T)io;
CtlP->AiopIntChanIO[i] = io + _INT_CHAN;
MyKdPrint(D_Ssci,("Setup AIOP io, MReg2IO=%xH\n",
(unsigned int)CtlP->MReg2IO))
sOutB((CtlP->MReg2IO),(unsigned char)(CtlP->MReg2 | (i & 0x03))); /* AIOP index */
sOutB(MudbacIO,(unsigned char)(pio >> 6)); /* set up AIOP I/O in MUDBAC */
MyKdPrint(D_Ssci,("Enable AIOP\n"))
sEnAiop(CtlP,i); /* enable the AIOP */
MyKdPrint(D_Ssci,("Read AIOP ID\n"))
CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */
if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */
{
sDisAiop(CtlP,i); /* disable AIOP */
break; /* done looking for AIOPs */
}
MyKdPrint(D_Ssci,("Read AIOP numchan\n"))
CtlP->AiopNumChan[i] = sReadAiopNumChan((WIOA_T)io); /* num channels in AIOP */
MyKdPrint(D_Ssci,("Setup Aiop Clk\n"))
sOutW((WIOA_T)IoIndexAddr,_CLK_PRE); /* clock prescaler */
//sOutB((PUCHAR)IoIndexData,CLOCK_PRESC);
sOutB((PUCHAR)IoIndexData, (BYTE)prescaler);
CtlP->NumAiop++; /* bump count of AIOPs */
MyKdPrint(D_Ssci,("Setup aiop done\n"))
sDisAiop(CtlP,i); /* disable AIOP */
}
MyKdPrint(D_Ssci,("One ISA ROCKET with %d aiops\n",CtlP->NumAiop))
if(CtlP->NumAiop == 0) {
MyKdPrint(D_Error,("ISA NumAiop == 0\n"))
return 1; // error
}
return 0; // ok // old:(CtlP->NumAiop);
} // end of ISA controller init
else if(CtlP->BusType == PCIBus)
{
MyKdPrint(D_Ssci,("One PCI ROCKET \n"))
//CtlP->CtlNum = CtlNum;
CtlP->CtlID = CTLID_0001; /* controller release 1 */
MyKdPrint(D_Ssci,("Ctrl(%x) IrqNum: %x \n", CtlP, IRQNum))
if(IRQNum == 0) /* interrupts disabled for this controler*/
{
CtlP->PCI1 = 0x0008; /* no periodic, interrupts disabled */
}
else
{
Frequency >>= 4; /*Right shift 4 times to move 4:7 to 0:3 */
CtlP->PCI1 |= Frequency;
if(PeriodicOnly) /* periodic interrupt only */
{
CtlP->PCI1 |= PER_ONLY_PCI;
}
}
CtlP->PCI1IO = (WIOA_T)((BIOA_T)AiopIOList[0] + _PCI_INT_FUNC);
MyKdPrint(D_Ssci,("Setting PCI config reg with %x at %x\n",
CtlP->PCI1,CtlP->PCI1IO)) // move these calls to ssci.h
sOutW(CtlP->PCI1IO,CtlP->PCI1);
////////////////////new/////////////////////
///CtlP->PortsPerAiop = 8;
switch (CtlP->PCI_DevID)
{
case PCI_DEVICE_4Q: // 4 Port Quadcable
case PCI_DEVICE_4RJ: // 4 Port RJ
CtlP->PortsPerAiop = 4;
break;
case PCI_DEVICE_8RJ: // 8 Port RJ
case PCI_DEVICE_8O: // 8 Port Octacable
case PCI_DEVICE_8I: // 8 Port interface
case PCI_DEVICE_16I: //16 Port interface
case PCI_DEVICE_32I: // 32 Port interface
case PCI_DEVICE_SIEMENS8 :
case PCI_DEVICE_SIEMENS16 :
CtlP->PortsPerAiop = 8;
break;
case PCI_DEVICE_RMODEM6 :
CtlP->PortsPerAiop = 6;
break;
case PCI_DEVICE_RMODEM4 :
CtlP->PortsPerAiop = 4;
break;
case PCI_DEVICE_RPLUS4 :
case PCI_DEVICE_RPLUS8 :
CtlP->PortsPerAiop = 4;
break;
case PCI_DEVICE_RPLUS2 :
case PCI_DEVICE_422RPLUS2 :
CtlP->PortsPerAiop = 2;
break;
default:
//Eprintf("Err,Bad PCI DevID:%d", CtlP->PCI_DevID);
break;
} // switch
///////////////////////////////////////////
/* Init AIOPs */
CtlP->NumAiop = 0;
for(i=0; i < AiopIOListSize; i++)
{
io = AiopIOList[i];
CtlP->AiopIO[i] = (WIOA_T)io;
CtlP->AiopIntChanIO[i] = (BIOA_T)io + _INT_CHAN;
// 10-22-96, add this(only hardware-verification done) kpb.
CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */
if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */
{
break; /* done looking for AIOPs */
}
///////old CtlP->AiopNumChan[i] = sReadAiopNumChan((WIOA_T)io); /* num channels in AIOP */
////////////////////new///////////////////////
CtlP->AiopNumChan[i] = CtlP->PortsPerAiop; /* num channels in AIOP */
///////////////////////////////////////////////////////////////
IoIndexAddr=(WIOA_T)((BIOA_T)io+_INDX_ADDR);
IoIndexData=(WIOA_T)((BIOA_T)io+_INDX_DATA);
sOutW((WIOA_T)IoIndexAddr,_CLK_PRE); /* clock prescaler */
sOutB((BIOA_T)IoIndexData, (BYTE)prescaler);
CtlP->NumAiop++; /* bump count of AIOPs */
}
sDisGlobalIntPCI(CtlP);
sPCIControllerEOI(CtlP); /* clear EOI if warm init */
MyKdPrint(D_Ssci,("One PCI ROCKET with %d aiops\n",CtlP->NumAiop))
if(CtlP->NumAiop == 0) {
MyKdPrint(D_Error,("PCI NumAiop == 0\n"))
return 2; // error
}
return 0; // old:(CtlP->NumAiop);
} /*end of PCI rocket INIT */
else { /* not PCI or ISA */
MyKdPrint(D_Error,("Not ISA or PCI\n"))
return 3; // old:(CTLID_NULL);
}
return 0;
}
/***************************************************************************
Function: sReadAiopID
Purpose: Read the AIOP idenfication number directly from an AIOP.
Call: sReadAiopID(io)
BIOA_T io: AIOP base I/O address
Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X
is replace by an identifying number.
Flag AIOPID_NULL if no valid AIOP is found
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
int _CDECL sReadAiopID(BIOA_T io)
{
unsigned char AiopID; /* ID byte from AIOP */
sOutB(io + _CMD_REG,RESET_ALL); /* reset AIOP */
sOutB(io + _CMD_REG,0x0);
AiopID = sInB(io + _CHN_STAT0) & 0x07;
if (AiopID == 0x06) /* AIOP release 1 */
return(AIOPID_0001);
else /* AIOP does not exist */
return(AIOPID_NULL);
}
/***************************************************************************
Function: sReadAiopNumChan
Purpose: Read the number of channels available in an AIOP directly from
an AIOP.
Call: sReadAiopNumChan(io)
WIOA_T io: AIOP base I/O address
Return: int: The number of channels available
Comments: The number of channels is determined by write/reads from identical
offsets within the SRAM address spaces for channels 0 and 4.
If the channel 4 space is mirrored to channel 0 it is a 4 channel
AIOP, otherwise it is an 8 channel.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
int _CDECL sReadAiopNumChan(WIOA_T io)
{
unsigned int x;
WIOA_T IoIndexAddr;
WIOA_T IoIndexData;
IoIndexAddr = (PUSHORT)((PUCHAR)io+_INDX_ADDR);
IoIndexData = (PUSHORT)((PUCHAR)io+_INDX_DATA);
sOutDW((DWIOA_T)IoIndexAddr, 0x12340000L); /* write to chan 0 SRAM */
sOutW(IoIndexAddr,0); /* read from SRAM, chan 0 */
x = sInW(IoIndexData);
sOutW(IoIndexAddr, 0x4000); /* read from SRAM, chan 4 */
if (x != sInW(IoIndexData)) /* if different must be 8 chan */
return(8);
else
return(4);
}
/***************************************************************************
Function: sInitChan
Purpose: Initialization of a channel and channel structure
Call: sInitChan(CtlP,ChP,AiopNum,ChanNum)
CONTROLLER_T *CtlP; Ptr to controller structure
CHANPTR_T ChP; Ptr to channel structure
int AiopNum; AIOP number within controller
int ChanNum; Channel number within AIOP
Return: int: TRUE if initialization succeeded, FALSE if it fails because channel
number exceeds number of channels available in AIOP.
Comments: This function must be called before a channel can be used.
Warnings: No range checking on any of the parameters is done.
No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
int _CDECL sInitChan(CONTROLLER_T *CtlP,
CHANPTR_T ChP,
int AiopNum,
int ChanNum)
{
int i;
WIOA_T AiopIO;
WIOA_T ChIOOff; /* I/O offset of chan with AIOP */
unsigned char *ChMCode; /* channel copy of microcode */
unsigned char *MasterMCode; /* master copy of microcode */
unsigned int ChOff; /* SRAM offset of channel within AIOP */
static unsigned char MCode[4]; /* local copy of microcode double word*/
WIOA_T AiopIndexAddr;
if(ChanNum >= CtlP->AiopNumChan[AiopNum])
return(FALSE); /* exceeds num chans in AIOP */
/* Channel, AIOP, and controller identifiers */
ChP->CtlP = CtlP;
ChP->ChanID = CtlP->AiopID[AiopNum];
ChP->AiopNum = AiopNum;
ChP->ChanNum = ChanNum;
/* Tx FIFO size */
sSetTxSize(ChP,MAXTX_SIZE);
/* Global direct addresses */
AiopIO = CtlP->AiopIO[AiopNum];
ChP->Cmd = (BIOA_T)AiopIO + _CMD_REG;
ChP->IntChan = (BIOA_T)AiopIO + _INT_CHAN;
ChP->IntMask = (BIOA_T)AiopIO + _INT_MASK;
AiopIndexAddr=(WIOA_T)((BIOA_T)AiopIO+_INDX_ADDR);
ChP->IndexAddr = (DWIOA_T)AiopIndexAddr;
ChP->IndexData = (WIOA_T)((BIOA_T)AiopIO + _INDX_DATA);
/* Channel direct addresses */
ChIOOff = (WIOA_T)((BIOA_T)AiopIO + ChP->ChanNum * 2);
ChP->TxRxData = (WIOA_T)((BIOA_T)ChIOOff + _TD0);
ChP->ChanStat = (WIOA_T)((BIOA_T)ChIOOff + _CHN_STAT0);
ChP->TxRxCount =(WIOA_T)((BIOA_T)ChIOOff + _FIFO_CNT0);
ChP->IntID = (BIOA_T)AiopIO + ChP->ChanNum + _INT_ID0;
/* Channel microcode initialization. This writes a complete copy
of the microcode into the SRAM. */
MasterMCode = MasterMCode1;
for(i = 0;i < MCODE1_SIZE; i+=4)
{
/* get low byte of index */
MCode[0] = MasterMCode[i];
/* get high byte of index */
MCode[1] = MasterMCode[i+1] + 0x10 * ChanNum;
/* get low microcode byte */
MCode[2] = MasterMCode[i+2];
/* get high microcode byte */
MCode[3] = MasterMCode[i+3];
sOutDW(ChP->IndexAddr,*((ULONGPTR_T)&MCode[0]));
}
/* Initialize SSCI copy of microcode registers. This saves only the portion
of the microcode that will be used as registers. */
ChMCode = ChP->MCode;
MasterMCode = MCode1Reg;
for(i = 0;i < MCODE1REG_SIZE; i+=4)
{
/* low byte of index */
ChMCode[i] = MasterMCode[i];
/* high byte of index */
ChMCode[i+1] = MasterMCode[i+1] + 0x10 * ChanNum;
/* low microcode byte */
ChMCode[i+2] = MasterMCode[i+2];
/* high microcode byte */
ChMCode[i+3] = MasterMCode[i+3];
}
/* Indexed registers */
ChOff = (unsigned int)ChanNum * 0x1000;
ChP->BaudDiv[0] = (unsigned char)(ChOff + _BAUD);
ChP->BaudDiv[1] = (unsigned char)((ChOff + _BAUD) >> 8);
//ChP->BaudDiv[2] = (unsigned char)BRD9600;
//ChP->BaudDiv[3] = (unsigned char)(BRD9600 >> 8);
// just default the baud register to something..
ChP->BaudDiv[2] = (unsigned char)47;
ChP->BaudDiv[3] = (unsigned char)(47 >> 8);
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->BaudDiv[0]);
ChP->TxControl[0] = (unsigned char)(ChOff + _TX_CTRL);
ChP->TxControl[1] = (unsigned char)((ChOff + _TX_CTRL) >> 8);
ChP->TxControl[2] = 0;
ChP->TxControl[3] = 0;
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxControl[0]);
ChP->RxControl[0] = (unsigned char)(ChOff + _RX_CTRL);
ChP->RxControl[1] = (unsigned char)((ChOff + _RX_CTRL) >> 8);
ChP->RxControl[2] = 0;
ChP->RxControl[3] = 0;
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->RxControl[0]);
ChP->TxEnables[0] = (unsigned char)(ChOff + _TX_ENBLS);
ChP->TxEnables[1] = (unsigned char)((ChOff + _TX_ENBLS) >> 8);
ChP->TxEnables[2] = 0;
ChP->TxEnables[3] = 0;
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxEnables[0]);
ChP->TxCompare[0] = (unsigned char)(ChOff + _TXCMP1);
ChP->TxCompare[1] = (unsigned char)((ChOff + _TXCMP1) >> 8);
ChP->TxCompare[2] = 0;
ChP->TxCompare[3] = 0;
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxCompare[0]);
ChP->TxReplace1[0] = (unsigned char)(ChOff + _TXREP1B1);
ChP->TxReplace1[1] = (unsigned char)((ChOff + _TXREP1B1) >> 8);
ChP->TxReplace1[2] = 0;
ChP->TxReplace1[3] = 0;
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxReplace1[0]);
ChP->TxReplace2[0] = (unsigned char)(ChOff + _TXREP2);
ChP->TxReplace2[1] = (unsigned char)((ChOff + _TXREP2) >> 8);
ChP->TxReplace2[2] = 0;
ChP->TxReplace2[3] = 0;
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxReplace2[0]);
ChP->TxFIFOPtrs = ChOff + _TXF_OUTP;
ChP->TxFIFO = ChOff + _TX_FIFO;
sOutB(ChP->Cmd,(unsigned char)(ChanNum | RESTXFCNT)); /* apply reset Tx FIFO count */
sOutB(ChP->Cmd,(unsigned char)ChanNum); /* remove reset Tx FIFO count */
sOutW((WIOA_T)ChP->IndexAddr,(USHORT)ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */
sOutW(ChP->IndexData,0);
ChP->RxFIFOPtrs = ChOff + _RXF_OUTP;
ChP->RxFIFO = ChOff + _RX_FIFO;
sOutB(ChP->Cmd,(unsigned char)(ChanNum | RESRXFCNT)); /* apply reset Rx FIFO count */
sOutB(ChP->Cmd,(unsigned char)ChanNum); /* remove reset Rx FIFO count */
sOutW((WIOA_T)ChP->IndexAddr,(USHORT)ChP->RxFIFOPtrs); /* clear Rx out ptr */
sOutW(ChP->IndexData,0);
sOutW((WIOA_T)ChP->IndexAddr,(USHORT)(ChP->RxFIFOPtrs + 2)); /* clear Rx in ptr */
sOutW(ChP->IndexData,0);
ChP->TxPrioCnt = ChOff + _TXP_CNT;
sOutW((WIOA_T)ChP->IndexAddr,(USHORT)ChP->TxPrioCnt);
sOutB((PUCHAR)ChP->IndexData,0);
ChP->TxPrioPtr = ChOff + _TXP_PNTR;
sOutW((WIOA_T)ChP->IndexAddr,(USHORT)ChP->TxPrioPtr);
sOutB((PUCHAR)ChP->IndexData,0);
ChP->TxPrioBuf = ChOff + _TXP_BUF;
sEnRxProcessor(ChP); /* start the Rx processor */
return(TRUE);
}
/*****************************************************************************
Function: sGetRxErrStatus
Purpose: Get a channel's receive error status
Call: sGetRxErrStatus(ChP)
CHANPTR_T ChP; Ptr to channel structure
Return: unsigned char: Receive error status, can be 0 if there are no
errors or any combination of the following flags:
STMBREAK: BREAK
STMFRAME: framing error
STMRCVROVR: receiver over run error
STMPARITY: parity error
Warnings: The channel must be in Rx Status Mode (see sEnRxStatusMode())
before calling this function.
No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
unsigned char _CDECL sGetRxErrStatus(CHANPTR_T ChP)
{
unsigned int RxFIFOOut; /* Rx FIFO out status ptr */
sOutW((WIOA_T)ChP->IndexAddr, (USHORT)ChP->RxFIFOPtrs); /* get Rx FIFO out status ptr */
RxFIFOOut = sInW(ChP->IndexData) * 2 + 1;
sOutW((WIOA_T)ChP->IndexAddr, (USHORT)(ChP->RxFIFO + RxFIFOOut)); /* return the status */
return(sInB((PUCHAR)ChP->IndexData) & (STMBREAK | STMFRAME | STMPARITY | STMRCVROVR));
}
/***************************************************************************
Function: sSetParity
Purpose: Set parity to none, odd, or even.
Call: sSetParity(ChP,Parity)
CHANPTR_T ChP; Ptr to channel structure
int Parity; Parity, can be one of the following:
0: no parity
1: odd parity
2: even parity
Return: void
Comments: Function sSetParity() can be used in place of functions sEnParity(),
sDisParity(), sSetOddParity(), and sSetEvenParity().
-------------------------------------------------------------------------*/
void _CDECL sSetParity(CHANPTR_T ChP,int Parity)
{
if (Parity == 0)
{
ChP->TxControl[2] &= ~PARITY_EN;
}
else if (Parity == 1)
{
ChP->TxControl[2] |= PARITY_EN;
ChP->TxControl[2] &= ~EVEN_PAR;
}
else if (Parity == 2)
{
ChP->TxControl[2] |= (PARITY_EN | EVEN_PAR);
}
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxControl[0]);
}
/***************************************************************************
Function: sStopRxProcessor
Purpose: Stop the receive processor from processing a channel's microcode.
Call: sStopRxProcessor(ChP)
CHANPTR_T ChP; Ptr to channel structure
Return: void
Comments: The receive processor can be started again with sStartRxProcessor().
This function causes the receive processor to skip over the
microcode for the stopped channel. It does not stop it from
processing other channels.
Warnings: No context switches are allowed while executing this function.
Do not leave the receive processor stopped for more than one
character time.
After calling this function a delay of 4 uS is required to ensure
that the receive processor is no longer processing microcode for
this channel.
-------------------------------------------------------------------------*/
void _CDECL sStopRxProcessor(CHANPTR_T ChP)
{
unsigned char MCode[4]; /* 1st two microcode bytes */
MCode[0] = ChP->MCode[0];
MCode[1] = ChP->MCode[1];
MCode[3] = ChP->MCode[3];
MCode[2] = 0x0a; /* inc scan cnt inst to freeze Rx proc */
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&MCode[0]);
}
/***************************************************************************
Function: sStopSWInFlowCtl
Purpose: Stop the receive processor from processing a channel's
software input flow control microcode.
Call: sStopSWInFlowCtl(ChP)
CHANPTR_T ChP; Ptr to channel structure
Return: void
Comments: The receive processor can be started again with sStartRxProcessor().
This function causes the receive processor to skip over the
software input flow control microcode for the stopped channel.
It does not stop it from processing other channels.
Warnings: No context switches are allowed while executing this function.
After calling this function a delay of 1 uS is required to ensure
that the receive processor is no longer processing software input
flow control microcode for this channel.
-------------------------------------------------------------------------*/
void _CDECL sStopSWInFlowCtl(CHANPTR_T ChP)
{
unsigned char MCode[4]; /* 1st two microcode bytes */
MCode[0] = ChP->MCode[0];
MCode[1] = ChP->MCode[1];
MCode[2] = ChP->MCode[2];
MCode[3] = 0x0a; /* inc scan cnt inst to freeze Rx proc */
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&MCode[0]);
}
/***************************************************************************
Function: sFlushRxFIFO
Purpose: Flush the Rx FIFO
Call: sFlushRxFIFO(ChP)
CHANPTR_T ChP; Ptr to channel structure
Return: void
Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
while it is being flushed the receive processor is stopped
and the transmitter is disabled. After these operations a
4 uS delay is done before clearing the pointers to allow
the receive processor to stop. These items are handled inside
this function.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
void _CDECL sFlushRxFIFO(CHANPTR_T ChP)
{
int i;
unsigned char Ch; /* channel number within AIOP */
int RxFIFOEnabled; /* TRUE if Rx FIFO enabled */
if (sGetRxCnt(ChP) == 0) /* Rx FIFO empty */
return; /* don't need to flush */
RxFIFOEnabled = FALSE;
if (ChP->MCode[RXFIFO_DATA] == RXFIFO_EN) /* Rx FIFO is enabled */
{
RxFIFOEnabled = TRUE;
sDisRxFIFO(ChP); /* disable it */
for (i = 0;i < 2000/200;i++) /* delay 2 uS to allow proc to disable FIFO*/
sInB(ChP->IntChan);
}
sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */
Ch = (unsigned char)sGetChanNum(ChP);
sOutB(ChP->Cmd, (UCHAR)(Ch | RESRXFCNT)); /* apply reset Rx FIFO count */
sOutB(ChP->Cmd,Ch); /* remove reset Rx FIFO count */
sOutW((WIOA_T)ChP->IndexAddr, (USHORT)(ChP->RxFIFOPtrs)); /* clear Rx out ptr */
sOutW(ChP->IndexData,0);
sOutW((WIOA_T)ChP->IndexAddr, (USHORT)(ChP->RxFIFOPtrs + 2)); /* clear Rx in ptr */
sOutW(ChP->IndexData, 0);
if (RxFIFOEnabled)
sEnRxFIFO(ChP); /* enable Rx FIFO */
}
/***************************************************************************
Function: sFlushTxFIFO
Purpose: Flush the Tx FIFO
Call: sFlushTxFIFO(ChP)
CHANPTR_T ChP; Ptr to channel structure
Return: void
Comments: To prevent data from being enqueued or dequeued in the Tx FIFO
while it is being flushed the receive processor is stopped
and the transmitter is disabled. After these operations a
4 uS delay is done before clearing the pointers to allow
the receive processor to stop. These items are handled inside
this function.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
void _CDECL sFlushTxFIFO(CHANPTR_T ChP)
{
int i;
unsigned char Ch; /* channel number within AIOP */
int TxEnabled; /* TRUE if transmitter enabled */
if (sGetTxCnt(ChP) == 0) /* Tx FIFO empty */
return; /* don't need to flush */
TxEnabled = FALSE;
if (ChP->TxControl[3] & TX_ENABLE)
{
TxEnabled = TRUE;
sDisTransmit(ChP); /* disable transmitter */
}
sStopRxProcessor(ChP); /* stop Rx processor */
for (i = 0;i < 4000/200;i++) /* delay 4 uS to allow proc to stop */
sInB(ChP->IntChan);
Ch = (unsigned char)sGetChanNum(ChP);
sOutB(ChP->Cmd,(UCHAR)(Ch | RESTXFCNT)); /* apply reset Tx FIFO count */
sOutB(ChP->Cmd,Ch); /* remove reset Tx FIFO count */
sOutW((WIOA_T)ChP->IndexAddr, (USHORT)(ChP->TxFIFOPtrs)); /* clear Tx in/out ptrs */
sOutW(ChP->IndexData,0);
if (TxEnabled)
sEnTransmit(ChP); /* enable transmitter */
sStartRxProcessor(ChP); /* restart Rx processor */
}
/***************************************************************************
Function: sFlushTxPriorityBuf
Purpose: Flush the Tx priority buffer
Call: sFlushTxPriorityBuf(ChP,unsigned char *Data)
CHANPTR_T ChP; Ptr to channel structure
unsigned char *Data; Next data byte to be transmitted from the
Tx priority buffer before the flush occurred, if any. If
the return value is TRUE a byte is returned in "Data," if
the return value is FALSE nothing is returned in "Data."
Return: int: TRUE if there was data in the Tx priority buffer before
the flush occurs. In this case the next byte that would
have been transmitted is returned in the "Data" parameter.
FALSE if there was no data in the Tx priority buffer before
the flush.
Comments: This flush returns the next byte in the priority buffer to
allow that byte to be sent via sWriteTxByte() after all
transmit flushing is complete. This is done to allow pending
XON and XOFF bytes to be transmitted regardless of the flush.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
int _CDECL sFlushTxPriorityBuf(CHANPTR_T ChP,unsigned char *Data)
{
unsigned int PrioState; /* Tx prio buf status, count, and pointer */
unsigned int BufOff; /* Offset of next data byte in Tx prio buf */
WIOA_T IndexAddr;
WIOA_T IndexData;
IndexAddr = (WIOA_T)ChP->IndexAddr;
IndexData = (WIOA_T)ChP->IndexData;
sDisTransmit(ChP);
sOutW(IndexAddr, (USHORT)ChP->TxPrioCnt); /* get priority buf status */
PrioState = sInW(IndexData);
if (PrioState & PRI_PEND) /* data in Tx prio buf */
{
BufOff = PrioState >> 8; /* get offset of next data byte in buf */
sOutW(IndexAddr,(USHORT)(ChP->TxPrioBuf + BufOff));
*Data = sInB((BIOA_T)IndexData); /* return next data byte */
sEnTransmit(ChP);
return(TRUE);
}
sEnTransmit(ChP); /* no data in Tx prio buf */
return(FALSE);
}
/***************************************************************************
Function: sGetTxPriorityCnt
Purpose: Get the number of data bytes in the Tx priority buffer
Call: sGetTxPriorityCnt(ChP)
CHANPTR_T ChP; Ptr to channel structure
Return: unsigned char: The number of data bytes in the Tx FIFO.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
unsigned char _CDECL sGetTxPriorityCnt(CHANPTR_T ChP)
{
unsigned char Cnt;
sOutW((WIOA_T)ChP->IndexAddr, (USHORT)ChP->TxPrioCnt); /* get priority buf status */
Cnt = sInB((BIOA_T)ChP->IndexData);
if (Cnt & PRI_PEND)
return(Cnt & 0x1f); /* only lower 5 bits contain count */
else
return(0);
}
#ifndef INTEL_ORDER
/*---------------------------------------------------------------------
sReadRxBlk - MIPS VERSION
|---------------------------------------------------------------------*/
int _CDECL sReadRxBlk(CHANPTR_T ChP,unsigned char *Buffer,int Count)
{
int RetCount;
int WordCount;
int ByteCount = 0;
unsigned short TempWord;
RetCount = sGetRxCnt(ChP); /* number bytes in Rx FIFO */
/* are there pending chars? */
if (RetCount <= 0) /* no data available */
return(0x0);
if (RetCount > Count) /* only dequeue as much as requested */
RetCount = Count;
WordCount = RetCount >> 1; /* compute count as words */
while (WordCount--)
{
TempWord = sInW((WIOA_T)sGetTxRxDataIO(ChP));
Buffer[ByteCount++] = TempWord & 0xff;
Buffer[ByteCount++] = ( TempWord >> 8 ) & 0xff;
}
if (RetCount & 1)
{
Buffer[ByteCount++] = sInB( (BIOA_T)sGetTxRxDataIO(ChP));
}
return(RetCount);
}
#else // NOT MIPS version
/*------------------------------------------------------------------------
Function: sReadRxBlk - X86 INTEL VERSION
Purpose: Read a block of receive data from a channel
Call: sReadRxBlk(ChP,Buffer,Count)
CHANPTR_T ChP; Ptr to channel structure
unsigned char *Buffer; Ptr to buffer for receive data
int Count; Max number of bytes to read
Return: int: Number of bytes actually read from the channel
Warnings: Buffer must be large enough to hold Count characters.
This function must not be called when in Rx Status Mode.
-------------------------------------------------------------------------*/
int _CDECL sReadRxBlk(CHANPTR_T ChP,unsigned char *Buffer,int Count)
{
int RetCount;
int WordCount;
USHORT UNALIGNED *WordP;
WIOA_T io;
RetCount = sGetRxCnt(ChP); // number bytes in Rx FIFO
// are there pending chars?
if (RetCount <= 0) // no data available
return(0x0);
if (RetCount > Count) // only dequeue as much as requested
RetCount = Count;
WordCount = RetCount >> 1; // compute count as words
WordP = (USHORT UNALIGNED *)Buffer; // word ptr to buffer
io = sGetTxRxDataIO(ChP);
#ifdef WORD_ALIGN
while (WordCount--)
{
*WordP++ = sInW(io);
}
#else
sInStrW((PUSHORT)io, WordP, WordCount);
#endif
if (RetCount & 1) // odd count
{
Buffer[RetCount - 1] = sInB((PUCHAR)io); // read last byte
}
return(RetCount);
}
#endif // INTEL X86 version
#ifndef INTEL_ORDER
/*---------------------------------------------------------------------
sWriteTxBlk - MIPS VERSION
|---------------------------------------------------------------------*/
ULONG _CDECL sWriteTxBlk(CHANPTR_T ChP,PUCHAR Buffer,ULONG Count)
{
ULONG RetCount;
ULONG WordCount;
unsigned short TempWordLo;
unsigned short TempWordHi;
int ByteCount = 0;
RetCount = MAXTX_SIZE - (int)sGetTxCnt(ChP); /* open space in Tx FIFO*/
if (RetCount <= 0) /* no space available */
return(0x0);
if (RetCount > Count)
RetCount = Count; /* only enqueue as much as requested */
WordCount = RetCount >> 1 ; /* compute count as words */
while (WordCount--)
{
TempWordLo = Buffer[ByteCount++] & 0xff;
TempWordHi = Buffer[ByteCount++];
TempWordHi = (TempWordHi << 8) & 0xff00; /* shift to high byte */
TempWordHi |= TempWordLo;
sOutW((PUCHAR)sGetTxRxDataIO(ChP), TempWordHi);
}
if (RetCount & 1)
{
sOutB( (PUCHAR)sGetTxRxDataIO(ChP), Buffer[ByteCount++] );
}
return(RetCount);
}
#else // NOT MIPS
/*------------------------------------------------------------------------
Function: sWriteTxBlk
Purpose: Write a block of transmit data to a channel
Call: sWriteTxBlk(ChP,Buffer,Count)
CHANPTR_T ChP; Ptr to channel structure
unsigned char *Buffer; Ptr to buffer containing data to transmit
int Count; Size of buffer in bytes
Return: int: Number of bytes actually written to the channel
-------------------------------------------------------------------------*/
ULONG _CDECL sWriteTxBlk(CHANPTR_T ChP,PUCHAR Buffer,ULONG Count)
{
ULONG RetCount;
ULONG WordCount;
USHORT UNALIGNED *WordP;
WIOA_T io;
// 250, restrict to WORD amounts (boundary access thing)
RetCount = MAXTX_SIZE - sGetTxCnt(ChP);
if (RetCount > Count)
{
RetCount = Count; /* only enqueue as much as requested */
#ifdef WORD_ALIGN
// try to keep aligned on WORD boundary
//if (RetCount & 1)
//{
// if (RetCount > 1)
// --RetCount;
//}
#endif
}
if (RetCount <= 0) // no space or nothing to send
return 0;
WordCount = RetCount >> 1; /* compute count as words */
WordP = (PUSHORT)Buffer; /* word ptr to buffer */
io = sGetTxRxDataIO(ChP);
/* Write the data */
#ifdef WORD_ALIGN
while( WordCount-- )
{
sOutW(io, *WordP++);
}
#else
sOutStrW(io,WordP,WordCount);
#endif
if (RetCount & 1) /* odd count */
{
WordP=WordP+WordCount;
sOutB((PUCHAR)io, Buffer[RetCount - 1]); /* send last byte */
}
return(RetCount);
}
#endif
/*--------------------------------------------------------------------------
Function: sWriteTxPrioBlk
Purpose: Write a block of priority transmit data to a channel
Call: sWriteTxPrioBlk(ChP,Buffer,Count)
CHANPTR_T ChP; Ptr to channel structure
unsigned char *Buffer; Ptr to buffer containing data to transmit
int Count; Size of buffer in bytes, TXP_SIZE bytes maximum. If
Count > TXP_SIZE only TXP_SIZE bytes will be written.
Return: int: Number of bytes actually written to the channel, 0 if none
written.
Comments: The entire block of priority data is transmitted before any data
in the Tx FIFO.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
int _CDECL sWriteTxPrioBlk(CHANPTR_T ChP,unsigned char *Buffer,int Count)
{
unsigned char DWBuf[4]; /* buffer for double word writes */
register DWIOA_T IndexAddr;
int WordCount,i;
unsigned int UNALIGNED *WordP;
unsigned int *DWBufLoP;
unsigned int *DWBufHiP;
IndexAddr = ChP->IndexAddr;
sOutW((WIOA_T)IndexAddr,(USHORT)ChP->TxPrioCnt); /* get priority queue status */
if (sInB((BIOA_T)ChP->IndexData) & PRI_PEND) /* priority queue busy */
return(0); /* nothing sent */
if (Count > TXP_SIZE)
Count = TXP_SIZE;
WordCount = Count >> 1; /* compute count as words */
if (Count & 1) /* adjust for odd count */
WordCount++;
WordP = (unsigned int *)Buffer; /* word ptr to buffer */
DWBufLoP = (unsigned int *)&DWBuf[0];
DWBufHiP = (unsigned int *)&DWBuf[2];
*DWBufLoP = ChP->TxPrioBuf; /* data byte address */
for(i = 0;i < WordCount;i++) /* write data to Tx prioity buf */
{
*DWBufHiP = WordP[i]; /* data word value */
sOutDW(IndexAddr,*(ULONGPTR_T)DWBuf); /* write it out */
*DWBufLoP += 2;
}
*DWBufLoP = ChP->TxPrioCnt; /* Tx priority count address */
*DWBufHiP = PRI_PEND + Count; /* indicate count bytes pending */
sOutDW(IndexAddr, *(ULONGPTR_T)DWBuf); /* write it out */
return(Count);
}
/***************************************************************************
Function: sWriteTxPrioByte
Purpose: Write a byte of priority transmit data to a channel
Call: sWriteTxPrioByte(ChP,Data)
CHANPTR_T ChP; Ptr to channel structure
unsigned char Data; The transmit data byte
Return: int: 1 if the bytes is successfully written, otherwise 0.
Comments: The priority byte is transmitted before any data in the Tx FIFO.
Warnings: No context switches are allowed while executing this function.
-------------------------------------------------------------------------*/
int _CDECL sWriteTxPrioByte(CHANPTR_T ChP,unsigned char Data)
{
unsigned char DWBuf[4]; /* buffer for double word writes */
unsigned int UNALIGNED *WordPtr;
register DWIOA_T IndexAddr;
/* Don't write to prio buf unless guarenteed Tx FIFO is not empty because
of bug in AIOP */
if(sGetTxCnt(ChP) > 1) /* write it to Tx priority buffer */
{
IndexAddr = ChP->IndexAddr;
sOutW((WIOA_T)IndexAddr, (USHORT)ChP->TxPrioCnt); /* get priority buffer status */
if (sInB((BIOA_T)ChP->IndexData) & PRI_PEND) /* priority buffer busy */
return(0); /* nothing sent */
WordPtr = (unsigned int *)(&DWBuf[0]);
*WordPtr = ChP->TxPrioBuf; /* data byte address */
DWBuf[2] = Data; /* data byte value */
sOutDW(IndexAddr, *((ULONGPTR_T)(&DWBuf[0]))); /* write it out */
*WordPtr = ChP->TxPrioCnt; /* Tx priority count address */
DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */
DWBuf[3] = 0; /* priority buffer pointer */
sOutDW(IndexAddr, *((ULONGPTR_T)(&DWBuf[0]))); /* write it out */
}
else /* write it to Tx FIFO */
{
sWriteTxByte((BIOA_T)sGetTxRxDataIO(ChP),Data);
}
return(1); /* 1 byte sent */
}
/***************************************************************************
Function: sEnInterrupts
Purpose: Enable one or more interrupts for a channel
Call: sEnInterrupts(ChP,Flags)
CHANPTR_T ChP; Ptr to channel structure
unsigned int Flags: Interrupt enable flags, can be any combination
of the following flags:
TXINT_EN: Interrupt on Tx FIFO empty
RXINT_EN: Interrupt on Rx FIFO at trigger level (see
sSetRxTrigger())
SRCINT_EN: Interrupt on SRC (Special Rx Condition)
MCINT_EN: Interrupt on modem input change
CHANINT_EN: Allow channel interrupt signal to the AIOP's
Interrupt Channel Register.
Return: void
Comments: If an interrupt enable flag is set in Flags, that interrupt will be
enabled. If an interrupt enable flag is not set in Flags, that
interrupt will not be changed. Interrupts can be disabled with
function sDisInterrupts().
This function sets the appropriate bit for the channel in the AIOP's
Interrupt Mask Register if the CHANINT_EN flag is set. This allows
this channel's bit to be set in the AIOP's Interrupt Channel Register.
Interrupts must also be globally enabled before channel interrupts
will be passed on the the host. This is done with function
sEnGlobalInt().
In some cases it may be desirable to disable interrupts globally but
enable channel interrupts. This would allow the global interrupt
status register to be used to determine which AIOPs need service.
-------------------------------------------------------------------------*/
void _CDECL sEnInterrupts(CHANPTR_T ChP,unsigned int Flags)
{
unsigned char Mask; /* Interrupt Mask Register */
ChP->RxControl[2] |=
((unsigned char)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->RxControl[0]);
ChP->TxControl[2] |= ((unsigned char)Flags & TXINT_EN);
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxControl[0]);
if(Flags & CHANINT_EN)
{
Mask = sInB(ChP->IntMask) | (1 << ChP->ChanNum);
sOutB(ChP->IntMask,Mask);
}
}
/***************************************************************************
Function: sDisInterrupts
Purpose: Disable one or more interrupts for a channel
Call: sDisInterrupts(ChP,Flags)
CHANPTR_T ChP; Ptr to channel structure
unsigned int Flags: Interrupt flags, can be any combination
of the following flags:
TXINT_EN: Interrupt on Tx FIFO empty
RXINT_EN: Interrupt on Rx FIFO at trigger level (see
sSetRxTrigger())
SRCINT_EN: Interrupt on SRC (Special Rx Condition)
MCINT_EN: Interrupt on modem input change
CHANINT_EN: Disable channel interrupt signal to the
AIOP's Interrupt Channel Register.
Return: void
Comments: If an interrupt flag is set in Flags, that interrupt will be
disabled. If an interrupt flag is not set in Flags, that
interrupt will not be changed. Interrupts can be enabled with
function sEnInterrupts().
This function clears the appropriate bit for the channel in the AIOP's
Interrupt Mask Register if the CHANINT_EN flag is set. This blocks
this channel's bit from being set in the AIOP's Interrupt Channel
Register.
-------------------------------------------------------------------------*/
void _CDECL sDisInterrupts(CHANPTR_T ChP,unsigned int Flags)
{
unsigned char Mask; /* Interrupt Mask Register */
ChP->RxControl[2] &=
~((unsigned char)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN));
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->RxControl[0]);
ChP->TxControl[2] &= ~((unsigned char)Flags & TXINT_EN);
sOutDW(ChP->IndexAddr,*(ULONGPTR_T)&ChP->TxControl[0]);
if(Flags & CHANINT_EN)
{
Mask = sInB(ChP->IntMask) & (~(1 << ChP->ChanNum));
sOutB(ChP->IntMask,Mask);
}
}
/***************************************************************************
Function: sReadMicrocode
Purpose: Read the microcode directly from a channel
Call: sReadMicrocode(ChP,Buffer,Count)
CHANPTR_T ChP; Ptr to channel structure
char *Buffer; Ptr to buffer for microcode
int Count; Number of bytes to read
Return: void
Warnings: Buffer must be large enough to hold Count bytes.
-------------------------------------------------------------------------*/
void _CDECL sReadMicrocode(CHANPTR_T ChP,char *Buffer,int Count)
{
WIOA_T IndexAddr;
BIOA_T IndexData;
unsigned int McodeOff;
IndexAddr = (WIOA_T)ChP->IndexAddr;
IndexData = (BIOA_T)ChP->IndexData;
McodeOff = MCODE_ADDR + (unsigned int)sGetChanNum(ChP) * 0x1000;
while(Count-- > 0)
{
sOutW(IndexAddr,(USHORT)(McodeOff++));
*Buffer++ = sInB((BIOA_T)IndexData);
}
}
/*------------------------------------------------------------------
sSetBaudRate - Set the desired baud rate. Return non-zero on error.
|-------------------------------------------------------------------*/
int sSetBaudRate(CHANNEL_T *ChP,
ULONG desired_baud,
USHORT SetHardware)
{
ULONG diff;
ULONG act_baud;
ULONG percent_error;
ULONG div;
ULONG base_clock_rate;
ULONG clock_freq = ChP->CtlP->ClkRate;
ULONG clk_prescaler = (ULONG)ChP->CtlP->ClkPrescaler;
base_clock_rate = ((clock_freq/16) / ((clk_prescaler & 0xf)+1));
///////////////////////////////////////
// calculate the divisor for our hardware register.
// this is really just div = clk/desired_baud -1. but we do some
// work to minimize round-off error.
if (desired_baud <= 0)
desired_baud = 1; // guard against div 0
div = ((base_clock_rate+(desired_baud>>1)) / desired_baud) - 1;
if (div > 8191) // overflow hardware divide register
div = 8191;
// this is really just (clk) / (div+1) but we do some
// work to minimize round-off error.
act_baud = (base_clock_rate+((div+1)>>1)) / (div+1);
if (desired_baud > act_baud)
diff = desired_baud - act_baud;
else
diff = act_baud - desired_baud;
percent_error = (diff * 100) / desired_baud;
if (percent_error > 5)
return (int) percent_error;
if (SetHardware)
{
sChanOutWI(ChP, _BAUD, div);
}
return 0;
}
/*------------------------------------------------------------------
Function: sChanOutWI
Purpose: Write an Indirect Register on the Rocket Port Board
Call: sChanOutWI(CHANNEL_T *ChP, WORD RegNum, WORD val)
CHANPTR_T ChP; Ptr to channel structure
WORD RegNum; Indirect Register Number to Write
WORD val; Value to Write.
Return: void
Comments: This is a little slower than using macros but far less ugly
and error prone. Macros should only be used where speed is
imperative.
|-------------------------------------------------------------------*/
void sChanOutWI(CHANNEL_T *ChP, USHORT RegNum, ULONG val)
{
UCHAR m[4];
USHORT ChOff;
ChOff = ChP->ChanNum * 0x1000; // change this to look up table
// see about speeding this up:
m[0] = (unsigned char)(ChOff + RegNum);
m[1] = (unsigned char)((ChOff + RegNum) >> 8);
m[2] = (unsigned char) val;
m[3] = (unsigned char)(val >> 8);
sOutDW(ChP->IndexAddr,*(ULONG *)&m[0]);
}
/*------------------------------------------------------------------
Function: sModemReset
Purpose: Set or clear reset state on second generation RocketModem
Call: sModemReset(CHANNEL_T *ChP, int on)
CHANNEL_T *ChP; Ptr to channel structure
int on; on!=0 to enable reset; on=0 to clear reset
Return: void
Comments: The newer RocketModem boards power up in a reset state.
This routine is used to clear the board from reset state or
re-enable a reset state. Called from the driver during
initialization to clear the reset and via an ioctl to
manually reset the board. [jl] 980206
BUGBUG: this code violates io-resource handling under NT and will
probably break running on ALPHA machines due to bypassing NT's
io-mapping scheme(i.e. should not be doing AiopIO[1] = AiopIO[0] +..)
Also, this driver is probably not calling IoResource calls to claim
this IO space properly(could result in conflicting hardware.)
|-------------------------------------------------------------------*/
void sModemReset(CHANNEL_T *ChP, int on)
{
CONTROLLER_T *CtlP;
WIOA_T addr;
BYTE val;
CtlP = ChP->CtlP;
if (CtlP->BusType == Isa)
{
// ensure second aiop CS is enabled. there will be no physical
// aiop to enable, but the CS (which ususally goes to an aiop
// is routed to a latch, which latches the RESET signal. we
// have to also ensure that the mudback-Isa bus controller
// aiopic io-addr has been configured for the proper address
// space. since the rocketmodem Isa product is limited to
// eight ports, we know that the second aiop will be configured
// 400h above the first eight port aiop chip...
val = sInB(CtlP->MBaseIO + 3);
// read in, see if aiop[1] enabled...
if ((CtlP->AiopIO[1] != (PUSHORT)((unsigned int)(CtlP->AiopIO[0]) + 0x400)) ||
((val & 2) == 0))
{
// cr second aiop chip not enabled. Isa board alias
CtlP->AiopIO[1] = (PUSHORT)((unsigned int)(CtlP->AiopIO[0]) + 0x400);
// tell mudback where to position the base-io of the aiopic...
val = sInB(CtlP->MBaseIO + 2); // read in irq, aiop-io reg
sOutB(CtlP->MBaseIO + 2, (BYTE)((val & 0xfc) | (1 & 0x03))); //aiop index
// setup aiop i/o in mudbac...
sOutB(CtlP->MBaseIO, (BYTE)((unsigned int)CtlP->AiopIO[1] >> 6));
}
sEnAiop(CtlP,1); // enable the (un)AIOP
}
else if (CtlP->BusType == PCIBus)
{
// PCI bus RocketModem reset...
// we reference where the second AIOP would be, if there were one,..
CtlP->AiopIO[1] = (PUSHORT)((unsigned int)CtlP->AiopIO[0] + 0x40);
}
// the latch has 3-pin mux which determines which latch the
// data gets routed to. these pins are hooked to the first
// three address lines. the fourth address line (8h) is used
// as the data line.
addr = CtlP->AiopIO[1];
// adjust reset state...
sOutB(((PUCHAR)(addr) + ChP->ChanNum + (on ? 0 : 8)), 0);
// disable the aiop; must disable to prevent chip select from getting hit
// with continuous pulses (causing reset to occur).
// additionally it seems that a read of some other address is required
// before the disable or the first channel on the board goes back into the
// reset state. there's nothing special about ChP->IntChan...a read of
// any port would probably work...
sInB(ChP->IntChan);
if (CtlP->BusType == Isa)
{
sDisAiop(CtlP, 1);
}
}
/*------------------------------------------------------------------
Function: sModemWriteROW
Purpose: Send the "Rest of World" configuration string to the
RocketModem port.
Call: sModemSendROW(CHANNEL_T *ChP, USHORT CountryCode)
CHANNEL_T *ChP; Ptr to channel structure
USHORT CountryCode; Country to configure the modem for
Return: void
Comments: The ROW "SocketModem" RocketModem boards can compensate for
the differences in various internation phone systems. This
function sends the appropriate configuration string based
upon a registry setting specified by the user. [jl] 980316
Modem should be hard reset before calling this function. Otherwise,
use AT modem reset commands...
|-------------------------------------------------------------------*/
void sModemWriteROW(CHANNEL_T *ChP, USHORT CountryCode)
{
CONTROLLER_T *CtlP = ChP->CtlP;
char *ModemConfigString = {"AT*NCxxZ\r"};
int max;
MyKdPrint(D_Init,("sModemWriteROW: %x, %x\n",(unsigned long)ChP,CountryCode)) // DEBUG
if (CountryCode == ROW_NA) {
MyKdPrint(D_Init,("ROW Write, North America\n"))
return;
}
/*
create the country config string...
*/
ModemConfigString[5] = '0' + (CountryCode / 10);
ModemConfigString[6] = '0' + (CountryCode % 10);
MyKdPrint(D_Init,("ROW Write, Chan:%d, Cfg:%s\n", ChP->ChanNum, ModemConfigString))
time_stall(10); // TUNE
sFlushTxFIFO(ChP);
sFlushRxFIFO(ChP);
sSetBaudRate(ChP,9600,TRUE);
sSetData8(ChP);
sClrTxXOFF(ChP);
sEnRTSFlowCtl(ChP);
sEnCTSFlowCtl(ChP);
if (sGetChanStatus(ChP) & STATMODE) {
sDisRxStatusMode(ChP);
}
sGetChanIntID(ChP);
sEnRxFIFO(ChP);
sEnTransmit(ChP);
sSetRTS(ChP);
/*
spin while port readies...
*/
time_stall(10);
sModemWriteDelay(ChP,ModemConfigString,strlen(ModemConfigString));
(void) sModemRead(ChP,"OK",sizeof("OK\r") - 1,10);
time_stall(1);
sFlushRxFIFO(ChP);
sClrRTS(ChP);
}
/*------------------------------------------------------------------
Function: sModemSpeakerEnable
Purpose: Enable RocketModemII board speaker
Call: sModemSpeakerEnable(CHANNEL_T *ChP)
CHANNEL_T *ChP; Ptr to channel structure
Return: void
Comments: Called from the driver during initialization to
enable the board speaker.
|-------------------------------------------------------------------*/
void sModemSpeakerEnable(CHANNEL_T *ChP)
{
CONTROLLER_T *CtlP;
WIOA_T addr;
BYTE val;
CtlP = ChP->CtlP;
/*
PCI bus RocketModem reset...
*/
if (CtlP->BusType != PCIBus)
return;
/*
we reference where the second AIOP would be,..
*/
CtlP->AiopIO[1] = (PUSHORT)((unsigned int)CtlP->AiopIO[0] + 0x40);
/*
the latch has 3-pin mux which determines which latch the
data gets routed to. these pins are hooked to the first
three address lines. the fourth address line (8h) is used
as the data line...
*/
addr = CtlP->AiopIO[1];
/*
following is hack to enable the speaker (PCI cards only). we don't want
to construct an extension and related storage for a speaker, so we'll
just piggyback the enable of the speaker onto another channel...
*/
sOutB(((PUCHAR)(addr) + 7 + 8), 0);
}
/*------------------------------------------------------------------
Function: sModemWriteDelay
Purpose: Send a string to the RocketModem port, pausing for each character
to clear the FIFO.
Call: sModemSendROW(CHANNEL_T *ChP, char *string,int length)
CHANNEL_T *ChP; Ptr to channel structure
char *string; String to write
int length Length of string, not including any trailing null
Return: void
Comments: Output characters one at a time
|-------------------------------------------------------------------*/
void
sModemWriteDelay(CHANNEL_T *ChP,char *string,int length)
{
int index,count;
unsigned char buffer[2];
sFlushTxFIFO(ChP);
sFlushRxFIFO(ChP);
if (
(length <= 0)
||
(string == (char *)NULL)
)
return;
index = 0;
count = 0;
while (length--) {
while (count = (int)sGetTxCnt(ChP)) {
/*
byte or bytes in transmit FIFO. wait a while. adjust interval...
*/
ms_time_stall(10 * count);
/*
no change? assume FIFO stuck, bail out of loop...
*/
if (count == (int)sGetTxCnt(ChP)) {
break;
}
}
/*
transmit FIFO probably available. put a byte in it, pause a moment...
*/
sWriteTxByte((BIOA_T)sGetTxRxDataIO(ChP),(unsigned char)string[index]);
++index;
}
}
/********************************************************************
send string to modem...
*********************************************************************/
void
sModemWrite(CHANNEL_T *ChP, char *string, int length)
{
if (
(length <= 0)
||
(string == (char *)NULL)
)
return;
sWriteTxBlk(ChP, (unsigned char *)string, length);
}
/********************************************************************
look for match on a particular character string...
********************************************************************/
int sModemRead(CHANNEL_T *ChP, char *string,int length, int poll_retries)
{
unsigned char buffer;
long count;
int arg_index;
int read_retries;
WIOA_T io;
unsigned int fifo_data;
#ifdef DUMPDATA
DumpIndex = 0;
#endif
/*
bail if board not installed...
*/
fifo_data = (unsigned int)sGetRxCnt(ChP);
/*
see if board installed and functioning. if not, architecture returns
bad value. if so, stonewall on read...
*/
if (fifo_data > (unsigned int)RXFIFO_SIZE)
return(-1);
io = sGetTxRxDataIO(ChP);
poll_retries *= 10;
buffer = (char)0;
arg_index = 0;
/*
search until we see a match on the argument characters, or we run out of data...
*/
do {
while (sGetRxCnt(ChP) > 0) {
buffer = sReadRxByte((PUCHAR)io);
#ifdef DUMPDATA
DumpResponseByte(buffer);
#endif
/*
force response to upper case, since responses are different depending on
whether the modem was loaded already or not...
*/
if (buffer >= 'a')
buffer ^= 0x20;
if (string[arg_index] == buffer) {
++arg_index;
/*
see if we're done. if so, bail with good return code...
*/
if (arg_index == length) {
time_stall(TENTH_SECOND);
#ifdef DUMPDATA
while (sGetRxCnt(ChP) > 0) {
buffer = sReadRxByte((PUCHAR)io);
DumpResponseByte(buffer);
}
MyKdPrint(D_Init,("sModemRead: %x [%s]\n",(unsigned long)ChP,DumpArray))
#endif
sFlushRxFIFO(ChP);
return(0);
}
}
else {
arg_index = 0;
}
}
ms_time_stall(10);
} while (poll_retries-- > 0);
#ifdef DUMPDATA
MyKdPrint(D_Init,("sModemRead: %x [%s]\n",(unsigned long)ChP,DumpArray))
#endif
return(-1);
}
/********************************************************************
look for match on two possibilities...
********************************************************************/
int sModemReadChoice(CHANNEL_T *ChP,
char *string0,
int length0,
char *string1,
int length1,
int poll_retries)
{
char buffer;
long count;
int arg_index0;
int arg_index1;
char *ptr;
WIOA_T io;
unsigned int fifo_data;
#ifdef DUMPDATA
DumpIndex = 0;
#endif
MyKdPrint(D_Init,("sModemReadChoice: %x\n",(unsigned long)ChP))
poll_retries *= 10;
/*
bail if board not installed...
*/
fifo_data = (unsigned int)sGetRxCnt(ChP);
/*
see if board installed and functioning. if not, architecture returns
likely -1. if so, stonewall on read...
*/
if (fifo_data > (unsigned int)RXFIFO_SIZE)
return(-1);
io = sGetTxRxDataIO(ChP);
buffer = (char)0;
arg_index0 = 0;
arg_index1 = 0;
/*
first, we discard characters until we see a match on the argument characters,
or we run out of data...
*/
do {
while (sGetRxCnt(ChP) > 0) {
buffer = sReadRxByte((PUCHAR)io);
#ifdef DUMPDATA
DumpResponseByte(buffer);
#endif
/*
force response to upper case, since responses can be different depending on
whether the modem was loaded already or not...
*/
if (buffer >= 'a')
buffer ^= 0x20;
/*
check first argument...
*/
if (string0[arg_index0] == buffer) {
++arg_index0;
/*
see if we're done matching on string 0...
*/
if (arg_index0 >= length0) {
time_stall(TENTH_SECOND);
#ifdef DUMPDATA
while (sGetRxCnt(ChP) > 0) {
buffer = sReadRxByte((PUCHAR)io);
DumpResponseByte(buffer);
}
MyKdPrint(D_Init,("sModemReadChoice: %x\r\n[%s]\n",(unsigned long)ChP,DumpArray))
#endif
sFlushRxFIFO(ChP);
return(0);
}
}
else {
arg_index0 = 0;
}
/*
check argument 1...
*/
if (string1[arg_index1] == buffer) {
++arg_index1;
/*
see if we're done matching on string 1...
*/
if (arg_index1 >= length1) {
time_stall(TENTH_SECOND);
#ifdef DUMPDATA
while (sGetRxCnt(ChP) > 0) {
buffer = sReadRxByte((PUCHAR)io);
DumpResponseByte(buffer);
}
MyKdPrint(D_Init,("sModemReadChoice: %x\r\n[%s]\n",(unsigned long)ChP,DumpArray))
#endif
sFlushRxFIFO(ChP);
return(1);
}
}
else {
arg_index1 = 0;
}
}
ms_time_stall(10);
} while (poll_retries-- > 0);
/*
no match...
*/
#ifdef DUMPDATA
MyKdPrint(D_Init,("sModemReadChoice: %x\r\n[%s]\n",(unsigned long)ChP,DumpArray))
#endif
sFlushRxFIFO(ChP);
return(-1);
}
/********************************************************************
check transmit FIFO...
*********************************************************************/
int sTxFIFOStatus(CHANNEL_T *ChP)
{
unsigned int fifo_size;
/*
see if board installed and functioning. if not, architecture returns
bad count. if so, stonewall on fifo ready...
*/
fifo_size = (unsigned int)sGetTxCnt(ChP);
if (fifo_size > (unsigned int)TXFIFO_SIZE)
return(MAXTX_SIZE);
if (MAXTX_SIZE <= (unsigned int)sGetTxCnt(ChP))
return(MAXTX_SIZE);
/*
return number of data bytes in FIFO...
*/
return(sGetTxCnt(ChP));
}
/********************************************************************
check available space in transmit FIFO. there's two checks here:
one for whether the FIFO is present;
one for whether the FIFO is full...
*********************************************************************/
int sTxFIFOReady(CHANNEL_T *ChP)
{
unsigned int fifo_size;
/*
see if board installed and functioning. if not, architecture likely returns
a bad value. if so, stonewall on fifo ready...
*/
fifo_size = (unsigned int)sGetTxCnt(ChP);
if (fifo_size > (unsigned int)TXFIFO_SIZE)
return(0);
/*
if number of data bytes currently in FIFO is greater than the
available space, return busy for now...
*/
if (sGetTxCnt(ChP) >= MAXTX_SIZE)
return(0);
/*
return (size of FIFO - number of data bytes in FIFO)...
*/
return(MAXTX_SIZE - sGetTxCnt(ChP));
}
/********************************************************************
discard pending data in receive FIFO. pull in data until data
runs out or count goes to zero...
*********************************************************************/
int sRxFIFOReady(CHANNEL_T *ChP)
{
unsigned char buffer;
int retries;
WIOA_T io;
unsigned int count;
count = (unsigned int)sGetRxCnt(ChP);
if (count > (unsigned int)RXFIFO_SIZE)
return(-1);
if (!count)
return(0);
retries = 20;
io = sGetTxRxDataIO(ChP);
do {
count = RXFIFO_SIZE + 2; // set to size of FIFO + slop...
while (
(sGetRxCnt(ChP))
&&
(count--)
) {
buffer = sReadRxByte((PUCHAR)io);
}
/*
if receive FIFO is now empty, bail out. if it was full, though,
pause a moment and then check to see if it has refilled -
if it has, flush that, and then check again. what we're trying to do
here is empty the FIFO, and still detect a run-on condition...
*/
if (count)
return(0);
ms_time_stall(10);
} while (--retries);
/*
receive FIFO didn't empty, though we gave it several chances...
*/
return(-1);
}
#ifdef DUMPDATA
/********************************************************************
dump responses to log...
********************************************************************/
void DumpResponseByte(char buffer)
{
if (DumpIndex < sizeof(DumpArray) - 2) {
switch (buffer) {
case '\n': {
DumpArray[DumpIndex++] = '\\';
DumpArray[DumpIndex++] = 'n';
break;
}
case '\r': {
DumpArray[DumpIndex++] = '\\';
DumpArray[DumpIndex++] = 'r';
break;
}
case '\t': {
DumpArray[DumpIndex++] = '\\';
DumpArray[DumpIndex++] = 't';
break;
}
case '\0': {
DumpArray[DumpIndex++] = '\\';
DumpArray[DumpIndex++] = '0';
break;
}
default: {
if (buffer < ' ') {
DumpArray[DumpIndex++] = '?';
}
else {
DumpArray[DumpIndex++] = buffer;
}
}
}
DumpArray[DumpIndex] = 0;
}
}
#endif