This chapter covers advanced driver development issues and contains guidelines for using WinDriver to perform tasks that cannot be fully automated by the DriverWizard.
Note that WinDriver's enhanced support for specific chipsets
This section describes how to use WinDriver to implement bus-master Direct Memory Access (DMA) for devices capable of acting as bus masters. Such devices have a DMA controller, which the driver should program directly.
DMA is a capability provided by some computer bus architectures, including PCI, PCMCIA and CardBus, which allows data to be sent directly from an attached device to the memory on the host, freeing the CPU from involvement with the data transfer and thus improving the host's performance.
A DMA buffer can be allocated in two ways:
The programming of a device's DMA controller is hardware specific. Normally, you need to program your device with the local address (on your device), the host address (the physical memory address on your PC) and the transfer count (the size of the memory block to transfer), and then set the register that initiates the transfer.
WinDriver provides you with API for implementing both Contiguous Buffer DMA and
Scatter/Gather DMA (if supported by the hardware) — see the description of
WDC_DMAContigBufLock()WDC_DMASGBufLock()WDC_DMABufUnlock()WD_DMAxxx API is
described in the WinDriver PCI Low-Level API Reference, but we recommend using the convenient
wrapper WDC_xxx API instead.)
This section includes code samples that demonstrate how to use WinDriver to implement Scatter/Gather and Contiguous Buffer DMA.
![]() | |
|
Following is a sample routine that uses WinDriver's WDC API
A more detailed example, which is specific to the enhanced support for PLX
chipsets
BOOL DMARoutine(WDC_DEVICE_HANDLE hDev, DWORD dwBufSize,
{
PVOID pBuf;
WD_DMA *pDma = NULL;
BOOL fRet = FALSE;
/* Allocate a user-mode buffer for Scatter/Gather DMA */
pBuf = malloc(dwBufSize);
if (!pBuf)
return FALSE;
/* Lock the DMA buffer and program the DMA controller */
if (!DMAOpen(hDev, pBuf, u32LocalAddr, dwBufSize, fToDev, &pDma))
goto Exit;
/* Enable DMA interrupts (if not polling) */
if (!fPolling)
{
if (!MyDMAInterruptEnable(hDev, MyDmaIntHandler, pDma))
goto Exit; /* Failed enabling DMA interrupts */
}
/* Flush the CPU caches (see documentation of WDC_DMASyncCpu()) */
WDC_DMASyncCpu(pDma);
/* Start DMA - write to the device to initiate the DMA transfer */
MyDMAStart(hDev, pDma);
/* Wait for the DMA transfer to complete */
MyDMAWaitForCompletion(hDev, pDma, fPolling);
/* Flush the I/O caches (see documentation of WDC_DMASyncIo()) */
WDC_DMASyncIo(pDma);
fRet = TRUE;
Exit:
DMAClose(pDma, fPolling);
free(pBuf);
return fRet;
}
/* DMAOpen: Locks a Scatter/Gather DMA buffer */
BOOL DMAOpen(WDC_DEVICE_HANDLE hDev, PVOID pBuf, UINT32 u32LocalAddr,
DWORD dwDMABufSize, BOOL fToDev, WD_DMA **ppDma)
{
DWORD dwStatus, i;
DWORD dwOptions = fToDev ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
/* Lock a Scatter/Gather DMA buffer */
dwStatus = WDC_DMASGBufLock(hDev, pBuf, dwOptions, dwDMABufSize, ppDma);
if (WD_STATUS_SUCCESS != dwStatus)
{
printf("Failed locking a Scatter/Gather DMA buffer. Error 0x%lx - %s\n",
dwStatus, Stat2Str(dwStatus));
return FALSE;
}
/* Program the device's DMA registers for each physical page */
MyDMAProgram((*ppDma)->Page, (*ppDma)->dwPages, fToDev);
return TRUE;
}
/* DMAClose: Unlocks a previously locked Scatter/Gather DMA buffer */
void DMAClose(WD_DMA *pDma, BOOL fPolling)
{
/* Disable DMA interrupts (if not polling) */
if (!fPolling)
MyDMAInterruptDisable(hDev);
/* Unlock and free the DMA buffer */
WDC_DMABufUnlock(pDma);
}
In the code sample above, it is up to you to implement the following
MyDMAxxx() routines, according to your device's specification
MyDMAProgram(): Program the device's DMA registers.MyDMAStart(): Write to the device to initiate DMA transfers.
MyDMAInterruptEnable() and
MyDMAInterruptDisable(): Use WDC_IntEnable()WDC_IntDisable()MyDMAWaitForCompletion(): Poll the device for completion or
wait for "DMA DONE" interrupt.
![]() | |
When using the basic WD_xxx API (described in the
WinDriver PCI Low-Level API Reference) to allocate a Scatter/Gather DMA buffer that is larger than 1MB, you
need to set the DMA_LARGE_BUFFER flag in the call to
WD_DMALock() and allocate memory for the additional memory pages,
as explained in the following FAQ: http://www.jungo.com/st/support/faq.html#dma1. However, when using
WDC_DMASGBufLock() |
Following is a sample routine that uses WinDriver's WDC API
For more detailed, hardware-specific, contiguous DMA examples, refer to the
following enhanced-support chipset
BOOL DMARoutine(WDC_DEVICE_HANDLE hDev, DWORD dwDMABufSize,
UINT32 u32LocalAddr, DWORD dwOptions, BOOL fPolling, BOOL fToDev)
{
PVOID pBuf = NULL;
WD_DMA *pDma = NULL;
BOOL fRet = FALSE;
/* Allocate a DMA buffer and open DMA for the selected channel */
if (!DMAOpen(hDev, &pBuf, u32LocalAddr, dwDMABufSize, fToDev, &pDma))
goto Exit;
/* Enable DMA interrupts (if not polling) */
if (!fPolling)
{
if (!MyDMAInterruptEnable(hDev, MyDmaIntHandler, pDma))
goto Exit; /* Failed enabling DMA interrupts */
}
/* Flush the CPU caches (see documentation of WDC_DMASyncCpu()) */
WDC_DMASyncCpu(pDma);
/* Start DMA - write to the device to initiate the DMA transfer */
MyDMAStart(hDev, pDma);
/* Wait for the DMA transfer to complete */
MyDMAWaitForCompletion(hDev, pDma, fPolling);
/* Flush the I/O caches (see documentation of WDC_DMASyncIo()) */
WDC_DMASyncIo(pDma);
fRet = TRUE;
Exit:
DMAClose(pDma, fPolling);
return fRet;
}
/* DMAOpen: Allocates and locks a Contiguous DMA buffer */
BOOL DMAOpen(WDC_DEVICE_HANDLE hDev, PVOID *ppBuf, UINT32 u32LocalAddr,
DWORD dwDMABufSize, BOOL fToDev, WD_DMA **ppDma)
{
DWORD dwStatus;
DWORD dwOptions = fToDev ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
/* Allocate and lock a Contiguous DMA buffer */
dwStatus = WDC_DMAContigBufLock(hDev, ppBuf, dwOptions, dwDMABufSize, ppDma);
if (WD_STATUS_SUCCESS != dwStatus)
{
printf("Failed locking a Contiguous DMA buffer. Error 0x%lx - %s\n",
dwStatus, Stat2Str(dwStatus));
return FALSE;
}
/* Program the device's DMA registers for the physical DMA page */
MyDMAProgram((*ppDma)->Page, (*ppDma)->dwPages, fToDev);
return TRUE;
}
/* DMAClose: Frees a previously allocated Contiguous DMA buffer */
void DMAClose(WD_DMA *pDma, BOOL fPolling)
{
/* Disable DMA interrupts (if not polling) */
if (!fPolling)
MyDMAInterruptDisable(hDev);
/* Unlock and free the DMA buffer */
WDC_DMABufUnlock(pDma);
}MyDMAxxx() routines, according to your device's specification
MyDMAProgram(): Program the device's DMA registers.MyDMAStart(): Write to the device to initiate DMA transfers.
MyDMAInterruptEnable() and
MyDMAInterruptDisable(): Use WDC_IntEnable()WDC_IntDisable()MyDMAWaitForCompletion(): Poll the device for completion or
wait for "DMA DONE" interrupt.
The SPARC platform supports Direct Virtual Memory Access (DVMA). Platforms that support DVMA provide the device with a virtual address rather than a physical address. With this memory access method, the platform translates device accesses to the provided virtual address into the proper physical addresses using a type of Memory Management Unit (MMU). The device transfers data to and from a contiguous virtual image that can be mapped to dis-contiguous physical pages. Devices that operate on these platforms do not require Scatter/Gather DMA capability.