📄 mdd.c
字号:
//
// 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 + -