Windows CE Device Driver Development, Part II
by James Y. Wilson 

Example 1:
(a)
MyDriversData.pBuffer1 = MapPtrToProcess(pCallerBuffer1,GetCurrentProcess());
MyDriversData.pBuffer2 = MapPtrToProcess(pCallerBuffer2,GetCurrentProcess());

(b)
MyDriversData.pBuffer1 = MapPtrToProcess(pCallerBuffer1,GetCallerProcess());
MyDriversData.pBuffer2 = MapPtrToProcess(pCallerBuffer2,GetCallerProcess());


Listing One
// This constant contains a value which is defined in 
// $(_TARGETPLATROOT)\FILES\CONFIG.BIB as ROMOFFSET.  Physical addresses
// must be or'ed with this value or they will be considered invalid.  See
// below for more information.
#define PLATFORM_OFFSET 0x80000000

// This macro or's the 'address' with PLATFORM_OFFSET to generate
// a valid physical address.
#define ROMOFFSET(address) (address | PLATFORM_OFFSET)

#define MY_DEVICE_FRAME_BUFFER        0xB0000
#define MY_DEVICE_FRAME_BUFFER_LENGTH 0x20000

// Call this function to obtain a virtual address which is bound to the
// physical address specified in the PhysicalAddress parameter.
PVOID pMappedMemory =
    MmMapIoSpace((PHYSICAL_ADDRESS)ROMOFFSET(MY_DEVICE_FRAME_BUFFER), 
                 (ULONG)MY_DEVICE_FRAME_BUFFER_LENGTH,
                 FALSE);      // Disable caching on this memory address.
ASSERT(pMappedMemory != NULL);
 ...
// Access to this physical memory is no longer required, so free up the 
// virtual address.
MmUnmapIoSpace(pMappedMemory, (ULONG)MY_DEVICE_FRAME_BUFFER_LENGTH);

Listing Two
// Modifications to the ETK sample source file
// $(_TARGETPLATOOT)\INC\OAKINTR.H (in the CE 2.1 Preview Release)
// $(_PUBLICROOT)\COMMON\OAK\INC\NKINTR.H (in the CE 2.0 ETK)
 ...
#define SYSINTR_NETWORK             (SYSINTR_DEVICES+19)
#define SYSINTR_IDE                 (SYSINTR_DEVICES+20)
#define SYSINTR_MYDEVICE_FUNCTION1  (SYSINTR_DEVICES+21)  
// Add additional logical interrupt IDs here, not to exceed SYSINTR_MAXIMUM
#define SYSINTR_MAXIMUM             (SYSINTR_DEVICES+SYSINTR_MAX_DEVICES)
 ...

Listing Three
// Modifications to the ETK sample source file
// PLATFORM\CEPC\KERNEL\HAL\CFWPC.C:
 ...
#include "nkintr.h"
#include "oalintr.h"
#include "pc.h"
 ...
SETUP_INTERRUPT_MAP(SYSINTR_NETWORK, INTR_NETWORK);
SETUP_INTERRUPT_MAP(SYSINTR_IDE, INTR_IDE);
SETUP_INTERRUPT_MAP(SYSINTR_MYDEVICE_FUNCTION1, INTR_MYDEVICE_FUNCTION1);
// Map additional logical interrupt IDs to physical interrupt IDs and 
// remember that the physical interrupt ID constant, prefixed with INTR_, can 
// appear more than once.

// Modifications to the ETK sample source file PLATFORM\CEPC\INC\PC.H:
 ...
#define INTR_AUDIO                          11
#define INTR_MOUSE                          12
#define INTR_MYDEVICE_FUNCTION1             13
 ...

Listing Four
// Modifications to the ETK sample source file: 
// PLATFORM\CEPC\KERNEL\HAL\X86\FWPC.C. 
// It represents the most basic of ISRs for the physical interrupt 
// INTR_MYDEVICE_FUNCTION1 (defined in PLATFORM\CEPC\INC\PC.H).
ULONG MyDeviceFunction1ISR(void)
{
    // Disable the physical interrupt line to prevent reentering the IST.  
    // When IST has completed interrupt processing it will call InterruptDone 
    // which will cause the INTR_MYDEVICE_FUNCTION1 interrupt to be reenabled.
    PICEnableInterrupt(INTR_MYDEVICE_FUNCTION1, FALSE);
    // Perform high-priority, low-latency interrupt processing here. Leave
    // as much processing as possible for IST since all interrupts at this
    // point are disabled. If you determine that no additional processing is
    // needed you may return the reserved logical interrupt ID SYSINTR_NOP,
    // which will prevent activation of the IST.
     ...
    // Issue EOI command to the 8259A interrupt controller to let it process
    // next pending interrupt.  Send EOI to both interrupt controllers.
    __asm
    {
        mov al, 020h    ; Nonspecific EOI
        out 0A0h, al
        mov al, 020h    ; Nonspecific EOI
        out 020h, al
    }
    // Return the logical interrupt ID to the Interrupt Support Handler to
    // allow activation of the IST for this physical interrupt.
    return (SYSINTR_MYDEVICE_FUNCTION1);
 }
// This function is provided by Microsoft in the sample ETK source file: 
// PLATFORM\CEPC\KERNEL\HAL\X86\FWPC.C 
// It is used to initialize the 8259A compatible interrupt controllers and to
// associate ALL physical interrupts with one ISR.  Note the code added
// for the custom ISR MyDeviceFunction1.
void InitPICs(void)
{
 ...
    for (i = 64; i < 80; i++)
    {
        /* Setup the PeRP interrupt handler and enable the PeRP interrupt
         * in the BasePSR */
        HookInterrupt(i, (void *)PeRPISR);
    }
    // Add ISR for the physical interrupt INTR_MYDEVICE_FUNCTION1. Be certain 
    // to remove PeRPISR ISR which was setup in previous for loop.
    UnhookInterrupt(INTR_MYDEVICE_FUNCTION1, (void *)PeRPISR);
    HookInterrupt(INTR_MYDEVICE_FUNCTION1, (void *)MyDeviceFunction1ISR);
    //
    // Enable interrupts from cascaded PIC
    //
    PICEnableInterrupt(INTR_PIC2, TRUE);
}

Listing Five
// Provides an example implementation of an Interrupt Service Thread for the 
// logical interrupt ID SYSINTR_MYDEVICE_FUNCTION1. Assume this function is 
// activated by a call to CreateThread from main function of MyDriver.
DWORD MyDeviceFunction1IST(LPVOID pvarg)
{
    HANDLE hevInterrupt;  // event signaling a received interrupt
    // Begin by elevating the priority of this thread to be 
    // certain that it preempts the execution of application threads.
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

    // Create an unnamed event for use by the Interrupt Support 
    // Handler to signal this thread that an interrupt has been received.
    if ((hevInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
    {
        return (ERROR_INVALID_HANDLE);
    }
    // Inform the Interrupt Support Handler of the event handle
    // associated with the logical interrupt ID SYSINTR_MYDEVICE_FUNCTION1
    if (!InterruptInitialize(SYSINTR_MYDEVICE_FUNCTION1,hevInterrupt,NULL,0))
    {   
        return (ERROR_GEN_FAILURE);
    }
    // Remain in this loop indefinitely, and assume that this driver will
    // always be loaded. 
    while (TRUE)
    {
        // Wait for the Interrupt Support Handler to indicate that an
        // interrupt has been received by the ISR.
        WaitForSingleObject(hevInterrupt, INFINITE);
        // We are now ready to access Function1 of MyDevice. The next few
        // lines of code, for example, might read MyDevice registers or
        // data from a predefined port address.
         ... 
        // All processing related to this interrupt is complete. Inform
        // the Interrupt Support Handler so that the physical interrupt
        // associated with the logical interrupt ID will be reenabled.   
        InterruptDone(SYSINTR_MYDEVICE_FUNCTION1);
    }
    return (ERROR_SUCCESS);
}

Listing Six
typedef struct _MYDRIVERSDATA
{
    UINT uStructLength;    // length of this structure, new fields at end
    UINT uBuffer1Length;   // length of data referenced by pBuffer1
    UINT uBuffer2Length;   // length of data referenced by pBuffer2
    PVOID pBuffer1;        // points to data allocated by caller
    PVOID pBuffer2;        // points to data allocated by caller
          ...
} MYDRIVERSDATA, *PMYDRIVERSDATA;

Listing Seven
DWORD MyDriverThread(LPVOID pTempFileName)
{
    LPTSTR pszTempFileName = (LPTSTR)pTempFileName;
    ASSERT(pszTempFileName != NULL);
    // Open the existing file name provided in the pTempFileName parameter
    // of this thread.
    HANDLE hFileFlashBuffer =
        CreateFileForMapping(pszTempFileName, GENERIC_READ, FILE_SHARE_READ,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
        NULL);       
    ASSERT(hFileFlashBuffer != INVALID_HANDLE_VALUE);
    // Create a mapping object from the file handle just created.
    HANDLE hMapFlashBuffer =
        CreateFileMapping(hFileFlashBuffer, NULL, PAGE_READONLY, 0, 0, NULL);
    ASSERT(hMapFlashBuffer != INVALID_HANDLE_VALUE);
    // Now use this mapping object to obtain a pointer into the file, within
    // the address space of this thread. 
    LPVOID pData = MapViewOfFile(hMapFlashBuffer, FILE_MAP_READ, 0, 0, 0);
    ASSERT(pData != NULL);
    // The address in pData is now available for read only access by the 
    // by the driver's thread. Additional code accessing the data referenced
    // by pData would normally follow.
      ... 
    // Shut down all of the resources allocated.
    VERIFY(UnmapViewOfFile(pData));
    // Close the mapping object, but do NOT close the file handle.  
    // Experience has shown that this handle is closed automatically when 
    // the mapping object is closed.  Any attempt to close the file handle
    // will cause an exception.
    VERIFY(CloseHandle(hMapFlashBuffer));

    return (ERROR_SUCCESS);
}

Listing Eight
TARGETNAME=MYDRIVER
SOURCES= \
    Api.cpp \
    IO.cpp
INCLUDES=..\..\inc; \
    $(_COMMONDDKROOT)\inc; \
    $(_COMMONOAKROOT)\inc
RELEASETYPE=PLATFORM
TARGETLIBS= \
   $(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib  
SOURCELIBS=
   $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\MyCompanyUtils.lib
TARGETTYPE=DYNLINK
DLLENTRY=MyDriverDllEntry

Listing Nine 
 ...
IF MYPLATFORM_NOMYDRIVER !
    mydriver.dll     $(_FLATRELEASEDIR)\mydriver.dll               NK  SH
ENDIF
 ...

Listing Ten
 ...
PUSHD %_PLATFORMROOT%\%_TGTPLAT%\DRIVERS\MYDRIVER
BUILD -cfs
if not exist build.err goto ELSE_10
    echo !!!! Build in %_PLATFORMROOT%\%_TGTPLAT%\DRIVERS\MYDRIVER had errors.
    pause
:ELSE_10
POPD
 ...

Listing Eleven
I/O Driver MYDRIVER.DLL
 ...
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\MyDriver]
    "Dll"="MyDriver.dll"
    "Order"=dword:1
    "Prefix"="MYD"
    "Index"=dword:1
;Add custom keys for this driver here...
    "FriendlyName"="Sample Stream I/O Driver"
 ...

6


