1070 lines
27 KiB
C
1070 lines
27 KiB
C
//-----------------------------------------------------------------------
|
|
//
|
|
// P3C.C
|
|
//
|
|
// Trantor P3C access file.
|
|
//
|
|
// Revisions:
|
|
// 09-01-92 KJB First.
|
|
// 02-25-93 KJB Reorganized, supports dataunderrun with long delay
|
|
// for under run on large xfers. Can we fix this?
|
|
// 03-11-93 JAP Changed retcode equates to reflect new names.
|
|
// 03-11-93 KJB Changed to use N5380Enable/DisableDmaRead/Write
|
|
// routines.
|
|
// 03-12-93 KJB Now supports polling thru CardInterrupt and
|
|
// StartCommandInterrupt/FinishCommandInterrupt.
|
|
// 03-19-93 JAP Implemented condition build FAR and NEAR pointers
|
|
// 03-22-93 KJB Added support for scatter gather: P3CDoIo.
|
|
// 03-25-93 JAP Fixed up typedef and prototype inconsistencies
|
|
// 03-26-93 KJB Uni and bi directional read ports.
|
|
// 04-05-93 KJB Removed assembly loop instruction to work with
|
|
// winnt compiler. Removed unused variables.
|
|
// 05-14-93 KJB Remove all WINNT specific #ifdef i386 references.
|
|
// 05-14-93 KJB Removed P3CDoIo, it did not work for scatter gather.
|
|
// 05-17-93 KJB Fixed bugs where the wrong parameter was being
|
|
// passed in EP3CDo/Start/FinishCommandInterrupt.
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
|
|
#include CARDTXXX_H
|
|
|
|
// Local Functions
|
|
|
|
VOID P3CPutControl(PADAPTER_INFO g,UCHAR mode, UCHAR reg);
|
|
VOID P3CSetPrinterMode(PADAPTER_INFO g, UCHAR data, UCHAR control);
|
|
VOID P3CSetScsiMode(PADAPTER_INFO g, PUCHAR data, PUCHAR control);
|
|
|
|
USHORT P3CReadBytesFastBiDir(PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase);
|
|
USHORT P3CReadBytesFastUniDir(PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase);
|
|
BOOLEAN P3CCheckAdapterBiDir(PADAPTER_INFO g);
|
|
BOOLEAN P3CCheckAdapterUniDir(PADAPTER_INFO g);
|
|
|
|
|
|
//
|
|
// P3CPutControl
|
|
//
|
|
// This routine writes the p3c mode and the n5380 register number to the
|
|
// P3C.
|
|
//
|
|
VOID P3CPutControl(PADAPTER_INFO g,UCHAR mode, UCHAR reg)
|
|
{
|
|
|
|
UCHAR tmp;
|
|
|
|
// output the mode and 5380 register to the parallel data reg
|
|
tmp = (mode & (PC_ADRS ^ 0xff)) | reg;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,tmp);
|
|
|
|
//
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,&tmp);
|
|
tmp = tmp & (0xff ^ P_BUFEN);
|
|
tmp = tmp | P_STB;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
tmp = tmp & (0xff ^ P_STB);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
}
|
|
|
|
//
|
|
// P3CSetPrinterMode
|
|
//
|
|
// This routine sets the P3C to printer pass through mode. This is the
|
|
// default mode and should be set after the brief use of scsi mode.
|
|
//
|
|
VOID P3CSetPrinterMode(PADAPTER_INFO g, UCHAR data, UCHAR control)
|
|
{
|
|
UCHAR tmp;
|
|
|
|
// to prevent glitching, put P3C into read sig nibble mode
|
|
P3CPutControl(g,PCCC_MODE_RSIG_NIBBLE,0);
|
|
|
|
// restore data register
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,data);
|
|
|
|
// restore control register
|
|
// leave p_init negated
|
|
tmp = control | P_INIT;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
}
|
|
|
|
//
|
|
// P3CSetScsiMode
|
|
//
|
|
// This routine sets the P3C into scsi mode. Now the parallel port can
|
|
// be used to send commands the the n5380. This mode should be set only
|
|
// briefly during when the scsi command is being executed.
|
|
//
|
|
VOID P3CSetScsiMode(PADAPTER_INFO g, PUCHAR data, PUCHAR control)
|
|
{
|
|
UCHAR tmp;
|
|
|
|
// save parallel data
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_DATA,data);
|
|
|
|
// zero data register
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,0);
|
|
|
|
// save parallel control
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,control);
|
|
*control = *control & (P_BUFEN ^ 0xff);
|
|
|
|
// if in peripheral mode, get out to avoid glitch
|
|
tmp = *control | P_INIT;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// set ID pattern to data register
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,0xfe);
|
|
|
|
// clear slc and init on control
|
|
tmp = tmp & ((P_SLC | P_INIT) ^0xff);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// assert slc
|
|
tmp = tmp | P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// clear all bits in control
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
}
|
|
|
|
//
|
|
// P3CCheckAdapter
|
|
//
|
|
// This routine is used to sense the presense of the P3C adapter out
|
|
// on the Parallel port. It will only detect the adapter if a device
|
|
// is providing termination power.
|
|
//
|
|
BOOLEAN P3CCheckAdapter(PADAPTER_INFO g)
|
|
{
|
|
BOOLEAN rval = FALSE;
|
|
|
|
if (g->ParallelPortType == PT_UNKNOWN) {
|
|
// do we have a bi-directional port?
|
|
|
|
if (P3CCheckAdapterBiDir(g)) {
|
|
|
|
// yes, bi-directional port with a p3c
|
|
|
|
g->ParallelPortType = PT_BI;
|
|
rval = TRUE;
|
|
|
|
} else {
|
|
|
|
// well, not bi-directional, but perhaps uni-directional?
|
|
|
|
if (P3CCheckAdapterUniDir(g)) {
|
|
|
|
// yes, uni-directional parallel port with P3C connected
|
|
|
|
g->ParallelPortType = PT_UNI;
|
|
rval = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
// we are to try the specific type given
|
|
|
|
if (g->ParallelPortType == PT_BI) {
|
|
|
|
// check only bi-directional
|
|
|
|
rval = P3CCheckAdapterBiDir(g);
|
|
|
|
} else {
|
|
|
|
if (g->ParallelPortType == PT_BI) {
|
|
|
|
// check only uni-directional
|
|
|
|
rval = P3CCheckAdapterUniDir(g);
|
|
|
|
} else {
|
|
|
|
// it is some other type we don't support
|
|
|
|
rval = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// BOOLEAN P3CCheckAdapterUniDir(PADAPTER_INFO g)
|
|
//
|
|
// Checks for an adapter on a uni-directional parallel port.
|
|
//
|
|
BOOLEAN P3CCheckAdapterUniDir(PADAPTER_INFO g)
|
|
{
|
|
UCHAR data;
|
|
UCHAR control;
|
|
UCHAR tmp;
|
|
UCHAR sig0,sig1;
|
|
UCHAR sig_byte[3];
|
|
USHORT i;
|
|
|
|
// set scsi mode
|
|
P3CSetScsiMode(g,&data,&control);
|
|
|
|
// set read sig nibble mode
|
|
P3CPutControl(g,PCCC_MODE_RSIG_NIBBLE,0);
|
|
|
|
// zero data reg to get max contention during read signature
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,0);
|
|
|
|
for (i=0;i<3;i++) {
|
|
// Assert SLC
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,&tmp);
|
|
tmp = (tmp & (P_BUFEN ^ 0xff)) | P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// read in the status reg, it has the low nibble
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_STATUS,&sig0);
|
|
|
|
// Deassert SLC
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,&tmp);
|
|
tmp = (tmp & (P_BUFEN ^ 0xff)) & (P_SLC ^ 0xff);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// note: there must be a delay here for timing
|
|
|
|
// Assert SLC
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,&tmp);
|
|
tmp = (tmp & (P_BUFEN ^ 0xff)) | P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// read in the status reg, it has the high nibble
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_STATUS,&sig1);
|
|
|
|
// Deassert SLC
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,&tmp);
|
|
tmp = (tmp & (P_BUFEN ^ 0xff)) & (P_SLC ^ 0xff);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
sig_byte[i] = ((sig0 >> 3) & 0xf) | ((sig1 << 1) & 0xf0);
|
|
}
|
|
|
|
// set parallel port for use by printer
|
|
|
|
P3CSetPrinterMode(g,data,control);
|
|
|
|
// compare the signature bytes
|
|
if ((sig_byte[0] == 0x6c) && (sig_byte[1] == 0x55) &&
|
|
(sig_byte[2] == 0xaa)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// BOOLEAN P3CCheckAdapterBiDir(PADAPTER_INFO g)
|
|
//
|
|
// Checks for an adapter on a bi-directional parallel port
|
|
//
|
|
//
|
|
BOOLEAN P3CCheckAdapterBiDir(PADAPTER_INFO g)
|
|
{
|
|
UCHAR data;
|
|
UCHAR control;
|
|
UCHAR tmp;
|
|
UCHAR sig_byte[3];
|
|
USHORT i;
|
|
|
|
// set scsi mode
|
|
P3CSetScsiMode(g,&data,&control);
|
|
|
|
// set parallel port for BI-DIR if ps2
|
|
// NOTE: this is destructive to NCR machines
|
|
// also, I don't know details about how this wakes up the
|
|
// ps/2's, it was copied directly from the t348.asm code.
|
|
|
|
#ifndef MACHINE_NCR
|
|
PortIOPut((PVOID)0x94,0x7f);
|
|
PortIOGet((PVOID)0x102,&tmp);
|
|
tmp = tmp & 0x7f;
|
|
PortIOPut((PVOID)0x102,tmp);
|
|
PortIOPut((PVOID)0x94,0xff);
|
|
#endif
|
|
|
|
// set read sig byte mode
|
|
|
|
P3CPutControl(g,PCCC_MODE_RSIG_BYTE,0);
|
|
|
|
// control register used to ack bytes
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,&tmp);
|
|
tmp |= P_BUFEN;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
for (i=0;i<3;i++) {
|
|
|
|
// clock next byte in...
|
|
// Assert SLC
|
|
tmp = tmp | P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// read in the byte
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_DATA,&sig_byte[i]);
|
|
|
|
// Deassert SLC
|
|
tmp &= (P_SLC ^ 0xff);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// note: there must be a delay here for timing, C will provide it
|
|
|
|
// Assert SLC
|
|
tmp |= P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// now upper nibble is on status register
|
|
|
|
// Deassert SLC
|
|
tmp &= (P_SLC ^ 0xff);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// ready to clock next byte in...
|
|
}
|
|
|
|
// set parallel port for use by printer
|
|
|
|
P3CSetPrinterMode(g,data,control);
|
|
|
|
// compare the signature bytes
|
|
if ((sig_byte[0] == 0x6c) && (sig_byte[1] == 0x55) &&
|
|
(sig_byte[2] == 0xaa)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// P3CDoCommand
|
|
//
|
|
// Called by the main loop to start a scsi command. This functions is the
|
|
// main entry point for all cards. It returns an SRB status code as defined
|
|
// in ..\..\inc\srb.h. A status code of RET_STATUS_PENDING means that the
|
|
// request has been sent to the controller and an interrupt is needed to
|
|
// finish the request. When this interrupt occurs CardFinishCommandInterrupt
|
|
// will be called.
|
|
//
|
|
USHORT P3CDoCommand(PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
P3CSetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
rval = ScsiDoCommand(t);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
P3CSetPrinterMode(g, data, control);
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// P3CStartCommandInterrupt
|
|
//
|
|
// This routines allow the driver to be polled by checking its
|
|
// CardInterrupt by for example using the timer interrupt, since
|
|
// the P3C does not support interrupts on its own.
|
|
//
|
|
//
|
|
USHORT P3CStartCommandInterrupt(PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
P3CSetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
rval = ScsiStartCommandInterrupt(t);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
P3CSetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// P3CFinishCommandInterrupt
|
|
//
|
|
// This routines allow the driver to be polled by checking its
|
|
// CardInterrupt by for example using the timer interrupt, since
|
|
// the P3C does not support interrupts on its own.
|
|
//
|
|
//
|
|
USHORT P3CFinishCommandInterrupt(PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
P3CSetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
rval = ScsiFinishCommandInterrupt(t);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
P3CSetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// P3CInterrupt
|
|
//
|
|
// This routines allow the driver to be polled by checking its
|
|
// CardInterrupt by for example using the timer interrupt, since
|
|
// the P3C does not support interrupts on its own.
|
|
//
|
|
BOOLEAN P3CInterrupt(PADAPTER_INFO g)
|
|
{
|
|
BOOLEAN rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
P3CSetScsiMode(g, &data, &control);
|
|
|
|
rval = N5380Interrupt(g);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
P3CSetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// P3CResetBus
|
|
//
|
|
// Resets the SCSI Bus
|
|
//
|
|
VOID P3CResetBus(PADAPTER_INFO g)
|
|
{
|
|
UCHAR data;
|
|
UCHAR control;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
P3CSetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
N5380ResetBus(g);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
P3CSetPrinterMode(g, data, control);
|
|
}
|
|
|
|
//
|
|
// P3CWriteBytesFast
|
|
//
|
|
// This routine is used by the ScsiFnc routines to write bytes to the scsi
|
|
// bus quickly. The ScsiFnc routines don't know how to do this quickly for
|
|
// a particular card, so they call this. This routine can be mapped to the
|
|
// slower ScsiWriteBytesSlow routine for small transferrs or if this routine
|
|
// is not supported.
|
|
//
|
|
USHORT P3CWriteBytesFast(PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
USHORT rval = 0;
|
|
UCHAR control;
|
|
UCHAR tmp;
|
|
|
|
// use slow mode for odd xfers (inquiry type commands) and audio
|
|
if (len % 512) {
|
|
return ScsiWriteBytesSlow(g, pbytes, len,
|
|
pActualLen, phase);
|
|
}
|
|
|
|
// enable dma on 5380
|
|
N5380EnableDmaWrite(g);
|
|
|
|
// put the P3C into write dma mode
|
|
P3CPutControl(g,PCCC_MODE_WDMA,0);
|
|
|
|
// start control reg off zero'ed, enable dma write mode
|
|
control = 0;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
|
|
{
|
|
ULONG xfer_count = len;
|
|
UCHAR control;
|
|
PBASE_REGISTER baseIoAddress = g->BaseIoAddress;
|
|
|
|
control = 0;
|
|
_asm {
|
|
push esi
|
|
push ds
|
|
#ifdef MODE_32BIT
|
|
mov edx,baseIoAddress
|
|
mov esi,pbytes
|
|
mov ecx,len
|
|
#else
|
|
mov dx, word ptr baseIoAddress
|
|
mov si, word ptr pbytes
|
|
mov cx, word ptr len
|
|
mov ds, word ptr pbytes+2
|
|
#endif // MODE_32BIT
|
|
mov bl,control // mask for control reg
|
|
mov bh,P_AFX // strobe mask
|
|
add dx,2 // dx points to control reg
|
|
get_bytes:
|
|
dec dx // dx points to status register
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz big_wait
|
|
ready:
|
|
dec dx // dx points to parallel data reg
|
|
mov al,[esi]
|
|
out dx,al
|
|
|
|
add dx,2 // dx points to control reg
|
|
xor bl,bh
|
|
mov al,bl
|
|
out dx,al // give strobe, clock next data byte
|
|
|
|
inc esi
|
|
dec ecx
|
|
jnz get_bytes
|
|
}
|
|
goto done_asm;
|
|
_asm {
|
|
big_wait:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
// wait for a while before going to a bigger timeout
|
|
push ecx
|
|
push ebx
|
|
mov ebx,TIMEOUT_READWRITE_LOOP
|
|
loop0:
|
|
mov ecx,0x10000
|
|
loop1:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready1
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready1
|
|
dec ecx
|
|
jnz loop1
|
|
dec ebx
|
|
jnz loop0
|
|
pop ebx
|
|
pop ecx
|
|
jmp short error
|
|
ready1:
|
|
pop ebx
|
|
pop ecx
|
|
jmp short ready
|
|
error:
|
|
mov rval,RET_STATUS_TIMEOUT
|
|
done_asm:
|
|
pop ds
|
|
pop esi
|
|
#ifdef MODE_32BIT
|
|
mov xfer_count,ecx
|
|
#else
|
|
mov word ptr xfer_count,ecx
|
|
#endif
|
|
}
|
|
|
|
// compute actual xfer len
|
|
|
|
*pActualLen = len - xfer_count;
|
|
}
|
|
|
|
// clear the dma mode of 5380
|
|
N5380DisableDmaWrite(g);
|
|
|
|
// if data underrun, return the under/over run error message
|
|
|
|
if (rval) {
|
|
|
|
// phase mismatch means data under/over run
|
|
|
|
N5380GetPhase(g,&tmp);
|
|
|
|
if (tmp == PHASE_STATUS) {
|
|
rval = RET_STATUS_DATA_OVERRUN;
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// P3CReadBytesFast
|
|
//
|
|
// This routine is used by the ScsiFnc routines to write bytes to the scsi
|
|
// bus quickly. The ScsiFnc routines don't know how to do this quickly for
|
|
// a particular card, so they call this. This routine can be mapped to the
|
|
// slower ScsiReadBytesSlow routine for small transferrs or if this routine
|
|
// is not supported.
|
|
//
|
|
#pragma optimize("",off)
|
|
USHORT P3CReadBytesFast(PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
USHORT rval = 0;
|
|
|
|
// use slow mode for odd xfers (inquiry type commands) and audio
|
|
if (len % 512) {
|
|
return ScsiReadBytesSlow(g, pbytes, len,
|
|
pActualLen, phase);
|
|
}
|
|
|
|
N5380EnableDmaRead(g);
|
|
|
|
// call the correct read fast routine based on the type of port
|
|
|
|
if (g->ParallelPortType == PT_BI) {
|
|
rval = P3CReadBytesFastBiDir(g, pbytes, len,
|
|
pActualLen, phase);
|
|
} else {
|
|
rval = P3CReadBytesFastUniDir(g, pbytes, len,
|
|
pActualLen, phase);
|
|
}
|
|
|
|
// clear the dma mode
|
|
N5380DisableDmaRead(g);
|
|
|
|
// if data underrun, return the under/over run error message
|
|
|
|
if (rval) {
|
|
UCHAR tmp;
|
|
|
|
// phase mismatch means data under/over run
|
|
|
|
N5380GetPhase(g,&tmp);
|
|
|
|
if (tmp == PHASE_STATUS) {
|
|
rval = RET_STATUS_DATA_OVERRUN;
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// USHORT P3CReadBytesFastBiDir(PADAPTER_INFO g, PUCHAR pbytes,
|
|
//
|
|
// Reads bytes fast on a bi-directional parallel port.
|
|
//
|
|
USHORT P3CReadBytesFastBiDir(PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
USHORT rval = 0;
|
|
|
|
// put the P3C into read dma mode
|
|
|
|
P3CPutControl(g,PCCC_MODE_RDMA_BYTE,0);
|
|
|
|
// start control reg with P_SLC and P_BUFEN
|
|
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,P_SLC | P_BUFEN);
|
|
|
|
// for inline assembly, we don't have to save eax-edx registers
|
|
{
|
|
ULONG xfer_count = len;
|
|
UCHAR control;
|
|
PBASE_REGISTER baseIoAddress = g->BaseIoAddress;
|
|
|
|
// keep track of control register
|
|
control = P_SLC | P_BUFEN;
|
|
|
|
_asm {
|
|
push esi
|
|
push ds
|
|
#ifdef MODE_32BIT
|
|
mov edx,baseIoAddress
|
|
mov esi,pbytes
|
|
mov ecx,len
|
|
#else
|
|
mov dx, word ptr baseIoAddress
|
|
mov si, word ptr pbytes
|
|
mov cx, word ptr len
|
|
mov ds, word ptr pbytes+2
|
|
#endif // MODE_32BIT
|
|
mov bl,control // mask for control reg
|
|
mov bh,P_AFX // strobe mask
|
|
add dx,2 // dx points to control register
|
|
get_bytes:
|
|
dec dx // dx points to status register
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz big_wait
|
|
|
|
ready:
|
|
dec dx // dx points to data register
|
|
|
|
// get next byte
|
|
in al,dx
|
|
mov [esi],al
|
|
|
|
// toggle strobe and select high nibble
|
|
add dx,2 // dx points to control register
|
|
xor bl,bh
|
|
mov al,bl
|
|
out dx,al // strobe to ack byte
|
|
|
|
// loop
|
|
inc esi
|
|
dec ecx
|
|
jnz get_bytes
|
|
}
|
|
goto done_asm;
|
|
_asm {
|
|
big_wait:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
// wait for a while before going to a bigger timeout
|
|
push ecx
|
|
push ebx
|
|
mov ebx,TIMEOUT_READWRITE_LOOP
|
|
loop0:
|
|
mov ecx,0x10000
|
|
loop1:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready1
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready1
|
|
|
|
dec ecx
|
|
jnz loop1
|
|
dec ebx
|
|
jnz loop0
|
|
pop ebx
|
|
pop ecx
|
|
jmp short error
|
|
ready1:
|
|
pop ebx
|
|
pop ecx
|
|
}
|
|
goto ready;
|
|
_asm {
|
|
error:
|
|
mov rval,RET_STATUS_TIMEOUT
|
|
done_asm:
|
|
pop ds
|
|
pop esi
|
|
#ifdef MODE_32BIT
|
|
mov xfer_count,ecx
|
|
#else
|
|
mov word ptr xfer_count,ecx
|
|
#endif
|
|
}
|
|
|
|
// compute actual xfer len
|
|
|
|
*pActualLen = len - xfer_count;
|
|
}
|
|
|
|
// zero control register, disable read dma mode
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// USHORT P3CReadBytesFastUniDir(PADAPTER_INFO g, PUCHAR pbytes,
|
|
//
|
|
// Reads bytes fast on a uni-directional parallel port.
|
|
//
|
|
USHORT P3CReadBytesFastUniDir(PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
USHORT rval = 0;
|
|
UCHAR data;
|
|
|
|
// put the P3C into read dma mode
|
|
P3CPutControl(g,PCCC_MODE_RDMA_NIBBLE,0);
|
|
|
|
// start data reg to select high nibble
|
|
data = 0x80;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,data);
|
|
data = 0;
|
|
|
|
// start control reg with P_SLC
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,P_SLC);
|
|
|
|
// for inline assembly, we don't have to save eax-edx registers
|
|
{
|
|
ULONG xfer_count = len;
|
|
PBASE_REGISTER baseIoAddress = g->BaseIoAddress;
|
|
|
|
_asm {
|
|
push esi
|
|
push ds
|
|
#ifdef MODE_32BIT
|
|
mov edx,baseIoAddress
|
|
mov esi,pbytes
|
|
mov ecx,len
|
|
#else
|
|
mov dx, word ptr baseIoAddress
|
|
mov si, word ptr pbytes
|
|
mov cx, word ptr len
|
|
mov ds, word ptr pbytes+2
|
|
#endif // MODE_32BIT
|
|
mov bl,data
|
|
get_bytes:
|
|
inc dx // dx points to status register
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz big_wait
|
|
|
|
// save the high nibble
|
|
ready:
|
|
mov ah,al
|
|
|
|
// select lower nibble
|
|
dec dx // dx points to data register
|
|
mov al,bl
|
|
out dx,al
|
|
|
|
// calculate high nibble
|
|
shl ah,1
|
|
and ah,0f0h
|
|
|
|
// get lower nibble
|
|
inc dx // dx points to status register
|
|
in al,dx
|
|
mov bh,al
|
|
|
|
// toggle strobe and select high nibble
|
|
dec dx // dx points to data register
|
|
xor bl,0x40
|
|
mov al,bl
|
|
or al,80h
|
|
out dx,al
|
|
|
|
//
|
|
// We need some delay here for the t348 to respond to the strobe
|
|
// and lower the p_busy line. This loop serves the purpose and
|
|
// allows us to break out early in some cases. I have tried this
|
|
// with cx = 1 and this is not enough time. cx = 2 is enough, but
|
|
// for faster 486's or 586's this may need to be boosted higher.
|
|
//
|
|
// Note: the jmp $+2 that has been in our 348 code for DOS runs
|
|
// significantly faster on the 486, and out instructions are also
|
|
// much faster in protected mode! This totally threw off the timing
|
|
// of our DOS code.
|
|
// -KJB
|
|
//
|
|
#if 0
|
|
push cx
|
|
mov cx,100
|
|
inc dx // dx points to status register
|
|
loop1:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz out1
|
|
dec cx
|
|
jnz loop1
|
|
out1:
|
|
pop cx
|
|
dec dx // dx points to data register
|
|
#endif
|
|
|
|
// compute low nibble and the whole byte
|
|
shr bh,1
|
|
shr bh,1
|
|
shr bh,1
|
|
and bh,0fh
|
|
or ah,bh
|
|
mov al,ah
|
|
|
|
|
|
// store data and loop
|
|
mov [esi],al
|
|
inc esi
|
|
dec ecx
|
|
jnz get_bytes
|
|
}
|
|
goto done_asm;
|
|
_asm {
|
|
big_wait:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready
|
|
|
|
// wait for a while before going to a bigger timeout
|
|
push ecx
|
|
push ebx
|
|
mov ebx,TIMEOUT_READWRITE_LOOP
|
|
loop0:
|
|
mov ecx,0x10000
|
|
loop1:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready1
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz ready1
|
|
|
|
dec ecx
|
|
jnz loop1
|
|
dec ebx
|
|
jnz loop0
|
|
pop ebx
|
|
pop ecx
|
|
jmp short error
|
|
ready1:
|
|
pop ebx
|
|
pop ecx
|
|
}
|
|
goto ready;
|
|
_asm {
|
|
error:
|
|
mov rval,RET_STATUS_TIMEOUT
|
|
done_asm:
|
|
pop ds
|
|
pop esi
|
|
#ifdef MODE_32BIT
|
|
mov xfer_count,ecx
|
|
#else
|
|
mov word ptr xfer_count,ecx
|
|
#endif
|
|
}
|
|
|
|
// compute actual xfer len
|
|
|
|
*pActualLen = len - xfer_count;
|
|
}
|
|
|
|
// zero control register, disable read dma mode
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
|
|
return rval;
|
|
}
|
|
#pragma optimize("",on)
|
|
|
|
//
|
|
// N5380PortPut
|
|
//
|
|
// This routine is used by the N5380.C module to write byte to a 5380
|
|
// controller. This allows the module to be card independent. Other
|
|
// modules that assume a N5380 may also use this function.
|
|
//
|
|
VOID N5380PortPut(PADAPTER_INFO g,UCHAR reg,UCHAR byte)
|
|
{
|
|
|
|
P3CPutControl(g,PCCC_MODE_WPER,reg);
|
|
|
|
// write the byte
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,byte);
|
|
|
|
// toggle the data_ready line
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,P_SLC);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
}
|
|
|
|
//
|
|
// N5380PortGet
|
|
//
|
|
// This routine is used by the N5380.C module to get a byte from a 5380
|
|
// controller. This allows the module to be card independent. Other
|
|
// modules that assume a N5380 may also use this function.
|
|
//
|
|
VOID N5380PortGet(PADAPTER_INFO g,UCHAR reg,PUCHAR byte)
|
|
{
|
|
UCHAR tmp,tmp1;
|
|
|
|
P3CPutControl(g,PCCC_MODE_RPER_NIBBLE,reg);
|
|
|
|
// assert slc
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,P_SLC);
|
|
|
|
// select high nibble
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,0x80);
|
|
|
|
// read high nibble
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_STATUS,&tmp);
|
|
|
|
// compute high nibble
|
|
tmp = (tmp << 1) & 0xf0;
|
|
|
|
// select low nibble
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,0x00);
|
|
|
|
// read low nibble
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_STATUS,&tmp1);
|
|
|
|
// compute low nibble
|
|
tmp1 = (tmp1 >> 3) & 0x0f;
|
|
|
|
// compute and return byte
|
|
*byte = tmp1 | tmp;
|
|
|
|
// clear slc
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
}
|