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