Windows NT 4.0 source code leak
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

//---------------------------------------------------------------------
//
// 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);
}