⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mdd.c

📁 我自己编译的armv4i wince60模拟器的bps源文件,已经验证可以使用,欢迎下载
💻 C
📖 第 1 页 / 共 5 页
字号:
//
// 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:           mdd.c

        Contains:       Serial-over-DMA driver.

==================================================================*/

#include <windows.h>
#include <linklist.h>
#include <serhw.h>
#include <ser16550.h>
#include <pegdser.h>
#include <pm.h>
#include "serdma.h"

#undef ZONE_INIT
#include <serdbg.h>


/* Debug Zones.
 */
#ifdef DEBUG

    #define DBG_INIT    0x0001
    #define DBG_OPEN    0x0002
    #define DBG_READ    0x0004
    #define DBG_WRITE   0x0008
    #define DBG_CLOSE   0x0010
    #define DBG_IOCTL   0x0020
    #define DBG_THREAD  0x0040
    #define DBG_EVENTS  0x0080
    #define DBG_CRITSEC 0x0100
    #define DBG_FLOW    0x0200
    #define DBG_IR      0x0400
    #define DBG_NOTHING 0x0800
    #define DBG_ALLOC   0x1000
    #define DBG_FUNCTION 0x2000
    #define DBG_WARNING 0x4000
    #define DBG_ERROR   0x8000

DBGPARAM dpCurSettings = {
    TEXT("SerDMA"), {
        TEXT("Init"),TEXT("Open"),TEXT("Read"),TEXT("Write"),
        TEXT("Close"),TEXT("Ioctl"),TEXT("Thread"),TEXT("Events"),
        TEXT("CritSec"),TEXT("FlowCtrl"),TEXT("Infrared"),TEXT("User Read"),
        TEXT("Alloc"),TEXT("Function"),TEXT("Warning"),TEXT("Error")},
    0
};
#endif


// SERDMA_Write uses this buffer to store data to be transmitted. Since multiple threads
// can call into it at the same time, we will provide one global buffer and if it
// is already in use, that 2nd (or 3rd...) thread will allocate a temporary buffer.
DMA_PACKET_BUFFER g_PacketBuffer;
volatile DMA_PACKET_BUFFER *g_pPacketBufferCache = &g_PacketBuffer;


//
// Macros to maintain a usage count for a particular serial structure,
// so that we know when it is safe to deallocate it.
//
#define COM_INC_USAGE_CNT(pOpenHead)  \
    InterlockedIncrement(&pOpenHead->StructUsers)

#define COM_DEC_USAGE_CNT(pOpenHead)  \
    InterlockedDecrement(&pOpenHead->StructUsers)

BOOL
SERDMA_Deinit(PHW_INDEP_INFO pSerialHead);


BOOL
DllEntry(
              HINSTANCE   hinstDll,
              DWORD   dwReason,
              LPVOID  lpReserved
              )
{
    if ( dwReason == DLL_PROCESS_ATTACH ) {
        DEBUGREGISTER(hinstDll);
        DEBUGMSG (ZONE_INIT, (TEXT("serial port process attach\r\n")));
        DisableThreadLibraryCalls((HMODULE) hinstDll);
    }

    if ( dwReason == DLL_PROCESS_DETACH ) {
        DEBUGMSG (ZONE_INIT, (TEXT("process detach called\r\n")));
    }

    return TRUE;
}

/*
 @doc INTERNAL
 @func  ULONG | SerialDispatchThread | Main serial event dispatch thread code.
 *      This is the reading and dispatching thread. It gets the
 *      event associated with the logical interrupt dwIntID and calls
 *      hardware specific routines to determine whether it's a receive event
 *      or a transmit event. If it's a transmit event, it calls the HW tx handler.
 *      If it's a receive event, it calls for the number of characters and calls
 *      atomic GetByte() to extract characters and put them into the drivers
 *      buffer represented by pSerialHead->pTargetBuffer, managing waiting
 *      for events and checking to see if those signals correspond to reading.
 *      It relies on NK masking the interrupts while it does it's thing, calling
 *      InterruptDone() to unmask them for each of the above cases.
 *
 *      Not exported to users.
 *
 @rdesc This thread technically returns a status, but in practice, doesn't return
 *              while the device is open.
 */
static DWORD WINAPI
SerialDispatchThread(
                    PVOID   pContext    /* @parm [IN] Pointer to main data structure. */
                    )
{
    PHW_INDEP_INFO      pSerialHead    = (PHW_INDEP_INFO)pContext;
    PHW_VTBL            pFuncTbl = pSerialHead->pHWObj->pFuncTbl;
    PVOID               pHWHead = pSerialHead->pHWHead;
    ULONG               RoomLeft;

    DEBUGMSG (ZONE_THREAD, (TEXT("Entered SerialDispatchThread %X\r\n"),
                            pSerialHead));

    // It is possible for a PDD to use this routine in its private thread, so
    // don't just assume that the MDD synchronization mechanism is in place.
    if ( pSerialHead->pHWObj->BindFlags & THREAD_IN_MDD ) {
        DEBUGMSG(ZONE_INIT,
                 (TEXT("Spinning in dispatch thread %X %X\n\r"), pSerialHead, pSerialHead->pHWObj));
        while ( !pSerialHead->pDispatchThread ) {
            Sleep(20);
        }
    }

    /* Wait for the event that any serial port action creates.
     */
    while ( !pSerialHead->KillRxThread ) {
        DWORD dw;

        // WinCE doesn't support WaitForMultipleObjects() with fWaitAll==TRUE.  So
        // Wait on each HANDLE individually.
        dw = WaitForSingleObject(pSerialHead->hReceiveBufferEmpty, INFINITE); // wait for the buffer to receive into to become available
        if (dw != WAIT_OBJECT_0) {
            DEBUGMSG (ZONE_FUNCTION|ZONE_ERROR,
                                (TEXT("Dispatch thread failed (1) - dw=%x, gle=%d\n"), dw, GetLastError()));
            DEBUGMSG (ZONE_THREAD, (TEXT("Exiting thread after error\r\n")));
            SetEvent(pSerialHead->hKillDispatchThread);
            return 0;
        }
        dw = WaitForSingleObject(pSerialHead->hReceiveDataReady, INFINITE); // wait for the DMA device to indicate that a packet is ready
        if (dw != WAIT_OBJECT_0) {
            DEBUGMSG (ZONE_FUNCTION|ZONE_ERROR,
                                (TEXT("Dispatch thread failed (2) - dw=%x, gle=%d\n"), dw, GetLastError()));
            DEBUGMSG (ZONE_THREAD, (TEXT("Exiting thread after error\r\n")));
            SetEvent(pSerialHead->hKillDispatchThread);
            return 0;
        }

        // The remainder of this while() loop is taken from SerialEventHandler() in the original SERIAL device
        if ( pSerialHead->KillRxThread ||
            !pSerialHead->hReceiveDataReady ) {
            DEBUGMSG (ZONE_THREAD, (TEXT("Exiting thread\r\n")));
            SetEvent(pSerialHead->hKillDispatchThread);
            return 0;
        }

        // NOTE - This one is a little tricky.  If the only owner is a monitoring task
        // then I don't have an owner for read/write, yet I might be in this routine
        // due to a change in line status.  Lets just do the best we can and increment
        // the count for the access owner if available.
        if ( pSerialHead->pAccessOwner )
            COM_INC_USAGE_CNT(pSerialHead->pAccessOwner);

        // Receive the packet from DMA into our ReceivePacketBuffer
        RoomLeft = sizeof(pSerialHead->ReceivePacketBuffer);
        pFuncTbl->HWRxIntrHandler(pHWHead,
                                  (PUCHAR)&pSerialHead->ReceivePacketBuffer,
                                  &RoomLeft);

        if (pSerialHead->ReceivePacketBuffer.u.PacketLength == 0xffff) {
            // Simulate a modem interrupt, which triggers an OS cable event if
            // no app currently has the driver open.
    	    ((PSERDMA_UART_INFO)pHWHead)->ControlFlags = 
                pSerialHead->ReceivePacketBuffer.u.PacketContents[0];
            DEBUGMSG (ZONE_EVENTS, (TEXT("SerialDispatchThread: ControlFlags = 0x%x\r\n"),
                ((PSERDMA_UART_INFO)pHWHead)->ControlFlags));
            pFuncTbl->HWModemIntrHandler(pHWHead);
        } else {
             // Indicate that data is available
            pSerialHead->RxDataReady = 1;
            SetEvent(pSerialHead->hReadEvent);
            EvaluateEventFlag(pSerialHead, EV_RXCHAR);
        }
    }

    DEBUGMSG (ZONE_THREAD, (TEXT("SerialDispatchThread %x exiting\r\n"),
                            pSerialHead));
    return 0;
}


// ****************************************************************
//
//      @doc INTERNAL
//      @func           BOOL | StartDispatchThread | Start thread if requested by PDD.
//
//      @parm           ULONG  | pSerialHead
//
//       @rdesc         TRUE if success, FALSE if failed.
//
BOOL
StartDispatchThread(
                   PHW_INDEP_INFO  pSerialHead
                   )
{
    DEBUGMSG(ZONE_INIT,
             (TEXT("Spinning thread %X\n\r"), pSerialHead));

    pSerialHead->pDispatchThread = CreateThread(NULL,0, SerialDispatchThread,
                                                pSerialHead, 0,NULL);
    if ( pSerialHead->pDispatchThread == NULL ) {
        DEBUGMSG(ZONE_INIT|ZONE_ERROR,
                 (TEXT("Error creating dispatch thread (%d)\n\r"),
                  GetLastError()));
        return FALSE;
    }

    return TRUE;
}

// ****************************************************************
//
//      @doc INTERNAL
//      @func           BOOL | StartDispatchThread | Stop thread, disable interrupt.
//
//      @parm           ULONG  | pSerialHead
//
//       @rdesc         TRUE if success, FALSE if failed.
//
BOOL
StopDispatchThread(
                  PHW_INDEP_INFO  pSerialHead
                  )

{
    HANDLE              pThisThread = GetCurrentThread();
    ULONG               priority256;

    /* If we have an interrupt handler thread, kill it */
    if ( pSerialHead->pDispatchThread ) {
        DEBUGMSG (ZONE_INIT, (TEXT("\r\nTrying to close dispatch thread\r\n")));

        /* Set the priority of the dispatch thread to be equal to this one,
         * so that it shuts down before we free its memory. If this routine
         * has been called from SerialDllEntry then RxCharBuffer is set to
         * NULL and the dispatch thread is already dead, so just skip the
         * code which kills the thread.
         */
        priority256 = CeGetThreadPriority(pThisThread);
        CeSetThreadPriority(pSerialHead->pDispatchThread, priority256);

        /* Signal the Dispatch thread to die.
         */
        pSerialHead->KillRxThread = 1;
        SetEvent(pSerialHead->hReceiveBufferEmpty);
        DEBUGMSG (ZONE_INIT, (TEXT("\r\nTrying to signal serial thread.\r\n")));
        SetEvent(pSerialHead->hReceiveDataReady);

        WaitForSingleObject(pSerialHead->hKillDispatchThread, 3000);
        Sleep(10);

        DEBUGMSG (ZONE_INIT, (TEXT("\r\nTrying to call CloseHandle\r\n")));

        CloseHandle(pSerialHead->pDispatchThread);
        pSerialHead->pDispatchThread = NULL;
        DEBUGMSG (ZONE_INIT, (TEXT("\r\nReturned from CloseHandle\r\n")));
    }

    return TRUE;
}


// ****************************************************************
//
//      @doc EXTERNAL
//      @func           HANDLE | SERDMA_Init | Serial device initialization.
//
//      @parm           ULONG  | Identifier | Port identifier.  The device loader
//                              passes in the registry key that contains information
//                              about the active device.
//
//      @remark         This routine is called at device load time in order
//                              to perform any initialization.   Typically the init
//                              routine does as little as possible, postponing memory
//                              allocation and device power-on to Open time.
//
//       @rdesc         Returns a pointer to the serial head which is passed into
//                              the SERDMA_Open and SERDMA_Deinit entry points as a device handle.
//
HANDLE
SERDMA_Init(
        ULONG   Identifier
        )
{
    PVOID           pHWHead     = NULL;
    PHW_INDEP_INFO  pSerialHead = NULL;
    DWORD           DevIndex;
    HKEY            hKey;
    ULONG           datasize;
    DWORD           DMAChannelNumber;
    WCHAR           ChannelEventName[9]; // "channelX"

    DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("+SERDMA_Init\r\n")));

    // Allocate our control structure.
    pSerialHead  =  (PHW_INDEP_INFO)LocalAlloc(LPTR, sizeof(HW_INDEP_INFO));

    if ( !pSerialHead ) {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR,
                 (TEXT("Error allocating memory for pSerialHead, SERDMA_Init failed\n\r")));
        return NULL;
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -