📄 dmatrans.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*==================================================================
File: DMATrans.cpp
Contains: Stream-interface CE driver for the Emulator's
DMATransportDevice. This driver provides a stream
interface for applications running in the emulator
to communicate with applications running on the
host machine (outside the emulator).
Communication on the other side of the device
is done via the Emulator's IVirtualTransport COM
interface.
Written by: Paul Pearcy
Copyright: 2001 Connectix Corporation
==================================================================*/
//#include "stdafx.h"
#include <windows.h> // For later builds of Talisker, remove the "stdafx.h" include and add this line
#include <bsp.h>
#include <devload.h> //for OpenDeviceKey
#include "dmatrans.h"
#include <ceddk.h> // for READ_PORT_XXXX, WRITE_PORT_XXXX
/*------------------------------------------------------------------
Defines and const
------------------------------------------------------------------*/
// These two configuration options are quite important. The
// Emulator's DMATransportDevice is the device this driver
// communicates with. The device can use either IRQ 14 or 15.
// The Emulator KITL is normally configured to use IRQ 14, so
// this driver must use IRQ 15 to avoid a conflict with the KITL.
// The Base address is the physical memory address (in the
// emulated machine) that we'll use to DMA packets to and from.
// The address here must match the address in the platform's
// Config.bib file (which is where we reserve physical
// memory for the DMATransportDevice.)
// IRQ
ULONG kDMATransport_IRQ;
// Global
const ULONG kDMATrasportHandshake = 1 << 0;
const ULONG kDMATransportInterruptEnabled = 1 << 8;
// flags
const ULONG kDMATransportDataReady = 1 << 0;
// channel base address on the bus
#define kDMATransportBase (vInputBuffer+0x2080)
// Channel size
const ULONG kDMATransportChannelSize = 0x20;
// DMATransportDevice register offsets.
enum
{
kDMATransportGlobalRegister = 0x00,
kDMATransportFlagsRegister = 0x04,
kDMATransportIOInputRegister = 0x08,
kDMATransportIOOutputRegister = 0x0C,
kDMATransportIRQRegister = 0x10,
kDMATransportIRQAcknowledgeRegister = 0x14,
};
// Regisgter macros, given the channel returns specific register
#define DMA_GLOBAL_REG(a) ((ULONG*)(kDMATransportBase+(a*kDMATransportChannelSize)+kDMATransportGlobalRegister))
#define DMA_FLAGS_ADDRESS_REG(a) ((ULONG*)(kDMATransportBase+(a*kDMATransportChannelSize)+kDMATransportFlagsRegister))
#define DMA_IO_INPUT_REG(a) ((ULONG*)(kDMATransportBase+(a*kDMATransportChannelSize)+kDMATransportIOInputRegister))
#define DMA_IO_OUTPUT_REG(a) ((ULONG*)(kDMATransportBase+(a*kDMATransportChannelSize)+kDMATransportIOOutputRegister))
#define DMA_IRQ_REG(a) ((ULONG*)(kDMATransportBase+(a*kDMATransportChannelSize)+kDMATransportIRQRegister))
#define DMA_IRQ_ACK_REG(a) ((ULONG*)(kDMATransportBase+(a*kDMATransportChannelSize)+kDMATransportIRQAcknowledgeRegister))
// Virtual channel operation register offsets.
enum
{
kDMAVirtualChannel = 0x00,
kDMAVirtualOperation = 0x04,
kDMAVirtualStatus = 0x08,
kDMAFlagsChannel = 0x0C,
kDMAReadChannel = 0x10,
kDMAWriteChannel = 0x14,
};
#define DMA_CURR_V_CHANNEL ((ULONG*)(kDMATransportBase+(NUM_DMA_CHANNELS*kDMATransportChannelSize)+kDMAVirtualChannel))
#define DMA_CURR_V_OPERATION ((ULONG*)(kDMATransportBase+(NUM_DMA_CHANNELS*kDMATransportChannelSize)+kDMAVirtualOperation))
#define DMA_CURR_V_STATUS ((ULONG*)(kDMATransportBase+(NUM_DMA_CHANNELS*kDMATransportChannelSize)+kDMAVirtualStatus))
#define DMA_V_CHANNEL_FLAGS ((ULONG*)(kDMATransportBase+(NUM_DMA_CHANNELS*kDMATransportChannelSize)+kDMAFlagsChannel))
#define DMA_V_CHANNEL_READ ((ULONG*)(kDMATransportBase+(NUM_DMA_CHANNELS*kDMATransportChannelSize)+kDMAReadChannel))
#define DMA_V_CHANNEL_WRITE ((ULONG*)(kDMATransportBase+(NUM_DMA_CHANNELS*kDMATransportChannelSize)+kDMAWriteChannel))
enum
{
kDMAVirtOperationAttach = 0x01,
kDMAVirtOperationDetach = 0x02,
kDMAVirtOpetationIsInUse = 0x04,
kDMAVirtOperationNewVirtChannel = 0x08,
kDMAVirtOperationNewAddrChannel = 0x10,
};
#define VIRTUAL_BASE 0x80000000
// Names of events which allow us to signal calling app that data is available
// These match up with the names in the header file included by applications using the transport
const LPTSTR channelEventName[] = { {L"channel4"},
{L"channel5"},
{L"channel6"},
{L"channel7"} };
// Number of channels
#define NUM_DMA_CHANNELS 4
#define NUM_DMA_FIRST_CHANNEL 4
#define NUM_DMA_LAST_CHANNEL 7
// Packet size: 4KB
#define DMA_PACKET_SIZE 0x1000
// Virtual channels
#define VIRT_CHANNEL_REQUEST 8
#define ADDR_CHANNEL_REQUEST 9
// Size of the memory window for accessing the DMA Transport peripheral device.
// The x86 dmatrans.cpp uses "(DMA_PACKET_SIZE * 2) + (NUM_DMA_CHANNELS * sizeof(ULONG)) + (2 * sizeof(IOParamBlock)+0x20)"
// But that size is smaller than what is actually used. The value, below, is based on examination of
// the dmatrans.cpp sources.
#define DMA_DEVICE_SIZE 0x2120
/*------------------------------------------------------------------
Types
------------------------------------------------------------------*/
// Parameter block structure used to communicate with Emulated hardware.
struct IOParamBlock
{
volatile ULONG ioDataLength; //NOTE: non-cached, not an issue on an emulated system.
volatile ULONG ioDataPtr;
volatile ULONG ioStatus;
};
// Holds data for the transport
struct DMATransportInfo
{
DMATransportInfo() // Constructor
{
fDMAInterruptEvent = 0; // Event signaled when DMA interrupt occurs
fInterruptID = 0; // IRQ of the interrupt
fIntrServiceThread = NULL; // Handle to the interrupt servicing thread (IST)
fKillISTEvent = NULL; // Event signaled when IST should be ended.
fKillIST = false; // Set to true when we should signal the killISTEvent.
}
HANDLE fDMAInterruptEvent; // fDMAInterruptEvent signaled when interrupt occurs
HANDLE fIntrServiceThread; // Handle to InterruptServiceThread
HANDLE fKillISTEvent; // Signalled when we want to kill the IST
BOOL fKillIST; // Set to true when we want to stop the IST
DWORD fInterruptID; // Logical interrupt ID
};
// Holds info for DMA channel, an instance per channel
struct DMAChannel
{
DMAChannel()
{
open = FALSE;
vPFlags = NULL;
pFlags = NULL;
access = 0;
dataReadyEvent = NULL;
next = NULL;
channelNumber = 0;
}
BOOL open; // True if channel is open
PULONG vPFlags; // Virtual pointer to flags
PULONG pFlags; // Physical pointer to flags
ULONG access; // Read/Write access
HANDLE dataReadyEvent; // Event is created in calling application, app passes event name to DeviceIOControl, and driver gets a handle to it
// Fired when data is ready to be read
ULONG channelNumber;
DMAChannel * next;
};
/*------------------------------------------------------------------
Globals
------------------------------------------------------------------*/
// Critical sections
// Locks are required because different processes in CE can call read or write simultaneously
CRITICAL_SECTION writeMutex;
CRITICAL_SECTION readMutex;
CRITICAL_SECTION vchannelAccess;
// Global parameter block variables
// physical
IOParamBlock* inputPB;
IOParamBlock* outputPB;
// virtual
IOParamBlock* vInputPB;
IOParamBlock* vOutputPB;
// Global buffers
PUCHAR vInputBuffer;
PUCHAR vOutputBuffer;
PUCHAR inputBuffer;
PUCHAR outputBuffer;
// Global structure to access transport info
DMATransportInfo dmaTransportInfo;
DMAChannel channels[NUM_DMA_CHANNELS];
DMAChannel * virtChannels;
BOOL initialized = FALSE;
/*------------------------------------------------------------------
Local Prototypes
------------------------------------------------------------------*/
static DWORD WINAPI DMAInterruptServiceThread( PVOID Empty );
static BOOL StartIST();
static BOOL InitializeGlobals();
DMAChannel * getVirtualChannel(DWORD channelNumber);
DMAChannel * addVirtualChannel(DWORD channelNumber);
void deleteVirtualChannel(DMAChannel * channel);
/*------------------------------------------------------------------
DllMain
Entry point called when a process hooks up to the DLL.
------------------------------------------------------------------*/
BOOL APIENTRY DllMain(HANDLE hinstDLL, DWORD fdwReason, LPVOID lpv)
{
switch ( fdwReason )
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
/*------------------------------------------------------------------
DMAInterruptServiceThread
When interrupt occurs on IRQ 15, this thread is triggered and
in turn sets the dataReadyEvent for the appropriate channels
------------------------------------------------------------------*/
static DWORD WINAPI
DMAInterruptServiceThread( PVOID /*ignore*/ ) // NULL pointer does nothing.
{
while ( TRUE ) // loop forever
{
// Wait for DMA event that is triggered by a the host sending data to the guest, so the guest reads
if ( WaitForSingleObject( dmaTransportInfo.fDMAInterruptEvent, INFINITE ) == WAIT_FAILED ) // Something bad happened
{
continue;
}
// Acknowledge the interrupt. Note we write to index 0, which is actually
// Channel 4 in our case. We're sharing one interrupt for all four channels,
// so we only need to ack on one of the channels to pull the IRQ line low.
// It doesn't matter what we write, the data is ignored.
WRITE_PORT_ULONG( DMA_IRQ_ACK_REG(0), 0 );
if ( dmaTransportInfo.fKillIST ) // Event was fired from stopIST()
{
SetEvent( dmaTransportInfo.fKillISTEvent );
ExitThread(0);
}
// Loop through channels
for ( USHORT channelIndex = 0; channelIndex < NUM_DMA_CHANNELS; ++channelIndex )
{
if ( channels[channelIndex].open ) // Channel is open
{
if ( *(channels[channelIndex].vPFlags) & kDMATransportDataReady ) // Channel has data to read
{
SetEvent( channels[channelIndex].dataReadyEvent );
}
}
}
// Loop through virtual channels
EnterCriticalSection( &vchannelAccess );
DMAChannel * currChannel = virtChannels;
while (currChannel != NULL )
{
if ( currChannel->open)
{
WRITE_PORT_ULONG( DMA_V_CHANNEL_FLAGS, currChannel->channelNumber );
// We can read any of the four flags registers - pick base one
if ( *(DMA_V_CHANNEL_FLAGS) & kDMATransportDataReady ) // Channel has data to read
{
SetEvent( currChannel->dataReadyEvent );
}
}
currChannel = currChannel->next;
}
LeaveCriticalSection( &vchannelAccess );
// Tell kernel we're done processing the interrupt
InterruptDone( dmaTransportInfo.fInterruptID );
}
return(0);
}
/*------------------------------------------------------------------
StartIST
Starts the interrupt service thread
Returns: Success(true) or failure(false)
------------------------------------------------------------------*/
BOOL
StartIST()
{
dmaTransportInfo.fKillIST = FALSE;
// Initialize the interrupt to be associated with the fDMAInterruptEvent event
if ( !InterruptInitialize(dmaTransportInfo.fInterruptID, dmaTransportInfo.fDMAInterruptEvent, NULL, 0) )
{
DEBUGMSG( TRUE, (TEXT("DMATransport:StartIST() failed on InterruptInitialize.\n")) );
return FALSE;
}
// Create the thread
dmaTransportInfo.fIntrServiceThread = CreateThread(NULL, 0, DMAInterruptServiceThread, NULL, 0, NULL);
if ( dmaTransportInfo.fIntrServiceThread == NULL ) // Failure
{
DEBUGMSG( TRUE, (TEXT("DMATransport:StartIST() failed to create interrupt service thread.\n")) );
return FALSE;
}
// Set thread priority above normal
//CeSetThreadPriority( dmaTransportInfo.fIntrServiceThread, THREAD_PRIORITY_ABOVE_NORMAL );
SetThreadPriority(dmaTransportInfo.fIntrServiceThread, THREAD_PRIORITY_HIGHEST);
// Clear any interrupts lingering
InterruptDone( dmaTransportInfo.fInterruptID );
return TRUE;
}
/*------------------------------------------------------------------
InitializeGlobals
Initializations that should only occur once for the
entire life of the driver
------------------------------------------------------------------*/
BOOL InitializeGlobals()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -