📄 dma.cpp
字号:
// Calculate the number of pages in the buffer(s)
dwPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(NULL, BYTE_OFFSET( pdwSourceBuffer[0] ) + dwTotalSize); //!!!
DEBUGMSG(1, (TEXT("DMA %d - Number of virtual pages required = %d\r\n"), ucDMAChannel, dwPages));
// Check if the transfer is too big for our list buffer
if(dwPages > (DWORD)MAX_DMA_LIST_ENTRIES) {
DEBUGMSG(1, (TEXT("Error - the DMA %d transfer is too large for the list buffer\r\n"), ucDMAChannel));
return FALSE;
}
// Setup the list entry pointer to the start of the list for this channel
dwListOffset = ucDMAChannel * sizeof(LLI_ENTRY) * MAX_DMA_LIST_ENTRIES;
DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntry = (PLLI_ENTRY)(DMA_LIST_BUFFER_VIRT + dwListOffset);
// Keep a physical copy
DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntryPhys = (PLLI_ENTRY)(DMA_LIST_BUFFER_PHYS + dwListOffset);
dwTransferSize = (DWORD)PAGE_SIZE - (DWORD)BYTE_OFFSET(pdwSourceBuffer[0]); //!!!
// Adjust the size to the number of transfers required
switch(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth) {
case TRANSFER_WIDTH_BYTE:
dwTransfers = dwTransferSize - 1;
// Add a 1 byte list entry to make up 1 virtual page
// This is due to the maximum transfer size of the DMAC (4095 bytes)
dwPage = 0;
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, (DWORD)-1);
break;
case TRANSFER_WIDTH_WORD:
dwTransfers = dwTransferSize/sizeof(WORD);
break;
case TRANSFER_WIDTH_DWORD:
dwTransfers = dwTransferSize/sizeof(DWORD);
break;
} /* switch(DMAInfo.DMAChannels[] */
pDMACChannelRegs->DMACCxControl |= dwTransfers; // Set the number of transfers
dwTotalSize -= (DWORD)dwTransferSize; // !!! The first block has been taken care of
// Point the DMA controller at the start of the list buffer
// We OR in AHB2 as the master select for loading LLIs (Versatile)
pDMACChannelRegs->DMACCxLLI = (DWORD)((DMA_LIST_BUFFER_PHYS + dwListOffset) | DMAC_LLI_LM);
// Create the linked list
for(dwPage=1;dwPage<dwPages;dwPage++) {
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA %d list entry - virtual list entry pointer = 0x%X\r\n"),
ucDMAChannel, DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntry));
DEBUGMSG(1, (TEXT("DMA %d list entry - physical list entry pointer = 0x%X\r\n"),
ucDMAChannel, DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntryPhys));
// Calculate the size of the transfer
dwTransferSize = MIN(dwTotalSize, (DWORD)PAGE_SIZE);
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA %d list entry - transfer size = 0x%X\r\n"), ucDMAChannel, dwTransferSize));
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, dwTransferSize);
// If we are transfering a page size of bytes we need an extra 1 byte transfer
if(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth == TRANSFER_WIDTH_BYTE &&
dwTransferSize == (DWORD)PAGE_SIZE) {
// Add a 1 byte list entry to make up 1 virtual page
// Again due to the maximum transfer size of the DMAC (4095 bytes)
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, (DWORD)-1);
}
dwTotalSize -= dwTransferSize; // Reduce the overall transfer size accordingly
DEBUGMSG(1, (TEXT("DMA %d list entry - bytes left = 0x%X\r\n"), ucDMAChannel, dwTotalSize));
}
DMAInfo.DMAChannels[ucDMAChannel].pPrevLLIEntry->pNextEntry = 0; // This is the end of the list
DMAInfo.DMAChannels[ucDMAChannel].pPrevLLIEntry->dwControl |= DMAC_CTRL_I; // Set to interrupt on this last entry
DEBUGMSG(1, (TEXT("DMA %d set to intr on the last entry\r\n"), ucDMAChannel));
} // if(dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE)
else
{
// Adjust the size to the number of transfers required
switch(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth) {
case TRANSFER_WIDTH_BYTE:
dwTransfers = dwTotalSize;
break;
case TRANSFER_WIDTH_WORD:
dwTransfers = dwTotalSize/sizeof(WORD);
break;
case TRANSFER_WIDTH_DWORD:
dwTransfers = dwTotalSize/sizeof(DWORD);
break;
}
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfers = 0x%X\r\n"), dwTransfers));
pDMACChannelRegs->DMACCxLLI = 0; // There is no linked list for small transfers
pDMACChannelRegs->DMACCxControl |= DMAC_CTRL_I; // Interrupt at the end of the transfer
pDMACChannelRegs->DMACCxControl |= dwTransfers; // Set the number of transfers
} // if(dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE)
break;
case (DWORD)DMAC_CTRL_D:
////////////////////////
DEBUGMSG(1, (TEXT("DMA %d - BYTE_OFFSET( pdwDestBuffer[0] ) = 0x%x\r\n"), ucDMAChannel, BYTE_OFFSET( pdwDestBuffer[0] )));
DEBUGMSG(1, (TEXT("DMA %d - dwTotalSize = 0x%x\r\n"), ucDMAChannel, dwTotalSize));
////////////////////////
// Clip the transfer size to one DMA block and create a linked list for the rest
if(BYTE_OFFSET( pdwDestBuffer[0] ) + dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE) //!!!
{
// Calculate the number of pages in the buffer(s)
dwPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(NULL, BYTE_OFFSET( pdwDestBuffer[0] ) + dwTotalSize); //!!!
DEBUGMSG(1, (TEXT("DMA %d - Number of virtual pages required = %d\r\n"), ucDMAChannel, dwPages));
// Check if the transfer is too big for our list buffer
if(dwPages > (DWORD)MAX_DMA_LIST_ENTRIES) {
DEBUGMSG(1, (TEXT("Error - the DMA %d transfer is too large for the list buffer\r\n"), ucDMAChannel));
return FALSE;
}
// Setup the list entry pointer to the start of the list for this channel
dwListOffset = ucDMAChannel * sizeof(LLI_ENTRY) * MAX_DMA_LIST_ENTRIES;
DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntry = (PLLI_ENTRY)(DMA_LIST_BUFFER_VIRT + dwListOffset);
// Keep a physical copy
DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntryPhys = (PLLI_ENTRY)(DMA_LIST_BUFFER_PHYS + dwListOffset);
dwTransferSize = (DWORD)PAGE_SIZE - (DWORD)BYTE_OFFSET(pdwDestBuffer[0]); //!!!
// Adjust the size to the number of transfers required
switch(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth) {
case TRANSFER_WIDTH_BYTE:
dwTransfers = dwTransferSize - 1;
// Add a 1 byte list entry to make up 1 virtual page
// This is due to the maximum transfer size of the DMAC (4095 bytes)
dwPage = 0;
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, (DWORD)-1);
break;
case TRANSFER_WIDTH_WORD:
dwTransfers = dwTransferSize/sizeof(WORD);
break;
case TRANSFER_WIDTH_DWORD:
dwTransfers = dwTransferSize/sizeof(DWORD);
break;
} /* switch(DMAInfo.DMAChannels[] */
pDMACChannelRegs->DMACCxControl |= dwTransfers; // Set the number of transfers
dwTotalSize -= (DWORD)dwTransferSize; // !!!The first block has been taken care of
// Point the DMA controller at the start of the list buffer
// We OR in AHB2 as the master select for loading LLIs (Versatile)
pDMACChannelRegs->DMACCxLLI = (DWORD)((DMA_LIST_BUFFER_PHYS + dwListOffset) | DMAC_LLI_LM);
// Create the linked list
for(dwPage=1;dwPage<dwPages;dwPage++) {
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA %d list entry - virtual list entry pointer = 0x%X\r\n"),
ucDMAChannel, DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntry));
DEBUGMSG(1, (TEXT("DMA %d list entry - physical list entry pointer = 0x%X\r\n"),
ucDMAChannel, DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntryPhys));
// Calculate the size of the transfer
dwTransferSize = MIN(dwTotalSize, (DWORD)PAGE_SIZE);
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA %d list entry - transfer size = 0x%X\r\n"), ucDMAChannel, dwTransferSize));
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, dwTransferSize);
// If we are transfering a page size of bytes we need an extra 1 byte transfer
if(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth == TRANSFER_WIDTH_BYTE &&
dwTransferSize == (DWORD)PAGE_SIZE) {
// Add a 1 byte list entry to make up 1 virtual page
// Again due to the maximum transfer size of the DMAC (4095 bytes)
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, (DWORD)-1);
}
dwTotalSize -= dwTransferSize; // Reduce the overall transfer size accordingly
DEBUGMSG(1, (TEXT("DMA %d list entry - bytes left = 0x%X\r\n"), ucDMAChannel, dwTotalSize));
}
DMAInfo.DMAChannels[ucDMAChannel].pPrevLLIEntry->pNextEntry = 0; // This is the end of the list
DMAInfo.DMAChannels[ucDMAChannel].pPrevLLIEntry->dwControl |= DMAC_CTRL_I; // Set to interrupt on this last entry
DEBUGMSG(1, (TEXT("DMA %d set to intr on the last entry\r\n"), ucDMAChannel));
} // if(dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE)
else
{
// Adjust the size to the number of transfers required
switch(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth) {
case TRANSFER_WIDTH_BYTE:
dwTransfers = dwTotalSize;
break;
case TRANSFER_WIDTH_WORD:
dwTransfers = dwTotalSize/sizeof(WORD);
break;
case TRANSFER_WIDTH_DWORD:
dwTransfers = dwTotalSize/sizeof(DWORD);
break;
}
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfers = 0x%X\r\n"), dwTransfers));
pDMACChannelRegs->DMACCxLLI = 0; // There is no linked list for small transfers
pDMACChannelRegs->DMACCxControl |= DMAC_CTRL_I; // Interrupt at the end of the transfer
pDMACChannelRegs->DMACCxControl |= dwTransfers; // Set the number of transfers
} // if(dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE)
break;
default: ;
}
// Flush the data cache as the DMA controller needs the LLI information in
// DMA accessible memory
OEMCacheRangeFlush(NULL, 0, CACHE_SYNC_DISCARD);
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: Source = 0x%X\r\n"), pDMACChannelRegs->DMACCxSrcAddr));
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: Dest = 0x%X\r\n"), pDMACChannelRegs->DMACCxDestAddr));
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: LLI = 0x%X\r\n"), pDMACChannelRegs->DMACCxLLI));
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: Control = 0x%X\r\n"), pDMACChannelRegs->DMACCxControl));
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: Config = 0x%X\r\n"), pDMACChannelRegs->DMACCxConfiguration));
// Enable the channel, this should start the transfer or allow a peripheral to do so
pDMACChannelRegs->DMACCxConfiguration |= DMAC_CHCONFIG_E;
DEBUGMSG(1, (TEXT("-StartDMATransfer\r\n")));
return TRUE;
}
//
// HaltDMATransfer - Halts a DMA channel
//
// Note: Care should be taken over the state of the Cache at this point
// It is up to the user to make sure the correct data will be accessed
//
extern "C" BOOL HaltDMATransfer(PHALT_DMA_PARAMS pParams, PHALT_DMA_RESULT pResults)
{
UCHAR ucDMAChannel;
volatile PDMAC_CHANNEL_REGS pDMACChannelRegs;
DEBUGMSG(DMA_ZONE_OPT, (TEXT("+HaltDMATransfer(channel %d)\r\n"), pParams->ucChannelNumber));
ucDMAChannel = pParams->ucChannelNumber;
pDMACChannelRegs = DMAInfo.DMAChannels[ucDMAChannel].pDMACChannelRegs;
// Halt any transfer (don't disable the channel as this will lose data!)
pDMACChannelRegs->DMACCxConfiguration |= (DWORD)DMAC_CHCONFIG_H;
// Wait for channel to become inactive
while(pDMACChannelRegs->DMACCxConfiguration&(DWORD)DMAC_CHCONFIG_A);
DEBUGMSG(DMA_ZONE_OPT, (TEXT("-HaltDMATransfer\r\n")));
return TRUE;
}
//
// RestartDMATransfer - Restarts a halted DMA channel
//
// Note: Care should be taken over the state of the Cache at this point.
// It is up to the user to make sure the correct data will be accessed.
//
extern "C" BOOL RestartDMATransfer(PRESTART_DMA_PARAMS pParams, PRESTART_DMA_RESULT pResults)
{
UCHAR ucDMAChannel;
volatile PDMAC_CHANNEL_REGS pDMACChannelRegs;
if (!fDMAInitialised) { /* not present */
DEBUGMSG(DMA_ZONE_OPT, (TEXT("No DMA blocking RestartDMATransfer(channel %d)\r\n"), pParams->ucChannelNumber));
return FALSE;
}
DEBUGMSG(DMA_ZONE_OPT, (TEXT("+RestartDMATransfer(channel %d)\r\n"), pParams->ucChannelNumber));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -