mirror of https://github.com/lianthony/NT4.0
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.
763 lines
18 KiB
763 lines
18 KiB
//---------------------------------------------------------------------
|
|
//
|
|
// T338.C
|
|
//
|
|
// Trantor T338 Logic Module. Contains functions to access the T338
|
|
// adapter.
|
|
//
|
|
// Revisions:
|
|
// 02-01-93 KJB First.
|
|
// 02-23-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: T338DoIo.
|
|
// 03-24-93 KJB Fixed SetScsiMode so it does not reset the n5380!
|
|
// 05-14-93 KJB Added CardParseCommandString for card specific
|
|
// standard string parsing across platforms.
|
|
// Changed CardCheckAdapter to accept an
|
|
// Initialization info from command line, ie
|
|
// force bi-directional ports, etc.
|
|
// All functions that used to take an PBASE_REGISTER
|
|
// parameter now take PWORKSPACE. CardCheckAdapter
|
|
// takes the both a PINIT and a PWORKSPACE parameters.
|
|
// 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-16-93 KJB Fixed parameter bugs introduced while doing the
|
|
// PWORKSPACE changes.
|
|
// 05-17-93 KJB Fixed compiler warnings.
|
|
//
|
|
//---------------------------------------------------------------------
|
|
|
|
#include CARDTXXX_H
|
|
|
|
// Local Functions
|
|
|
|
VOID T338PutControl(PADAPTER_INFO g,UCHAR mode, UCHAR reg);
|
|
VOID T338SetPrinterMode(PADAPTER_INFO g, UCHAR data, UCHAR control);
|
|
VOID T338SetScsiMode(PADAPTER_INFO g, PUCHAR data, PUCHAR control);
|
|
|
|
//
|
|
// T338PutControl
|
|
//
|
|
// Puts a control byte to the T338 style adapter. This sets the mode
|
|
// to IOR or IOW and the address byte of the N5380 register.
|
|
//
|
|
VOID T338PutControl(PADAPTER_INFO g,UCHAR mode, UCHAR reg)
|
|
{
|
|
UCHAR tmp;
|
|
|
|
// the following bits are active low: IOW, IOR, MR
|
|
|
|
tmp = reg | (mode ^ (T338_MR | T338_IOW | T338_IOR));
|
|
|
|
// put the control byte on the data lines
|
|
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,tmp);
|
|
|
|
// assert slc to indicate byte is there
|
|
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,P_SLC);
|
|
|
|
// clear slc
|
|
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,0);
|
|
}
|
|
|
|
//
|
|
// T338SetPrinterMode
|
|
//
|
|
// This routine sets the T338 to printer pass through mode. This is the
|
|
// default mode and should be set after the brief use of scsi mode.
|
|
//
|
|
VOID T338SetPrinterMode(PADAPTER_INFO g, UCHAR data, UCHAR control)
|
|
{
|
|
UCHAR tmp;
|
|
|
|
// do we have to disable interrupts?
|
|
|
|
// negate all control signals...
|
|
|
|
T338PutControl(g,0,0);
|
|
|
|
// restore data register
|
|
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,data);
|
|
|
|
// leave p_init negated (1)
|
|
|
|
tmp = control | P_INIT;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
}
|
|
|
|
//
|
|
// T338SetScsiMode
|
|
//
|
|
// This routine sets the T338 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 T338SetScsiMode(PADAPTER_INFO g, PUCHAR data, PUCHAR control)
|
|
{
|
|
UCHAR tmp;
|
|
|
|
// save parallel data
|
|
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_DATA,data);
|
|
|
|
// zero data register
|
|
// note: the signals IOW,IOR,MR are active low, so assert them..
|
|
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_DATA,
|
|
T338_MR | T338_IOW | T338_IOR);
|
|
|
|
// save parallel control
|
|
|
|
ParallelPortGet(g->BaseIoAddress,PARALLEL_CONTROL,control);
|
|
*control = *control & (P_BUFEN ^ 0xff);
|
|
|
|
// clear p_init and set p_slc
|
|
|
|
tmp = (*control & (P_INIT ^ 0xff) ) | P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// clear p_init and set p_slc
|
|
|
|
tmp = (*control & (P_INIT ^ 0xff) ) | P_SLC;
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
|
|
// clear slc, leave p_init asserted (0)
|
|
|
|
tmp = tmp & (P_SLC ^ 0xff);
|
|
ParallelPortPut(g->BaseIoAddress,PARALLEL_CONTROL,tmp);
|
|
}
|
|
|
|
//
|
|
// T338CheckAdapter
|
|
//
|
|
// This routine is used to sense the presense of the T338 adapter out
|
|
// on the Parallel port. It will only detect the adapter if a device
|
|
// is providing termination power.
|
|
//
|
|
BOOLEAN T338CheckAdapter(PADAPTER_INFO g)
|
|
{
|
|
UCHAR data;
|
|
UCHAR control;
|
|
BOOLEAN rval;
|
|
|
|
// set scsi mode
|
|
|
|
T338SetScsiMode(g,&data,&control);
|
|
|
|
// reset the 5380
|
|
|
|
T338PutControl(g,T338_MR,0);
|
|
T338PutControl(g,0,0);
|
|
|
|
// check to see if a 5380 is there
|
|
|
|
rval = N5380CheckAdapter(g);
|
|
|
|
// set parallel port for use by printer
|
|
|
|
T338SetPrinterMode(g,data,control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// T338DoCommand
|
|
//
|
|
// 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 T338DoCommand(PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
T338SetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
rval = ScsiDoCommand(t);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
T338SetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// T338StartCommandInterrupt
|
|
//
|
|
// This routines allow the driver to be polled by checking its
|
|
// CardInterrupt by for example using the timer interrupt, since
|
|
// the T338 does not support interrupts on its own.
|
|
//
|
|
//
|
|
USHORT T338StartCommandInterrupt(PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
T338SetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
rval = ScsiStartCommandInterrupt(t);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
T338SetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// T338FinishCommandInterrupt
|
|
//
|
|
// This routines allow the driver to be polled by checking its
|
|
// CardInterrupt by for example using the timer interrupt, since
|
|
// the T338 does not support interrupts on its own.
|
|
//
|
|
//
|
|
USHORT T338FinishCommandInterrupt(PTSRB t)
|
|
{
|
|
USHORT rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
PADAPTER_INFO g = t->pWorkspace;
|
|
|
|
// put the T338 into ScsiMode
|
|
|
|
T338SetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
rval = ScsiFinishCommandInterrupt(t);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
T338SetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
// T338StartCommandInterrupt
|
|
//
|
|
// This routines allow the driver to be polled by checking its
|
|
// CardInterrupt by for example using the timer interrupt, since
|
|
// the T338 does not support interrupts on its own.
|
|
//
|
|
BOOLEAN T338Interrupt(PADAPTER_INFO g)
|
|
{
|
|
BOOLEAN rval;
|
|
UCHAR data;
|
|
UCHAR control;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
T338SetScsiMode(g, &data, &control);
|
|
|
|
rval = N5380Interrupt(g);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
T338SetPrinterMode(g, data, control);
|
|
|
|
return rval;
|
|
}
|
|
|
|
//
|
|
//
|
|
// T338ResetBus
|
|
//
|
|
// Resets the SCSI Bus
|
|
//
|
|
VOID T338ResetBus(PADAPTER_INFO g)
|
|
{
|
|
UCHAR data;
|
|
UCHAR control;
|
|
|
|
// put the parallel adapter into scsi mode
|
|
|
|
T338SetScsiMode(g, &data, &control);
|
|
|
|
// execute the complete command now, without interrupts
|
|
|
|
N5380ResetBus(g);
|
|
|
|
// put the parallel adapter back to parallel mode
|
|
|
|
T338SetPrinterMode(g, data, control);
|
|
}
|
|
|
|
//
|
|
// T338WriteBytesFast
|
|
//
|
|
// 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 T338WriteBytesFast (PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
USHORT rval = 0;
|
|
|
|
// use slow mode for odd xfers (inquiry type commands) & audio
|
|
|
|
if (len % 512) {
|
|
return ScsiWriteBytesSlow (g, pbytes, len,
|
|
pActualLen, phase);
|
|
}
|
|
|
|
// start dma mode
|
|
|
|
N5380EnableDmaWrite (g);
|
|
|
|
// put the T338 into write dma mode
|
|
|
|
T338PutControl (g,T338_IOW,0);
|
|
|
|
{
|
|
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
|
|
|
|
add dx,2 // dx points to control reg
|
|
|
|
get_bytes:
|
|
dec dx // dx points to status register
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz big_wait
|
|
|
|
ready:
|
|
dec dx // dx points to parallel data reg
|
|
mov al,[esi]
|
|
out dx,al
|
|
|
|
// assert DACK
|
|
|
|
add dx,2 // dx points to control reg
|
|
mov al, P_AFX
|
|
out dx,al
|
|
|
|
// deassert DACK
|
|
|
|
mov al,0
|
|
out dx,al
|
|
|
|
inc esi
|
|
dec ecx
|
|
jnz get_bytes
|
|
}
|
|
goto done_asm;
|
|
_asm {
|
|
big_wait:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz 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
|
|
jz ready1
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz 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 bit of 5380
|
|
|
|
N5380DisableDmaWrite (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;
|
|
}
|
|
|
|
//
|
|
// T338ReadBytesFast
|
|
//
|
|
// 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 T338ReadBytesFast (PADAPTER_INFO g, PUCHAR pbytes,
|
|
ULONG len, PULONG pActualLen, UCHAR phase)
|
|
{
|
|
USHORT rval = 0;
|
|
|
|
// use slow mode for small xfers (inquiry type commands) and audio
|
|
|
|
if (len % 512) {
|
|
return ScsiReadBytesSlow (g, pbytes, len,
|
|
pActualLen, phase);
|
|
}
|
|
|
|
// start dma read
|
|
|
|
N5380EnableDmaRead (g);
|
|
|
|
// put the t338 into read mode
|
|
|
|
T338PutControl (g,T338_IOR,0);
|
|
|
|
// to be fast, for 386 machines, this must be coded in assembly
|
|
// 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
|
|
inc dx // dx points to status register
|
|
|
|
get_bytes:
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jnz big_wait
|
|
|
|
ready:
|
|
|
|
// assert DACK, the P_AFX bit
|
|
|
|
inc dx // dx points to control register
|
|
mov al,P_AFX
|
|
out dx,al
|
|
|
|
// select high nibble
|
|
|
|
sub dx,2 // dx points to data register
|
|
mov al,0x80
|
|
out dx,al
|
|
|
|
// get high nibble
|
|
|
|
inc dx // dx points to status register
|
|
in al,dx
|
|
mov ah,al
|
|
|
|
// select lower nibble
|
|
|
|
dec dx // dx points to data register
|
|
xor al,al
|
|
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
|
|
|
|
// deassert DACK, clear P_AFX
|
|
|
|
inc dx // dx points to control register
|
|
xor al,al
|
|
out dx,al
|
|
|
|
dec dx // dx points to status register
|
|
|
|
// 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
|
|
jz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz ready
|
|
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz 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
|
|
jz ready1
|
|
in al,dx
|
|
test al,P_BUSY
|
|
jz 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);
|
|
|
|
// clear the dma read 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;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
|
|
// set T338 logic into data write mode
|
|
|
|
T338PutControl (g, T338_IOW, reg);
|
|
|
|
// write the byte
|
|
|
|
ParallelPortPut (g->BaseIoAddress, PARALLEL_DATA, byte);
|
|
|
|
// toggle the strobe line
|
|
|
|
ParallelPortPut (g->BaseIoAddress, PARALLEL_CONTROL, P_STB);
|
|
ParallelPortPut (g->BaseIoAddress, PARALLEL_CONTROL, 0);
|
|
|
|
// clear data write mode
|
|
|
|
T338PutControl (g, 0, 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;
|
|
|
|
// set T338 logic to read mode
|
|
|
|
T338PutControl (g, T338_IOR, reg);
|
|
|
|
// select high nibble
|
|
|
|
ParallelPortPut (g->BaseIoAddress, PARALLEL_DATA, 0x80);
|
|
|
|
// assert stb
|
|
|
|
ParallelPortPut (g->BaseIoAddress, PARALLEL_CONTROL, P_STB);
|
|
|
|
// 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);
|
|
|
|
// clear data read mode
|
|
|
|
T338PutControl (g, 0, 0);
|
|
}
|
|
|
|
|