home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_03_04
/
3n04017a
< prev
next >
Wrap
Text File
|
1992-02-20
|
14KB
|
445 lines
#include <dos.h>
#include "dma.h"
//--- Global Data --------------------------------------------------------
BYTE PageRegister[ 8 ] = { DMA_PAGE_0, DMA_PAGE_1, DMA_PAGE_2, DMA_PAGE_3,
DMA_PAGE_4, DMA_PAGE_5, DMA_PAGE_6, DMA_PAGE_7 };
DMA_DESCRIPTOR DmaDescriptor;
DMA_INFO DmaInfo;
//--- External References ------------------------------------------------
extern void Device_RetrieveDmaInfo( DMA_INFO * );
extern INTERRUPT_TYPE Device_TypeOfInterrupt( void );
extern USHORT Device_QuerySpaceAvail( void );
extern USHORT Device_QueryDataAvail( void );
extern Env_DoEoi( void );
//--- Local Functions ----------------------------------------------------
void DmaAbort(DMA_INFO *);
void DmaSendComplete( DMA_INFO * );
void DmaReceiveComplete( DMA_INFO * );
void DmaSend( DMA_INFO * );
void DmaReceive( DMA_INFO * );
void ProgramDmaController( DMA_INFO * );
USHORT ReadTransferCount( DMA_INFO * );
void CheckForPageWrap( USHORT *pCount, ULONG physAddr );
#ifdef DOS
ULONG Dos_GetPhysAddr( char far *pBuffer, DMA_INFO *pDmaInfo );
#endif
#ifdef WIN3
ULONG Win3_GetPhysAddr( char far * pBuffer, DMA_INFO *pDmaInfo );
void Win3_ReleaseBuffer( DMA_INFO * );
#endif
#ifdef OS2
ULONG OS2_GetPhysAddr( char *pBuffer, DMA_INFO *pDmaInfo,
BUF_INFO *pBufStruc );
#endif
void InterruptHandler( void )
{
INTERRUPT_TYPE intType;
/* Determine cause(s) of interrupt. */
intType = Device_TypeOfInterrupt();
/* Interrupt priority is:
* 1. DmaAbort
* 2. DmaComplete
* 3. DeviceRdyToTx
* 4. DeviceRdyToRx */
if (intType.DmaAbort)
DmaAbort( &DmaInfo );
if (intType.DmaComplete)
{
/* Interrupt type doesn't tell us whether completed
* transfer was TO device or FROM device. Use
* TransferType field to decide. */
if (DmaInfo.TransferType == TRANSFER_WRITE_TO_MEM )
DmaReceiveComplete( &DmaInfo );
else if (DmaInfo.TransferType == TRANSFER_READ_FROM_MEM)
DmaSendComplete( &DmaInfo );
}
/* After DmaComplete housekeeping, driver is
* ready to handle another DMA transfer during
* same interrupt, if device is ready. */
if (intType.DeviceRdyToTx)
DmaReceive( &DmaInfo);
if (intType.DeviceRdyToRx)
DmaSend( &DmaInfo);
/* DMA stuff is complete. Must send EOI to
* interrupt controller before returning. */
Env_DoEoi();
}
void DmaAbort( DMA_INFO *pDmaInfo )
{
USHORT bytesNotTransferred;
/* Must determine how many bytes were transferred
* before abort. First ask DMA controller how many
* bytes have not yet been transferred. Then
* subtract from original transfer count. */
bytesNotTransferred = ReadTransferCount( pDmaInfo );
pDmaInfo->TransferCount = pDmaInfo->TransferCount - bytesNotTransferred;
// Now that TransferCount field is updated,
// call subroutine to advance queue pointers.
if (pDmaInfo->TransferType == TRANSFER_WRITE_TO_MEM)
DmaReceiveComplete( pDmaInfo );
else if (pDmaInfo->TransferType == TRANSFER_READ_FROM_MEM)
DmaSendComplete( pDmaInfo );
}
void DmaReceiveComplete( DMA_INFO *pDmaInfo )
{
// Update receive queue ptr, accounting for wrap.
pDmaInfo->RxBuf.pIn += pDmaInfo->TransferCount;
if (pDmaInfo->RxBuf.pIn > pDmaInfo->RxBuf.pEnd)
pDmaInfo->RxBuf.pIn = pDmaInfo->RxBuf.pStart;
#ifdef WIN3
Win3_ReleaseBuffer( pDmaInfo );
#endif
}
void DmaSendComplete( DMA_INFO *pDmaInfo )
{
// Update transmit queue ptr, accounting for wrap.
pDmaInfo->TxBuf.pOut += pDmaInfo->TransferCount;
if (pDmaInfo->TxBuf.pOut > pDmaInfo->TxBuf.pEnd)
pDmaInfo->TxBuf.pOut = pDmaInfo->TxBuf.pStart;
#ifdef WIN3
Win3_ReleaseBuffer( pDmaInfo );
#endif
}
void DmaSend( DMA_INFO *pDmaInfo )
{
char *pIn, *pOut;
USHORT bytesInQueue;
USHORT roomInDevice;
// Calculate bytes in driver's transmit queue.
pOut = pDmaInfo->TxBuf.pOut;
pIn = pDmaInfo->TxBuf.pIn;
if (pIn >= pOut)
{
bytesInQueue = pIn - pOut;
}
else
{
// DMA transfer data must be contiguous,
// so stop at end of buffer.
bytesInQueue = pDmaInfo->TxBuf.pEnd - pOut + 1;
}
// Ask device how much space it has available
// for new data. Device may not have enough space for
// all we have.
roomInDevice = Dev_QuerySpaceAvail();
if (roomInDevice < bytesInQueue)
pDmaInfo->TransferCount = roomInDevice;
else
pDmaInfo->TransferCount = bytesInQueue;
// Determine physical address of first byte in DMA
// transfer. Since we're sending data _to_ device,
// use the pOut pointer.
pDmaInfo->TransferPhys =
GET_PHYS_ADDR( (char far *)pDmaInfo->TxBuf.pOut,
pDmaInfo, &pDmaInfo->TxBuf );
pDmaInfo->TransferType = TRANSFER_READ_FROM_MEM;
ProgramDmaController( pDmaInfo );
}
void DmaReceive( DMA_INFO *pDmaInfo )
{
USHORT spaceAvail;
USHORT dataAvail;
char *pOut, *pIn;
// Calculate space in driver's receive queue.
pIn = pDmaInfo->RxBuf.pIn;
pOut = pDmaInfo->RxBuf.pOut;
if (pOut > pIn)
{
spaceAvail = pOut - pIn - 1;
}
else
{
// DMA transfer data must be contiguous,
// so stop at end of buffer.
spaceAvail = pDmaInfo->RxBuf.pEnd - pIn + 1;
if (pOut == pDmaInfo->RxBuf.pStart)
spaceAvail--; // must leave last byte empty
}
if (spaceAvail)
{
dataAvail = Dev_QueryDataAvail();
if (dataAvail > spaceAvail)
pDmaInfo->TransferCount = spaceAvail;
else
pDmaInfo->TransferCount = dataAvail;
// Determine physical address of first byte in
// DMA transfer. Since we're receiving data
// _from_ device, use the pIn pointer.
pDmaInfo->TransferPhys =
GET_PHYS_ADDR( (char far *)pDmaInfo->RxBuf.pIn,
pDmaInfo, &pDmaInfo->RxBuf );
pDmaInfo->TransferType = TRANSFER_WRITE_TO_MEM;
ProgramDmaController( pDmaInfo );
}
}
USHORT ReadTransferCount( DMA_INFO *pDmaInfo )
{
USHORT count;
BYTE regTransferCount;
BYTE channel = pDmaInfo->Channel;
if (pDmaInfo->BusType != BUS_MICROCHANNEL)
{
if (channel <= 3)
{
regTransferCount = DMA_CONTROLLER_0_3
+ DMA_OFFSET_COUNT;
outp( DMA_CONTROLLER_0_3 + DMA_OFFSET_CLEAR, 0 );
}
else
{
regTransferCount = DMA_CONTROLLER_4_7
+ DMA_OFFSET_COUNT;
outp( DMA_CONTROLLER_4_7 + DMA_OFFSET_CLEAR, 0 );
}
regTransferCount += (channel << 1);
// First byte read is LSB of count.
count = inp( regTransferCount );
// Second byte read is MSB of count.
// Combine with LSB.
count = (count << 8) | (inp( regTransferCount ) );
}
else
{
outp( DMA_XFN, Get_Count + channel );
// First byte read is LSB of count.
count = inp( DMA_EXE );
// Second byte read is MSB of count.
// Combine with LSB.
count = (count >> 8) | inp( DMA_EXE );
}
return( count - 1 );
}
void ProgramDmaController( DMA_INFO * pDmaInfo )
{
ULONG physAddr = pDmaInfo->TransferPhys;
USHORT count = pDmaInfo->TransferCount;
BYTE channel = pDmaInfo->Channel;
BYTE baseReg;
BYTE tempReg;
// Controller chips on MicroChannel are
// different than XT or AT.
if (pDmaInfo->BusType != BUS_MICROCHANNEL)
{
if (channel <= 3)
{
baseReg = DMA_CONTROLLER_0_3;
outp( DMA_CONTROLLER_0_3 + DMA_OFFSET_CLEAR, 0 );
}
else
{
baseReg = DMA_CONTROLLER_4_7;
outp( DMA_CONTROLLER_4_7 + DMA_OFFSET_CLEAR, 0 );
}
// Set channel's mask bit to disable channel
outp( baseReg + DMA_OFFSET_MASK, channel | 0x04 );
// Set mode to read or write.
if (pDmaInfo->TransferType == TRANSFER_READ_FROM_MEM)
{
outp( baseReg + DMA_OFFSET_MODE, channel
| DMA_type_read );
}
else
{
outp( baseReg + DMA_OFFSET_MODE, channel
| DMA_type_write );
}
// Set up address register. Remember to do
// "shift magic" if transfer will be a 16-bit
// transfer (channels 4-7).
if (channel >= 4)
physAddr >>= 1;
tempReg = baseReg + DMA_OFFSET_ADDRESS + (channel << 1);
outp( tempReg, LOBYTE((LUSHORT( physAddr ))) ); // bits 7-0 go first
outp( tempReg, HIBYTE((LUSHORT( physAddr ))) ); // bits 15-8 go next
// Set up transfer count, which is always I/O address
// after address register.
tempReg++;
count = pDmaInfo->TransferCount - 1;
if (channel >= 4)
count >>= 1;
outp( tempReg, LOBYTE( count ) ); // bits 7-0
outp( tempReg, HIBYTE( count ) ); // bits 15-8
// Set up page table register.
tempReg = PageRegister[ channel ];
outp( tempReg, LOBYTE(HIUSHORT( physAddr )) ); // bits 23-16
// Clear channel's mask bit to enable channel
outp( baseReg + DMA_OFFSET_MASK, channel );
}
else
{
// Disable transfers on this channel.
outp( DMA_XFN, Set_Chn_Mask + channel );
// Set channel mode to read or write.
outp( DMA_XFN, Set_Mode + channel );
if (pDmaInfo->TransferType ==
TRANSFER_READ_FROM_MEM)
{
outp( DMA_EXE, Transfer_Data );
}
else
{
outp( DMA_EXE, Transfer_Data | Write_Mem );
}
// Set up address register
outp( DMA_XFN, channel + Set_Mem_Adr );
outp( DMA_EXE, LOBYTE((LUSHORT( physAddr ))) );
outp( DMA_EXE, HIBYTE((LUSHORT( physAddr ))) );
outp( DMA_EXE, LOBYTE((HUSHORT( physAddr ))) );
// Set up transfer count.
count = pDmaInfo->TransferCount - 1;
outp( DMA_XFN, Set_Count + channel );
outp( DMA_EXE, LOBYTE( count ) ); // bits 7-0
outp( DMA_EXE, HIBYTE( count ) ); // bits 15-8
// Enable transfers on this channel.
outp( DMA_XFN, Reset_Chn_Mask + channel );
}
}
#ifdef DOS
ULONG Dos_GetPhysAddr( char far *pBuffer, DMA_INFO *pDmaInfo )
{
ULONG physAddr;
physAddr = (ULONG)FP_SEG( pBuffer );
physAddr <<= 4;
physAddr += (ULONG)FP_OFF( pBuffer );
// If transfer will cross over 64K page boundary,
// shorten transfer by updating count.
CheckForPageWrap( &(pDmaInfo->TransferCount), physAddr );
return( physAddr );
}
#endif
#ifdef WIN3
ULONG Win3_GetPhysAddr( char far * pBuffer, DMA_INFO *pDmaInfo )
{
DMA_DESCRIPTOR *pDmaDescriptor;
char far *pVdsCheckByte;
BYTE channel = pDmaInfo->Channel;
// Can't usually point directly to physical
// memory in Windows, but Windows guarantees
// selector 0x40 points to physical 0x0400.
FP_SEG( pVdsCheckByte ) = 0x40;
FP_OFF( pVdsCheckByte ) = 0x7B;
if (!(*pVdsCheckByte & 0x20))
{
// VDS is not available, do
// real mode calculation.
Dos_GetPhysAddr( pBuffer, pDmaInfo );
}
else
{
// Disable Translation
_asm
{
mov ah, VDS_SERVICES
mov al, VDS_DISABLE_TRANSLATION
mov bl, channel
int 0x4B
}
// Lock buffer region
DmaDescriptor.Selector = FP_SEG( pBuffer );
DmaDescriptor.Linear = FP_OFF( pBuffer );
DmaDescriptor.Size = pDmaInfo->TransferCount;
pDmaDescriptor = &DmaDescriptor;
_asm
{
mov ah, VDS_SERVICES
mov al, VDS_LOCK
mov dx, VDS_FLAGS_COPY+VDS_FLAGS_ALIGN64K+VDS_FLAGS_ALIGN128K
mov di, pDmaDescriptor
push es
push ds
pop es
int 0x4B
pop es
}
return( DmaDescriptor.Physical );
}
}
void Win3_ReleaseBuffer( DMA_INFO *pDmaInfo )
{
BYTE channel = pDmaInfo->Channel;
DMA_DESCRIPTOR *pDmaDescriptor;
// Enable Translation
_asm
{
mov ah, VDS_SERVICES
mov al, VDS_ENABLE_TRANSLATION
mov bl, channel
int 0x4B
}
// Unlock buffer region
pDmaDescriptor = &DmaDescriptor;
_asm
{
mov ah, VDS_SERVICES
mov al, VDS_UNLOCK
mov dx, VDS_FLAGS_COPY
mov di, pDmaDescriptor
push es
push ds
pop es
int 0x4B
pop es
}
}
#endif
#ifdef OS2
ULONG OS2_GetPhysAddr( char *pBuffer,
DMA_INFO *pDmaInfo, BUF_INFO *pBufStruc )
{
char *pDistanceFromStart;
ULONG physAddr;
pDistanceFromStart = pBuffer - pBufStruc->pStart;
physAddr = pBufStruc->PhysAddr +
(ULONG)pDistanceFromStart;
CheckForPageWrap( &(pDmaInfo->TransferCount), physAddr );
return( physAddr );
}
#endif
void CheckForPageWrap( USHORT *pCount, ULONG physAddr )
{
ULONG endPhysAddr;
endPhysAddr = physAddr + (ULONG)*pCount - 1;
if ( (endPhysAddr & 0xFFFF0000) !=
(physAddr & 0xFFFF0000) )
*pCount = 0x0000 - (USHORT)physAddr;
}