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 >
Text File  |  1992-02-20  |  14KB  |  445 lines

  1. #include <dos.h>
  2. #include "dma.h"
  3.  
  4. //--- Global Data --------------------------------------------------------
  5. BYTE PageRegister[ 8 ] = { DMA_PAGE_0, DMA_PAGE_1, DMA_PAGE_2, DMA_PAGE_3, 
  6.                            DMA_PAGE_4, DMA_PAGE_5, DMA_PAGE_6, DMA_PAGE_7 };
  7. DMA_DESCRIPTOR DmaDescriptor;
  8. DMA_INFO DmaInfo;
  9. //--- External References ------------------------------------------------
  10. extern void Device_RetrieveDmaInfo( DMA_INFO * );
  11. extern INTERRUPT_TYPE Device_TypeOfInterrupt( void );
  12. extern USHORT Device_QuerySpaceAvail( void );
  13. extern USHORT Device_QueryDataAvail( void );
  14. extern Env_DoEoi( void );
  15. //--- Local Functions ----------------------------------------------------
  16. void DmaAbort(DMA_INFO *);
  17. void DmaSendComplete( DMA_INFO * );
  18. void DmaReceiveComplete( DMA_INFO * );
  19. void DmaSend( DMA_INFO * );
  20. void DmaReceive( DMA_INFO * );
  21. void ProgramDmaController( DMA_INFO * );
  22. USHORT ReadTransferCount( DMA_INFO * );
  23. void CheckForPageWrap( USHORT *pCount, ULONG physAddr );
  24. #ifdef DOS
  25. ULONG Dos_GetPhysAddr( char far *pBuffer, DMA_INFO *pDmaInfo );
  26. #endif
  27. #ifdef WIN3
  28. ULONG Win3_GetPhysAddr( char far * pBuffer, DMA_INFO *pDmaInfo );
  29. void Win3_ReleaseBuffer( DMA_INFO * );
  30. #endif
  31. #ifdef OS2
  32. ULONG OS2_GetPhysAddr( char *pBuffer, DMA_INFO *pDmaInfo, 
  33.       BUF_INFO *pBufStruc );
  34. #endif
  35.  
  36. void InterruptHandler( void )
  37. {
  38.    INTERRUPT_TYPE intType;
  39.  
  40.    /* Determine cause(s) of interrupt. */  
  41.    intType = Device_TypeOfInterrupt();
  42.  
  43.    /* Interrupt priority is:                          
  44.     * 1. DmaAbort
  45.     * 2. DmaComplete
  46.     * 3. DeviceRdyToTx
  47.     * 4. DeviceRdyToRx */
  48.    if (intType.DmaAbort)
  49.       DmaAbort( &DmaInfo );
  50.    if (intType.DmaComplete)
  51.    {
  52.       /* Interrupt type doesn't tell us whether completed 
  53.        * transfer was TO device or FROM device. Use 
  54.        * TransferType field to decide. */
  55.       if (DmaInfo.TransferType == TRANSFER_WRITE_TO_MEM )
  56.          DmaReceiveComplete( &DmaInfo );
  57.       else if (DmaInfo.TransferType == TRANSFER_READ_FROM_MEM)
  58.          DmaSendComplete( &DmaInfo );
  59.    }
  60.  
  61.   /* After DmaComplete housekeeping, driver is 
  62.    * ready to handle another DMA transfer during 
  63.    * same interrupt, if device is ready. */ 
  64.   if (intType.DeviceRdyToTx)          
  65.      DmaReceive( &DmaInfo);           
  66.   if (intType.DeviceRdyToRx)          
  67.      DmaSend( &DmaInfo);              
  68.    /* DMA stuff is complete. Must send EOI to 
  69.     * interrupt controller before returning. */
  70.    Env_DoEoi();  
  71. }
  72.  
  73. void DmaAbort( DMA_INFO *pDmaInfo )
  74. {
  75.    USHORT   bytesNotTransferred;
  76.  
  77. /* Must determine how many bytes were transferred 
  78.  * before abort. First ask DMA controller how many 
  79.  * bytes have not yet been transferred. Then 
  80.  * subtract from original transfer count. */
  81.    bytesNotTransferred = ReadTransferCount( pDmaInfo );
  82.    pDmaInfo->TransferCount = pDmaInfo->TransferCount - bytesNotTransferred;
  83.    // Now that TransferCount field is updated, 
  84.    // call subroutine to advance queue pointers.
  85.    if (pDmaInfo->TransferType == TRANSFER_WRITE_TO_MEM)
  86.       DmaReceiveComplete( pDmaInfo );
  87.    else if (pDmaInfo->TransferType == TRANSFER_READ_FROM_MEM)   
  88.       DmaSendComplete( pDmaInfo );
  89. }
  90.  
  91. void DmaReceiveComplete( DMA_INFO *pDmaInfo )
  92. {
  93.    // Update receive queue ptr, accounting for wrap.
  94.    pDmaInfo->RxBuf.pIn += pDmaInfo->TransferCount;
  95.    if (pDmaInfo->RxBuf.pIn > pDmaInfo->RxBuf.pEnd)
  96.       pDmaInfo->RxBuf.pIn = pDmaInfo->RxBuf.pStart;
  97.  
  98. #ifdef WIN3
  99.    Win3_ReleaseBuffer( pDmaInfo );
  100. #endif
  101.  
  102. }
  103.  
  104. void DmaSendComplete( DMA_INFO *pDmaInfo )
  105. {
  106.    // Update transmit queue ptr, accounting for wrap.
  107.    pDmaInfo->TxBuf.pOut += pDmaInfo->TransferCount;
  108.    if (pDmaInfo->TxBuf.pOut > pDmaInfo->TxBuf.pEnd)
  109.       pDmaInfo->TxBuf.pOut = pDmaInfo->TxBuf.pStart;
  110.  
  111. #ifdef WIN3
  112.    Win3_ReleaseBuffer( pDmaInfo );
  113. #endif
  114.  
  115. }
  116.  
  117. void DmaSend( DMA_INFO *pDmaInfo )
  118. {
  119.    char     *pIn, *pOut; 
  120.    USHORT   bytesInQueue;
  121.    USHORT   roomInDevice;
  122.  
  123.    // Calculate bytes in driver's transmit queue.        
  124.    pOut = pDmaInfo->TxBuf.pOut;
  125.    pIn = pDmaInfo->TxBuf.pIn;
  126.    if (pIn >= pOut)       
  127.    {
  128.       bytesInQueue = pIn - pOut;
  129.    }
  130.    else
  131.    {
  132.       // DMA transfer data must be contiguous, 
  133.       // so stop at end of buffer.
  134.       bytesInQueue = pDmaInfo->TxBuf.pEnd - pOut + 1;
  135.    }
  136.  
  137.    // Ask device how much space it has available 
  138.    // for new data. Device may not have enough space for 
  139.    // all we have.
  140.    roomInDevice = Dev_QuerySpaceAvail();
  141.    if (roomInDevice < bytesInQueue)
  142.       pDmaInfo->TransferCount = roomInDevice;
  143.    else
  144.       pDmaInfo->TransferCount = bytesInQueue;
  145.  
  146.    // Determine physical address of first byte in DMA 
  147.    // transfer. Since we're sending data _to_ device, 
  148.    // use the pOut pointer.
  149.    pDmaInfo->TransferPhys = 
  150.       GET_PHYS_ADDR( (char far *)pDmaInfo->TxBuf.pOut,
  151.                      pDmaInfo, &pDmaInfo->TxBuf );
  152.  
  153.    pDmaInfo->TransferType = TRANSFER_READ_FROM_MEM;      
  154.    ProgramDmaController( pDmaInfo );
  155. }
  156.  
  157. void DmaReceive( DMA_INFO *pDmaInfo )
  158. {
  159.    USHORT   spaceAvail;
  160.    USHORT   dataAvail;
  161.    char     *pOut, *pIn;
  162.  
  163.    // Calculate space in driver's receive queue.
  164.    pIn = pDmaInfo->RxBuf.pIn;
  165.    pOut = pDmaInfo->RxBuf.pOut;
  166.    if (pOut > pIn)       
  167.    {
  168.       spaceAvail = pOut - pIn - 1; 
  169.    }
  170.    else
  171.    {
  172.       // DMA transfer data must be contiguous, 
  173.       // so stop at end of buffer.
  174.       spaceAvail = pDmaInfo->RxBuf.pEnd - pIn + 1;
  175.       if (pOut == pDmaInfo->RxBuf.pStart)
  176.          spaceAvail--;  // must leave last byte empty
  177.    }
  178.    if (spaceAvail)
  179.    {
  180.       dataAvail = Dev_QueryDataAvail();
  181.       if (dataAvail > spaceAvail)   
  182.          pDmaInfo->TransferCount = spaceAvail;
  183.       else 
  184.          pDmaInfo->TransferCount = dataAvail;
  185.  
  186.       // Determine physical address of first byte in 
  187.       // DMA transfer. Since we're receiving data 
  188.       // _from_ device, use the pIn pointer.
  189.       pDmaInfo->TransferPhys = 
  190.             GET_PHYS_ADDR( (char far *)pDmaInfo->RxBuf.pIn,
  191.                            pDmaInfo, &pDmaInfo->RxBuf );
  192.       pDmaInfo->TransferType = TRANSFER_WRITE_TO_MEM;      
  193.       ProgramDmaController( pDmaInfo );
  194.    }
  195. }
  196.  
  197. USHORT ReadTransferCount( DMA_INFO *pDmaInfo )
  198. {
  199.    USHORT   count;
  200.    BYTE     regTransferCount;
  201.    BYTE     channel = pDmaInfo->Channel;  
  202.  
  203.    if (pDmaInfo->BusType != BUS_MICROCHANNEL)
  204.    {
  205.       if (channel <= 3)
  206.       {
  207.          regTransferCount = DMA_CONTROLLER_0_3 
  208.                            + DMA_OFFSET_COUNT;
  209.          outp( DMA_CONTROLLER_0_3 + DMA_OFFSET_CLEAR, 0 );
  210.       }
  211.       else
  212.       {
  213.          regTransferCount = DMA_CONTROLLER_4_7 
  214.                            + DMA_OFFSET_COUNT;
  215.          outp( DMA_CONTROLLER_4_7 + DMA_OFFSET_CLEAR, 0 );
  216.       }
  217.       regTransferCount += (channel << 1);
  218.       // First byte read is LSB of count.
  219.       count = inp( regTransferCount );    
  220.       // Second byte read is MSB of count. 
  221.       // Combine with LSB.
  222.       count = (count << 8) | (inp( regTransferCount ) ); 
  223.    }
  224.    else
  225.    {
  226.       outp( DMA_XFN, Get_Count + channel );
  227.       // First byte read is LSB of count.
  228.       count = inp( DMA_EXE );               
  229.       // Second byte read is MSB of count. 
  230.       // Combine with LSB.
  231.       count = (count >> 8) | inp( DMA_EXE );  
  232.    }  
  233.    return( count - 1 );
  234. }
  235.  
  236. void ProgramDmaController( DMA_INFO * pDmaInfo )
  237. {
  238.    ULONG    physAddr = pDmaInfo->TransferPhys;
  239.    USHORT   count = pDmaInfo->TransferCount;
  240.    BYTE     channel = pDmaInfo->Channel;
  241.    BYTE     baseReg;
  242.    BYTE     tempReg;   
  243.  
  244.    // Controller chips on MicroChannel are 
  245.    // different than XT or AT.
  246.    if (pDmaInfo->BusType != BUS_MICROCHANNEL)
  247.    {
  248.       if (channel <= 3)
  249.       {
  250.          baseReg = DMA_CONTROLLER_0_3;
  251.          outp( DMA_CONTROLLER_0_3 + DMA_OFFSET_CLEAR, 0 );
  252.       }
  253.       else
  254.       {
  255.          baseReg = DMA_CONTROLLER_4_7;
  256.          outp( DMA_CONTROLLER_4_7 + DMA_OFFSET_CLEAR, 0 );
  257.       }
  258.       // Set channel's mask bit to disable channel 
  259.       outp( baseReg + DMA_OFFSET_MASK, channel | 0x04 );
  260.       // Set mode to read or write.   
  261.       if (pDmaInfo->TransferType == TRANSFER_READ_FROM_MEM)
  262.       {
  263.          outp( baseReg + DMA_OFFSET_MODE, channel 
  264.                                     | DMA_type_read );
  265.       }
  266.       else
  267.       {
  268.          outp( baseReg + DMA_OFFSET_MODE, channel 
  269.                                     | DMA_type_write );
  270.       }
  271.       // Set up address register. Remember to do 
  272.       // "shift magic" if transfer will be a 16-bit 
  273.       // transfer (channels 4-7).
  274.       if (channel >= 4)
  275.          physAddr >>= 1;
  276.       tempReg = baseReg + DMA_OFFSET_ADDRESS + (channel << 1);
  277.       outp( tempReg, LOBYTE((LUSHORT( physAddr ))) );  // bits 7-0  go first
  278.       outp( tempReg, HIBYTE((LUSHORT( physAddr ))) );  // bits 15-8 go next
  279.       // Set up transfer count, which is always I/O address 
  280.       // after address register. 
  281.       tempReg++;     
  282.       count = pDmaInfo->TransferCount - 1;
  283.       if (channel >= 4)
  284.          count >>= 1;
  285.       outp( tempReg, LOBYTE( count ) );   // bits 7-0
  286.       outp( tempReg, HIBYTE( count ) );   // bits 15-8
  287.       // Set up page table register.
  288.       tempReg = PageRegister[ channel ];
  289.       outp( tempReg, LOBYTE(HIUSHORT( physAddr )) );  // bits 23-16
  290.       // Clear channel's mask bit to enable channel 
  291.       outp( baseReg + DMA_OFFSET_MASK, channel );
  292.    }
  293.    else
  294.    {
  295.       // Disable transfers on this channel.            
  296.       outp( DMA_XFN, Set_Chn_Mask + channel );
  297.       // Set channel mode to read or write.
  298.       outp( DMA_XFN, Set_Mode + channel );
  299.       if (pDmaInfo->TransferType == 
  300.                               TRANSFER_READ_FROM_MEM)
  301.       {
  302.          outp( DMA_EXE, Transfer_Data );
  303.       }
  304.       else
  305.       {
  306.          outp( DMA_EXE, Transfer_Data | Write_Mem );
  307.       }
  308.       // Set up address register
  309.       outp( DMA_XFN, channel + Set_Mem_Adr );
  310.       outp( DMA_EXE, LOBYTE((LUSHORT( physAddr ))) );  
  311.       outp( DMA_EXE, HIBYTE((LUSHORT( physAddr ))) );  
  312.       outp( DMA_EXE, LOBYTE((HUSHORT( physAddr ))) );  
  313.       // Set up transfer count.
  314.       count = pDmaInfo->TransferCount - 1;
  315.       outp( DMA_XFN, Set_Count + channel );
  316.       outp( DMA_EXE, LOBYTE( count ) ); // bits 7-0
  317.       outp( DMA_EXE, HIBYTE( count ) ); // bits 15-8
  318.       // Enable transfers on this channel.          
  319.       outp( DMA_XFN, Reset_Chn_Mask + channel );
  320.    }
  321. }
  322.  
  323. #ifdef DOS
  324. ULONG Dos_GetPhysAddr( char far *pBuffer, DMA_INFO *pDmaInfo )
  325. {
  326.    ULONG       physAddr;
  327.  
  328.    physAddr = (ULONG)FP_SEG( pBuffer );
  329.    physAddr <<= 4;
  330.    physAddr += (ULONG)FP_OFF( pBuffer );
  331.    // If transfer will cross over 64K page boundary, 
  332.    // shorten transfer by updating count. 
  333.    CheckForPageWrap( &(pDmaInfo->TransferCount), physAddr );
  334.    return( physAddr );
  335. }
  336. #endif
  337.  
  338. #ifdef WIN3
  339. ULONG Win3_GetPhysAddr( char far * pBuffer, DMA_INFO *pDmaInfo )
  340. {
  341.    DMA_DESCRIPTOR *pDmaDescriptor;
  342.    char  far      *pVdsCheckByte;
  343.    BYTE           channel = pDmaInfo->Channel;
  344.  
  345.    // Can't usually point directly to physical 
  346.    // memory in Windows, but Windows guarantees 
  347.    // selector 0x40 points to physical 0x0400.
  348.    FP_SEG( pVdsCheckByte ) = 0x40;
  349.    FP_OFF( pVdsCheckByte ) = 0x7B;
  350.    if (!(*pVdsCheckByte & 0x20))
  351.    {
  352.       // VDS is not available, do 
  353.       // real mode calculation.
  354.       Dos_GetPhysAddr( pBuffer, pDmaInfo );
  355.    }
  356.    else
  357.    {
  358.       // Disable Translation
  359.       _asm
  360.       {
  361.          mov   ah, VDS_SERVICES
  362.          mov   al, VDS_DISABLE_TRANSLATION
  363.          mov   bl, channel
  364.          int   0x4B
  365.       }   
  366.  
  367.       // Lock buffer region 
  368.       DmaDescriptor.Selector = FP_SEG( pBuffer );
  369.       DmaDescriptor.Linear = FP_OFF( pBuffer );
  370.       DmaDescriptor.Size = pDmaInfo->TransferCount;
  371.       pDmaDescriptor = &DmaDescriptor;
  372.       _asm
  373.       {
  374.          mov   ah, VDS_SERVICES
  375.          mov   al, VDS_LOCK
  376.          mov   dx, VDS_FLAGS_COPY+VDS_FLAGS_ALIGN64K+VDS_FLAGS_ALIGN128K
  377.          mov   di, pDmaDescriptor
  378.          push  es
  379.          push  ds
  380.          pop   es       
  381.          int   0x4B
  382.          pop   es
  383.       }   
  384.       
  385.       return( DmaDescriptor.Physical );   
  386.    }
  387.    
  388. }     
  389.  
  390. void Win3_ReleaseBuffer( DMA_INFO *pDmaInfo )
  391. {
  392.    BYTE     channel = pDmaInfo->Channel;
  393.    DMA_DESCRIPTOR *pDmaDescriptor;
  394.  
  395.    // Enable Translation
  396.    _asm
  397.    {
  398.       mov   ah, VDS_SERVICES
  399.       mov   al, VDS_ENABLE_TRANSLATION
  400.       mov   bl, channel
  401.       int   0x4B
  402.    }   
  403.    // Unlock buffer region 
  404.    pDmaDescriptor = &DmaDescriptor;
  405.    _asm
  406.    {
  407.       mov   ah, VDS_SERVICES
  408.       mov   al, VDS_UNLOCK
  409.       mov   dx, VDS_FLAGS_COPY
  410.       mov   di, pDmaDescriptor
  411.       push  es
  412.       push  ds
  413.       pop   es       
  414.       int   0x4B
  415.       pop   es
  416.    }   
  417. }
  418. #endif
  419.  
  420. #ifdef OS2
  421. ULONG OS2_GetPhysAddr( char *pBuffer, 
  422.                DMA_INFO *pDmaInfo, BUF_INFO *pBufStruc )
  423. {
  424.    char  *pDistanceFromStart;
  425.    ULONG physAddr;
  426.  
  427.    pDistanceFromStart = pBuffer - pBufStruc->pStart;
  428.    physAddr = pBufStruc->PhysAddr + 
  429.             (ULONG)pDistanceFromStart;
  430.    CheckForPageWrap( &(pDmaInfo->TransferCount), physAddr );
  431.    return( physAddr );   
  432. }
  433. #endif
  434.  
  435. void CheckForPageWrap( USHORT *pCount, ULONG physAddr )
  436. {
  437.    ULONG       endPhysAddr;
  438.  
  439.    endPhysAddr = physAddr + (ULONG)*pCount - 1;
  440.    if ( (endPhysAddr & 0xFFFF0000) != 
  441.          (physAddr & 0xFFFF0000) )
  442.       *pCount = 0x0000 - (USHORT)physAddr;
  443. }
  444.  
  445.