📄 dmatrans.cpp
字号:
{
// Initialize critical sections
InitializeCriticalSection( &readMutex );
InitializeCriticalSection( &writeMutex );
InitializeCriticalSection( &vchannelAccess );
// No virtual channels have been allocated
virtChannels = NULL;
// Set up physical buffer
// Allocate 2 4KB buffers, 4 4-byte IOFlags, 2 12-byte IOParam
vInputBuffer = (PUCHAR)VirtualAlloc( 0,
DMA_DEVICE_SIZE,
MEM_RESERVE,
PAGE_NOACCESS );
// Check virtualalloc succeded
if (!vInputBuffer)
{
return FALSE;
}
// Map physical address to virtual address
if (!VirtualCopy( (LPVOID) vInputBuffer,
(LPVOID)(BSP_BASE_REG_PA_DMA >> 8),
DMA_DEVICE_SIZE,
PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL))
{
return FALSE;
}
// Set up virtual addresses we've allocated
vOutputBuffer = vInputBuffer + DMA_PACKET_SIZE;
channels[0].vPFlags = (PULONG)(vOutputBuffer + DMA_PACKET_SIZE);
channels[1].vPFlags = (channels[0].vPFlags + 1);
channels[2].vPFlags = (channels[1].vPFlags + 1);
channels[3].vPFlags = (channels[2].vPFlags + 1);
vInputPB = (IOParamBlock*)(channels[3].vPFlags + 1);
vOutputPB = (IOParamBlock*)((PUCHAR)vInputPB + sizeof(IOParamBlock));
// Set physical addresses we've allocated
inputBuffer = ( PUCHAR )( BSP_BASE_REG_PA_DMA );
outputBuffer = inputBuffer + DMA_PACKET_SIZE;
channels[0].pFlags = (PULONG)(outputBuffer + DMA_PACKET_SIZE);
channels[1].pFlags = (channels[0].pFlags + 1);
channels[2].pFlags = (channels[1].pFlags + 1);
channels[3].pFlags = (channels[2].pFlags + 1);
inputPB = (IOParamBlock*)(channels[3].pFlags + 1);
outputPB = (IOParamBlock*)((PUCHAR)inputPB + sizeof(IOParamBlock));
// Create events
dmaTransportInfo.fDMAInterruptEvent = CreateEvent(0, FALSE, FALSE, NULL);
dmaTransportInfo.fKillISTEvent = CreateEvent(0, FALSE, FALSE, NULL);
// Make sure events are valid
if ( !dmaTransportInfo.fDMAInterruptEvent ||
!dmaTransportInfo.fKillISTEvent )
{
return (FALSE);
}
// Request a SYSINTR corresponding to the IRQ
if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &kDMATransport_IRQ, sizeof(DWORD), &dmaTransportInfo.fInterruptID, sizeof(DWORD), NULL))
{
RETAILMSG(1, (TEXT("DMATransport:InitializeGlobals Failed to obtain sysintr value for data interrupt.\r\n")));
dmaTransportInfo.fInterruptID = SYSINTR_UNDEFINED;
return (0);
}
// Hook the interrupt and start the associated thread.
if ( !StartIST() )
{
return FALSE;
}
return TRUE;
}
/*------------------------------------------------------------------
DMA_Init
Called for each channel as device manager enumerates through
registry entries.
------------------------------------------------------------------*/
DWORD
DMA_Init( DWORD registryPath )
{
ULONG index;
// Figure out the DMA channel we're trying to Init by reading the registry
HKEY hRegKey = OpenDeviceKey( (LPCTSTR)registryPath );
ULONG dataSize = sizeof(DWORD);
ULONG valueType = 0;
ULONG channel = 0; // 4, 5, 6, 7 representing channel wanted
ULONG global = 0;
ULONG channelIRQ = 0;
// Index.
if (ERROR_SUCCESS != RegQueryValueEx(hRegKey,
L"Index",
NULL,
&valueType,
(LPBYTE)&channel,
&dataSize))
{
RETAILMSG(TRUE, (TEXT("DMATransport:DMA_Init() ERROR: failed to read INDEX value from the registry.\r\n")));
return(0);
}
// Irq.
if (ERROR_SUCCESS != RegQueryValueEx(hRegKey,
L"Irq",
NULL,
&valueType,
(LPBYTE)&channelIRQ,
&dataSize))
{
RETAILMSG(TRUE, (TEXT("DMATransport:DMA_Init() ERROR: failed to read IRQ value from the registry.\r\n")));
return(0);
}
RegCloseKey(hRegKey);
// Initialize global driver state.
//
if ( initialized == FALSE )
{
kDMATransport_IRQ = channelIRQ;
InitializeGlobals();
for ( index = 0; index < NUM_DMA_CHANNELS; index++ )
{
WRITE_PORT_ULONG( DMA_GLOBAL_REG(index), kDMATrasportHandshake );
}
initialized = TRUE;
}
else if ( channelIRQ != kDMATransport_IRQ )
{
RETAILMSG(TRUE, (TEXT("DMATransport:DMA_Init() ERROR: mismatched IRQ values.\r\n")));
return(0);
}
// If we found a valid channel number, initialize the hardware.
if ( channel >= NUM_DMA_FIRST_CHANNEL && channel <= NUM_DMA_LAST_CHANNEL )
{
// Channels go from 4-7, but access to channel array needs indecies 0-3
index = channel - NUM_DMA_FIRST_CHANNEL;
// Read the "global" port for this channel from the DMATransportDevice.
global = READ_PORT_ULONG( DMA_GLOBAL_REG(index) );
// If the handshake succeeded,
if ( global & kDMATrasportHandshake )
{
// Set flags address.
WRITE_PORT_ULONG( DMA_FLAGS_ADDRESS_REG(index), (ULONG)channels[index].pFlags );
// Init interrupt request line.
WRITE_PORT_ULONG( DMA_IRQ_REG(index), kDMATransport_IRQ );
// Enable interrupts.
global |= kDMATransportInterruptEnabled;
WRITE_PORT_ULONG( DMA_GLOBAL_REG(index), global );
}
else
{
// Error initializing hardware. Tear down.
DEBUGMSG( TRUE, (TEXT("DMA Transport:DMA_Init() failed!\n")) );
DMA_Deinit( channel );
return 0;
}
}
return channel;
}
/*------------------------------------------------------------------
DMA_Deinit
Deinitializes indicated channel for DMA, called by device
manager when
------------------------------------------------------------------*/
BOOL
DMA_Deinit( DWORD channel )
{
// Make sure channel is closed
DMA_Close(channel);
return TRUE;
}
/*------------------------------------------------------------------
DMA_Open
Opens the desired channel for an application, returns the
channel opened, or zero for failure (if the channel is
already open.)
------------------------------------------------------------------*/
extern "C" DWORD
DMA_Open( DWORD channelNumber, // In: Channel number opening, 4 - 7
DWORD access, // In: Access, read, write, or read | write
DWORD shareMode // In: not used in this driver
)
{
if ( channelNumber >= VIRT_CHANNEL_REQUEST )
{
DWORD index = channelNumber - NUM_DMA_FIRST_CHANNEL; // Channels go from 4 - 7, but access to channel array needs indexes 0-3
DMAChannel * channel = NULL;
EnterCriticalSection( &vchannelAccess );
// Check if we need to generate the channel number
if ( channelNumber == VIRT_CHANNEL_REQUEST ||
channelNumber == ADDR_CHANNEL_REQUEST)
{
// Notify the emulator that the channel has been opened on the device
WRITE_PORT_ULONG(DMA_CURR_V_CHANNEL, 0);
if ( channelNumber == VIRT_CHANNEL_REQUEST )
WRITE_PORT_ULONG(DMA_CURR_V_OPERATION, kDMAVirtOperationNewVirtChannel);
else
WRITE_PORT_ULONG(DMA_CURR_V_OPERATION, kDMAVirtOperationNewAddrChannel);
// Read the new channel number
channelNumber = *DMA_CURR_V_STATUS;
}
// Select the right channel
if ( index < NUM_DMA_CHANNELS )
channel = &channels[index];
else if ( channelNumber >= VIRTUAL_BASE )
{
channel = getVirtualChannel(channelNumber);
if (!channel)
channel = addVirtualChannel(channelNumber);
if (!channel)
{
DEBUGMSG( TRUE, (TEXT("DMA Transport:Could not allocate new virt channel\n")) );
goto Error; // out of memory
}
}
else
{
DEBUGMSG( TRUE, (TEXT("DMA Transport:Invalid channel passed in\n")) );
goto Error; // invalid channel number passed in
}
if ( channel->open ) // Channel already been opened
{
DEBUGMSG( TRUE, (TEXT("DMA Transport:Channel already open\n")) );
goto Error;
}
if ( index < NUM_DMA_CHANNELS )
channel->dataReadyEvent = CreateEvent( NULL, FALSE, FALSE, channelEventName[index] );
else
channel->dataReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( !channel->dataReadyEvent ) // Create event failed
{
if ( channelNumber >= VIRTUAL_BASE )
deleteVirtualChannel(channel);
DEBUGMSG( TRUE, (TEXT("DMA Transport:Could not create event for channel %x\n"), channelNumber) );
goto Error;
}
// Notify the emulator that the channel has been opened on the device
WRITE_PORT_ULONG(DMA_CURR_V_CHANNEL, (channelNumber >= VIRTUAL_BASE ? channelNumber: index));
WRITE_PORT_ULONG(DMA_CURR_V_OPERATION, kDMAVirtOperationAttach);
// Check if the emulator was able to create an endpoint for this channel
if (*DMA_CURR_V_STATUS)
{
DMA_Close(channelNumber);
DEBUGMSG( TRUE, (TEXT("DMA Transport:Emulator returned failure\n")) );
goto Error;
}
channel->open = TRUE;
channel->access = access;
LeaveCriticalSection( &vchannelAccess );
// Return actual channel
return channelNumber;
Error:
DEBUGMSG( TRUE, (TEXT("DMA Transport:FAILED DMA_Open() for %x\n"), channelNumber) );
LeaveCriticalSection( &vchannelAccess );
return 0;
}
else
{
DWORD index = channelNumber - NUM_DMA_FIRST_CHANNEL; // Channels go from 4 - 7, but access to channel array needs indexes 0-3
if ( channels[index].open ) // Channel already been opened
{
return 0;
}
channels[index].dataReadyEvent = CreateEvent( NULL, FALSE, FALSE, channelEventName[index] );
if ( !channels[index].dataReadyEvent ) // Create event failed
{
return 0;
}
channels[index].open = TRUE;
channels[index].access = access;
// Return actual channel
return channelNumber;
}
}
/*------------------------------------------------------------------
DMA_Close
Closes indicated DMA channel.
------------------------------------------------------------------*/
BOOL
DMA_Close( DWORD channelNumber )
{
if ( channelNumber >= VIRTUAL_BASE )
{
DWORD index = channelNumber - NUM_DMA_FIRST_CHANNEL;
DMAChannel * channel = NULL;
EnterCriticalSection( &vchannelAccess );
// Select the right channel
if ( index < NUM_DMA_CHANNELS )
channel = &channels[index];
else if ( channelNumber >= VIRTUAL_BASE )
{
channel = getVirtualChannel(channelNumber);
if (!channel)
{
LeaveCriticalSection( &vchannelAccess );
return FALSE; // invalid channel number passed in
}
}
else
{
LeaveCriticalSection( &vchannelAccess );
return FALSE; // invalid channel number passed in
}
// Make sure tha channel is open
if ( channel->open )
{
channel->open = FALSE;
if ( channel->dataReadyEvent != NULL )
CloseHandle( channel->dataReadyEvent );
}
// Remove the node from the linked list for virtual channels
if ( channelNumber >= VIRTUAL_BASE )
deleteVirtualChannel(channel);
// Notify the emulator that the channel has been opened on the device
WRITE_PORT_ULONG(DMA_CURR_V_CHANNEL, (channelNumber >= VIRTUAL_BASE ? channelNumber: index));
WRITE_PORT_ULONG(DMA_CURR_V_OPERATION, kDMAVirtOperationDetach);
// Check if the emulator was able to create an endpoint for this channel
if (*DMA_CURR_V_STATUS)
{
DEBUGMSG( TRUE, (TEXT("DMA Transport:Emulator returned failure closing channel %x\n"), channelNumber) );
}
LeaveCriticalSection( &vchannelAccess );
return TRUE;
}
else
{
DWORD index = channelNumber - NUM_DMA_FIRST_CHANNEL;
// Make sure tha channel is open
if ( channels[index].open )
{
channels[index].open = FALSE;
CloseHandle( channels[index].dataReadyEvent );
}
return TRUE;
}
}
/*------------------------------------------------------------------
DMA_Write
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -