📄 dmatrans.cpp
字号:
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;
}
DEBUGMSG( TRUE, (TEXT("DMA Transport:Leaving InitializeGlobals\n")) );
return TRUE;
}
/*------------------------------------------------------------------
DMA_Init
Called for each channel as device manager enumerates through
registry entries.
------------------------------------------------------------------*/
DWORD
DMA_Init( DWORD registryPath )
{
DEBUGMSG( TRUE, (TEXT("DMA Transport:Enter DMA_Init\n")) );
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);
}
DEBUGMSG( TRUE, (TEXT("DMATransport:DMA_Init() initializing channel %d.\n"), channel) );
// 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;
}
}
DEBUGMSG( TRUE, (TEXT("DMA Transport:Leaving DMA_Init\n")) );
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 )
goto NewOpenCode;
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;
}
NewOpenCode:
DEBUGMSG( TRUE, (TEXT("DMA Transport:Enter DMA_Open() for %x\n"), channelNumber) );
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)
{
DEBUGMSG( TRUE, (TEXT("DMA Transport:Generating channel number\n")) );
// 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;
DEBUGMSG( TRUE, (TEXT("DMA Transport:Selected channel %x\n"), channelNumber) );
}
// 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
DEBUGMSG( TRUE, (TEXT("DMA Transport:SUCCESS DMA_Open() for %x\n"), channelNumber) );
return channelNumber;
Error:
DEBUGMSG( TRUE, (TEXT("DMA Transport:FAILED DMA_Open() for %x\n"), channelNumber) );
LeaveCriticalSection( &vchannelAccess );
return 0;
}
/*------------------------------------------------------------------
DMA_Close
Closes indicated DMA channel.
------------------------------------------------------------------*/
BOOL
DMA_Close( DWORD channelNumber )
{
if ( channelNumber >= VIRTUAL_BASE )
goto NewCloseCode;
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;
}
NewCloseCode:
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) );
}
DEBUGMSG( TRUE, (TEXT("DMA Transport:SUCCESS DMA_Close() for %x\n"), channelNumber) );
LeaveCriticalSection( &vchannelAccess );
return TRUE;
}
/*------------------------------------------------------------------
DMA_Write
Writes indicated number of bytes to DMA device, up to a
maximum of 4K. (The DMATransport device transmits data in 4K
chunks. We leave it up to the client of this driver to handle
breaking up packets.)
Returns failure, or the number of bytes actually written.
------------------------------------------------------------------*/
DWORD
DMA_Write( DWORD channel, // In: channel number: 4-7
LPCVOID writeBytes, // In: Data to write
DWORD numBytes // In: number of bytes to write
)
{
if ( channel >= VIRTUAL_BASE )
goto NewWriteCode;
else
{
// Enter critical section so no other write call can interfere
EnterCriticalSection( &writeMutex );
vOutputPB->ioStatus = 0;
vOutputPB->ioDataPtr = (ULONG)outputBuffer; // specify physical address
if ( numBytes > DMA_PACKET_SIZE ) // Trying to write too many bytes, can only write at the max DMA_PACKET_SIZE
{
vOutputPB->ioDataLength = DMA_PACKET_SIZE;
}
else // Writing numBytes
{
vOutputPB->ioDataLength = numBytes;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -