wince串口線程、中斷等相關學習


wince串口線程、中斷等相關學習(作者:wogoyixikexie@gliet)收藏
                                  wince串口線程、中斷等相關學習(作者:wogoyixikexie@gliet
    前面發了好幾篇wince串口的學習的文章,由於是學習性質的,弄的比較亂,還請網友們原諒。以前只是搞懂了大體框架,對這個中斷線程等底層的東西還沒有了解,現在來來學習一下。

 
VOID
SerialEventHandler(PHW_INDEP_INFO       pSerialHead)
{
    PHW_VTBL            pFuncTbl = pSerialHead->pHWObj->pFuncTbl;
    PVOID               pHWHead = pSerialHead->pHWHead;
    ULONG               CharIndex;
    ULONG               RoomLeft = 0;
    ULONG               TotalLeft = 0;
    INTERRUPT_TYPE      it = INTR_NONE;
    BOOL                RxDataAvail = FALSE;
    DEBUGMSG (ZONE_THREAD, (TEXT("+SerialEventHandler, pHead 0x%X/r/n"),
                            pSerialHead));
    if ( pSerialHead->KillRxThread ||
         !pSerialHead->hSerialEvent ) {
        DEBUGMSG (ZONE_THREAD, (TEXT("Exitting thread/r/n")));
        SetEvent(pSerialHead->hKillDispatchThread);
        ExitThread(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);
    while ( 1 ) {
        if ( !(it = pFuncTbl->HWGetIntrType(pHWHead)) ) {
            DEBUGMSG (ZONE_THREAD,
                      (TEXT("SerialEventHandler, No Interrupt./r/n")));
            break;
        }
        DEBUGMSG (ZONE_THREAD,
                  (TEXT("SerialEventHandler, Interrupts 0x%X/r/n"), it));
        if ( it & INTR_RX ) {
            // It's read data event. Optimize the read by reading chunks
            // if the user has not specified using xflow control
            // or event/error/eof characters. Ack the receive,
            // unmask the interrupt, get the current data pointer
            // and see if data is available.
            // Note: We have to copy RxRead and RxWrite index to local in order to make it atomic.
            register DWORD RxWIndex=RxWrite(pSerialHead), RxRIndex=RxRead(pSerialHead);
            DEBUGMSG (ZONE_THREAD|ZONE_READ , (TEXT("Rx Event/r/n")));
            if ( RxRIndex == 0 ) {
                // have to leave one byte free.
                RoomLeft = RxLength(pSerialHead) - RxWIndex - 1;
            } else {
                RoomLeft = RxLength(pSerialHead) - RxWIndex;
            }
            if ( RxRIndex > RxWIndex ) {
                RoomLeft = RxRIndex - RxWIndex - 1;
            }
            if ( RoomLeft ) {
                pSerialHead->DroppedBytesPDD +=
                pFuncTbl->HWRxIntrHandler(pHWHead,
                                          RxBuffWrite(pSerialHead),
                                          &RoomLeft);
            } else {
                BYTE    TempBuf[16];
                RoomLeft = 16;
                pFuncTbl->HWRxIntrHandler(pHWHead,
                                          TempBuf,
                                          &RoomLeft);
                pSerialHead->DroppedBytesMDD += RoomLeft;
                DEBUGMSG (ZONE_WARN|ZONE_READ, (TEXT("Tossed %d bytes/r/n"),
                                                RoomLeft));
                RoomLeft = 0;
            }
            DEBUGMSG (ZONE_READ ,
                      (TEXT("After HWGetBytes, Fifo(R=%d,W=%d,BA=%d,L=%d) ByteRead=%d/r/n"),
                       RxRead(pSerialHead), RxWrite(pSerialHead),
                       RxBytesAvail(pSerialHead), RxLength(pSerialHead),
                       RoomLeft));
            // If flow control enabled then we need to scan for XON/XOFF
            // characters
            if ( pSerialHead->XFlow ) {
                for ( CharIndex=0; CharIndex < RoomLeft; ) {
                    if ( RxBuffWrite(pSerialHead)[CharIndex] ==
                         pSerialHead->DCB.XoffChar ) {
                        DEBUGMSG (ZONE_FLOW, (TEXT("Received XOFF/r/n")));
                        pSerialHead->StopXmit = 1;
                        memmove (RxBuffWrite(pSerialHead)+CharIndex,
                                 RxBuffWrite(pSerialHead)+CharIndex+1,
                                 RoomLeft - CharIndex);
                        RoomLeft--;
                        continue;
                    } else if ( RxBuffWrite(pSerialHead)[CharIndex] ==
                                pSerialHead->DCB.XonChar ) {
                        pSerialHead->StopXmit = 0;
                        DEBUGMSG (ZONE_FLOW, (TEXT("Received XON/r/n")));
                        memmove (RxBuffWrite(pSerialHead)+CharIndex,
                                 RxBuffWrite(pSerialHead)+CharIndex+1,
                                 RoomLeft - CharIndex);
                        RoomLeft--;
                        // We disabled TX on XOFF, so now we need to start sending
                        // again. Easiest way is to pretend we saw a TX interrupt
                        it |= INTR_TX;
                        continue;
                    }
                    CharIndex++;
                }
            }
            pSerialHead->RxBytes += RoomLeft;
            RxWrite(pSerialHead) = 
                (RxWrite(pSerialHead)+RoomLeft<RxLength(pSerialHead)? RxWrite(pSerialHead)+RoomLeft: RxWrite(pSerialHead)+RoomLeft-RxLength(pSerialHead));
            if ( RoomLeft ) {
                RxDataAvail = TRUE;
            }
            /* Support DTR_CONTROL_HANDSHAKE/RTS_CONTROL_HANDSHAKE
             * signal is cleared when the input buffer is more than 3/4 full.
             */
            if ( (pSerialHead->DCB.fDtrControl == DTR_CONTROL_HANDSHAKE) &
                 (!pSerialHead->DtrFlow) && 
                 (4*RxBytesAvail(pSerialHead) > (3*RxLength(pSerialHead))) ) {
                DEBUGMSG (ZONE_READ|ZONE_FLOW,
                          (TEXT("DTR_CONTROL_HANDSHAKE Clearing DTR/r/n")));
                pSerialHead->DtrFlow = 1;
                pFuncTbl->HWClearDTR(pHWHead);
            }
            if ( (pSerialHead->DCB.fRtsControl == RTS_CONTROL_HANDSHAKE) &
                 (!pSerialHead->RtsFlow) &
                 (4*RxBytesAvail(pSerialHead) > (3*RxLength(pSerialHead))) ) {
                DEBUGMSG (ZONE_READ|ZONE_FLOW,
                          (TEXT("RTS_CONTROL_HANDSHAKE Clearing RTS/r/n")));
                pSerialHead->RtsFlow = 1;
                pFuncTbl->HWClearRTS(pHWHead);
            }
            /* If Xon/Xoff flow control is desired. check the limit against
             * the remaining room and act accordingly.
             */
            if ( pSerialHead->DCB.fInX && !(pSerialHead->SentXoff) &
                 ( pSerialHead->DCB.XoffLim >=
                   (RxLength(pSerialHead) - RxBytesAvail(pSerialHead))) ) {
                DEBUGMSG (ZONE_FLOW, (TEXT("Sending XOFF/r/n")));
                pFuncTbl->HWXmitComChar(pHWHead, pSerialHead->DCB.XoffChar);
                pSerialHead->SentXoff = 1;
                if ( !pSerialHead->DCB.fTXContinueOnXoff ) {
                    pSerialHead->StopXmit = 1;
                }
            }
        }
        //-與中斷相關的操作
        if ( it & INTR_TX ) {
            DEBUGMSG (ZONE_THREAD|ZONE_WRITE , (TEXT("Tx Event/r/n")));
            DoTxData( pSerialHead );//收到發送中斷后發送數據
        }
        if ( (it & INTR_MODEM) ) {
            DEBUGMSG (ZONE_THREAD, (TEXT("Other Event, it:%x/r/n"), it));
            /* Call low level status clean up code.
             */
            pFuncTbl->HWModemIntrHandler(pHWHead);//這個函數會對應下層函數實現
        }
        if ( it & INTR_LINE ) {
            DEBUGMSG (ZONE_THREAD, (TEXT("Line Event, it:%x/r/n"), it));
            /* Call low level line status clean up code.
             * Then unmask the interrupt
             */
            pFuncTbl->HWLineIntrHandler(pHWHead);//這個是干什么的?
        }
    }
    // We kept this till the end to optimize the above loop
    if ( RxDataAvail ) {
        // Signal COM_Read that bytes are available.
        SetEvent(pSerialHead->hReadEvent);
        EvaluateEventFlag(pSerialHead, EV_RXCHAR);
    }
    DEBUGMSG (ZONE_THREAD ,
              (TEXT("-SerialEventHandler, Fifo(R=%d,W=%d,L=%d)/r/n"),
               RxRead(pSerialHead), RxWrite(pSerialHead),
               RxLength(pSerialHead)));
    if ( pSerialHead->pAccessOwner )
        COM_DEC_USAGE_CNT(pSerialHead->pAccessOwner);
    return;
}
                        

    從這個SerialEventHandler的實現代碼來看,這個SerialEventHandler幾乎包括了所有串口功能的操作。把讀寫線程等都集成在一起了。通過判斷中斷類型來執行不同的函數。

   SerialEventHandler函數被兩個地方調用

   一、在49行處調用,

 

/*
 @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;
    ULONG               WaitReturn;

    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 ) {
        DEBUGMSG (ZONE_THREAD, (TEXT("Event %X, %d/r/n"),
                                pSerialHead->hSerialEvent,
                                pSerialHead->pHWObj->dwIntID ));
        WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);

        SerialEventHandler(pSerialHead);//開始執行串口線程
        InterruptDone(pSerialHead->pHWObj->dwIntID);
    }

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

        SerialDispatchThread函數是等待線程啟動的作用,微軟的代碼還是寫的比較清晰的,來看看SerialDispatchThread函數在哪里調用了。

// ****************************************************************
//
// @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
                   )
{
    // Initialize the interrupt to be associated with the hSerialEvent
    // event. GetByte waits on this event and acts as a second
    // level decoder determining the type of serial event. If this return
    // fails, then another process has registered for the interrupt, so
    // fail the init and set the hSerialEvent to NULL.
    DEBUGMSG(ZONE_INIT,
             (TEXT("Initializing interrupt 0x%X, 0x%X/n/r"),
              pSerialHead->pHWObj->dwIntID, pSerialHead->hSerialEvent));
//中斷綁定串口線程SerialEventHandler,這個dwIntID並不是什么中斷
//而是注冊表中的DeviceArrayIndex的值why?
    if ( !InterruptInitialize(pSerialHead->pHWObj->dwIntID,
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) ) {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR,
                 (TEXT("Error initializing interrupt/n/r")));
        return(FALSE);
    }

    InterruptDone(pSerialHead->pHWObj->dwIntID);

    // Set up the dispatch thread and it's kill flag. Note that the thread
    // fills in its own handle in pSerialHead.
    pSerialHead->KillRxThread = 0;
    pSerialHead->pDispatchThread = NULL;

    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);
    }

    DEBUGMSG (ZONE_INIT, (TEXT("Created receive thread %X/r/n"),
                          pSerialHead->pDispatchThread));    
    return(TRUE);
}
 

再來看這個StartDispatchThread被誰調用了

(1)COM_Init調用了

  if ( pSerialHead->pHWObj->BindFlags & THREAD_AT_INIT ) {
        // Hook the interrupt and start the associated thread.
        if ( ! StartDispatchThread( pSerialHead ) ) {
            // Failed on InterruptInitialize or CreateThread.  Bail.
            COM_Deinit(pSerialHead);
            return(NULL);       
        }

    }

(2)COM_Open調用了

 if ( pSerialHead->pHWObj->BindFlags & THREAD_AT_OPEN ) {
            DEBUGMSG(ZONE_INIT|ZONE_OPEN,
                     (TEXT("COM_Open: Starting DispatchThread x%X/n/r"),
                      pOpenHead));
            // Hook the interrupt and start the associated thread.
            if ( ! StartDispatchThread( pSerialHead ) ) {
                // Failed on InterruptInitialize or CreateThread.  Bail.
                DEBUGMSG(ZONE_INIT|ZONE_OPEN,
                         (TEXT("COM_Open: Failed StartDispatchThread x%X/n/r"),
                          pOpenHead));
                goto OpenFail;
            }
        }

 

 

二、NotifyPDDInterrupt調用了SerialEventHandler函數

//NotifyPDDInterrupt在PDD和中間層被調用了
BOOL CSerialPDD::NotifyPDDInterrupt(INTERRUPT_TYPE interruptType)
{
    m_InterruptLock.Lock();
    // The interrupt is define as Bit event.
    m_dwInterruptFlag |= (DWORD)interruptType;
    m_InterruptLock.Unlock();
    if (IsPowerResumed ( )) {
        if (m_lOpenCount) { // If application is opened.
            EventCallback( EV_POWER );
        }
        else {
            if (GetModemStatus() & MS_RLSD_ON)
                CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED, NULL);
        }
    }
    SerialEventHandler(m_pMdd);
    return TRUE;
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 現在來看看這個中斷到底怎么回事,感覺和4.2的區別太大了。

 ———————————————————————————————————————————————

我找了好久,終於找到PDD線程相關的地方,這里值得注意的是C++語言的構造函數和析構函數的作用,在第一次

建類對象的時候會調用構造函數(構造函數可以用來初始化對象),而清除類對象的時候調用析構函數(如果不懂

C++的,比如我,找了好久才知道在析構函數關閉了串口線程句柄,慚愧。)

//構造函數,當創建對象的時候會自動調用

CPdd2440Uart::CPdd2440Uart (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj )
:   CSerialPDD(lpActivePath,pMdd, pHwObj)
,   m_ActiveReg(HKEY_LOCAL_MACHINE,lpActivePath)
,   CMiniThread (0, TRUE)  
{
    m_pReg2440Uart = NULL;
    m_pINTregs = NULL;
    m_dwIntShift = 0;
    m_dwSysIntr = MAXDWORD;
    m_hISTEvent = NULL;
    m_dwDevIndex = 0;
    m_pRegVirtualAddr = NULL;
    m_XmitFlushDone =  CreateEvent(0, FALSE, FALSE, NULL);
    m_XmitFifoEnable = FALSE;
    m_dwWaterMark = 8 ;
}

//析構函數,當清除類對象的時候會自動調用
CPdd2440Uart::~CPdd2440Uart()
{
    InitModem(FALSE);
    if (m_hISTEvent) {
        m_bTerminated=TRUE;
        ThreadStart();
        SetEvent(m_hISTEvent);
        ThreadTerminated(1000);
        InterruptDisable( m_dwSysIntr );   //禁止中斷     
        CloseHandle(m_hISTEvent);//關閉串口線程句柄
    };
    if (m_pReg2440Uart)
        delete m_pReg2440Uart;
    if (m_XmitFlushDone)
        CloseHandle(m_XmitFlushDone);
    if (m_pRegVirtualAddr != NULL) {
        MmUnmapIoSpace((PVOID)m_pRegVirtualAddr,0UL);//釋放虛擬內存
    }
    if (m_pINTregs!=NULL) {
        MmUnmapIoSpace((PVOID)m_pINTregs,0UL));//釋放虛擬內存

    }
       
}

//---------至於更進一步分析,明天做個比較完整的總結吧,看來年前的時間都用來學C++比較合適

 

————————————上面都是比較亂,現在總結一下一個正常的線程架構——————————————

首先是創建線程 pSerialHead->hSerialEvent = CreateEvent(0,FALSE,FALSE,NULL);

InterruptInitialize(pSerialHead->pHWObj->dwIntID,//這個中斷和PDD的中斷以及相關線程到底有什么不同?
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) )

 SetEvent(pSerialHead->hSerialEvent);

WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);

SerialEventHandler(pSerialHead);//開始執行串口線程

CloseHandle(pSerialHead->hSerialEvent);//關閉線程句柄

—————————————————————————————————————————————————

WaitForSingleObject函數用來檢測hHandle事件的信號狀態,當函數的執行時間超過dwMilliseconds就返回,但如果參數dwMilliseconds為INFINITE時函數將直到相應時間事件變成有信號狀態才返回,否則就一直等待下去,直到WaitForSingleObject有返回直才執行后面的代碼。在這里舉個例子:

先創建一個全局Event對象g_event:
CEvent g_event;
在程序中可以通過調用CEvent::SetEvent設置事件為有信號狀態。

在驅動程序中,因為線程往往和硬件中斷相關,中斷產生就執行線程,中斷關閉就不執行線程,所以串口提供了

兩種執行線程的方法(1)產生中斷.(2)SetEvent(線程句柄)

—————————————————————————————————————————————————

MDD中的中斷是怎么回事?

在SerialEventHandler函數中調用了HWGetIntrType函數(其實是調用了轉換函數表的SerGetInterruptType函數)

INTERRUPT_TYPE
SerGetInterruptType(
                   PVOID pHead      // Pointer to hardware head
                   )
{
    CSerialPDD * pSerialPDD  = ( CSerialPDD * )pHead;
    INTERRUPT_TYPE interrupts=INTR_NONE;
    DEBUGMSG (ZONE_EVENTS,(TEXT("+SerGetInterruptType 0x%X/r/n"), pHead));
    if (pSerialPDD)
        interrupts= pSerialPDD->GetInterruptType();
    DEBUGMSG (ZONE_EVENTS,(TEXT("-SerGetInterruptType (0x%X) return %X/r/n"), pHead, interrupts));
    return interrupts;
}

——————————

INTERRUPT_TYPE CSerialPDD::GetInterruptType()
{
    m_InterruptLock.Lock();
    // The interrupt is define as Bit event.
    INTERRUPT_TYPE lIntrFlagRet= (INTERRUPT_TYPE )m_dwInterruptFlag;
    m_dwInterruptFlag = INTR_NONE;
    m_InterruptLock.Unlock();
    return lIntrFlagRet;
}

——


BOOL CSerialPDD::NotifyPDDInterrupt(INTERRUPT_TYPE interruptType)
{
    m_InterruptLock.Lock();
    // The interrupt is define as Bit event.
    m_dwInterruptFlag |= (DWORD)interruptType;
    m_InterruptLock.Unlock();
    if (IsPowerResumed ( )) {
        if (m_lOpenCount) { // If application is opened.
            EventCallback( EV_POWER );
        }
        else {
            if (GetModemStatus() & MS_RLSD_ON)
                CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED, NULL);
        }
    }
    SerialEventHandler(m_pMdd);
    return TRUE;
}

——看看這個NotifyPDDInterrupt被誰調用了就知道怎么回事了。

 在PDD層的ThreadRun函數調用了NotifyPDDInterrupt函數

DWORD CPdd2440Uart::ThreadRun()
{
    while ( m_hISTEvent!=NULL && !IsTerminated()) {
        if (WaitForSingleObject( m_hISTEvent,m_dwISTTimeout)==WAIT_OBJECT_0) {
            m_HardwareLock.Lock();   
            while (!IsTerminated() ) {
                DWORD dwData = (GetInterruptStatus() & (S2440UART_INT_RXD|S2440UART_INT_TXD|S2440UART_INT_ERR));
                DWORD dwMask = (GetIntrruptMask() & (S2440UART_INT_RXD|S2440UART_INT_TXD|S2440UART_INT_ERR));
                 DEBUGMSG(ZONE_THREAD,
                      (TEXT(" CPdd2440Uart::ThreadRun INT=%x, MASK =%x/r/n"),dwData,dwMask));
                dwMask &= dwData;
                if (dwMask) {
                    DEBUGMSG(ZONE_THREAD,
                      (TEXT(" CPdd2440Uart::ThreadRun Active INT=%x/r/n"),dwMask));
                    DWORD interrupts=INTR_MODEM; // Always check Modem when we have change. It may work at polling mode.
                    if ((dwMask & S2440UART_INT_RXD)!=0)
                        interrupts |= INTR_RX;
                    if ((dwMask & S2440UART_INT_TXD)!=0)
                        interrupts |= INTR_TX;
                    if ((dwMask & S2440UART_INT_ERR)!=0)
                        interrupts |= INTR_LINE|INTR_RX;
                    NotifyPDDInterrupt((INTERRUPT_TYPE)interrupts);
                    ClearInterrupt(dwData);
                }
                else
                    break;
            }
            m_HardwareLock.Unlock();  
            InterruptDone(m_dwSysIntr);
        }
        else { // Polling Modem.
            NotifyPDDInterrupt(INTR_MODEM);
            DEBUGMSG(ZONE_THREAD,(TEXT(" CPdd2440Uart::ThreadRun timeout INT=%x,MASK=%d/r/n"),m_pINTregs->SUBSRCPND,m_pINTregs->INTSUBMSK));
#ifdef DEBUG
            if ( ZONE_THREAD )
                m_pReg2440Uart->DumpRegister();
#endif
        }
    }
    return 1;
}
——————————————————————————————————————————

這個有點奇怪,這個是硬件中斷相關的東西,但是在MMD層有個中斷實在讓人費解

InterruptInitialize(pSerialHead->pHWObj->dwIntID,//這個中斷和PDD的中斷以及相關線程到底有什么不同?
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) )

在中間層的SerInit函數有

//為什么會這樣做呢,中斷和
  //DeviceArrayIndex()注冊表扯上關系?
        DWORD dwIndex= pHWObj->dwIntID;
        pHWObj->dwIntID = 0;

 ——————————————————————

找到了

 

// GetSerialObj : The purpose of this function is to allow multiple PDDs to be
// linked with a single MDD creating a multiport driver.  In such a driver, the
// MDD must be able to determine the correct vtbl and associated parameters for
// each PDD.  Immediately prior to calling HWInit, the MDD calls GetSerialObject
// to get the correct function pointers and parameters.
//
extern "C" PHWOBJ
GetSerialObject(
               DWORD DeviceArrayIndex
               )
{
    PHWOBJ pSerObj;

    // Unlike many other serial samples, we do not have a statically allocated
    // array of HWObjs.  Instead, we allocate a new HWObj for each instance
    // of the driver.  The MDD will always call GetSerialObj/HWInit/HWDeinit in
    // that order, so we can do the alloc here and do any subsequent free in
    // HWDeInit.
    // Allocate space for the HWOBJ.
    pSerObj=(PHWOBJ)LocalAlloc( LPTR ,sizeof(HWOBJ) );
    if ( !pSerObj )
        return (NULL);

    // Fill in the HWObj structure that we just allocated.

    pSerObj->BindFlags = THREAD_IN_PDD;     // PDD create thread when device is first attached.
    pSerObj->dwIntID = DeviceArrayIndex;   // Only it is useful when set set THREAD_AT_MDD. We use this to transfer DeviceArrayIndex
    pSerObj->pFuncTbl = (HW_VTBL *) &IoVTbl; // Return pointer to appropriate functions

    // Now return this structure to the MDD.
    return (pSerObj);
}

 

果然是注冊表中的DeviceArrayIndex值就是dwIntID ,不過我始終對前面的線程綁定dwIntID 有點不解

//中斷綁定串口線程SerialEventHandler,這個dwIntID並不是什么硬件中斷,這樣調用怎么能成功?
//而是注冊表中的DeviceArrayIndex的值why?
    if ( !InterruptInitialize(pSerialHead->pHWObj->dwIntID,
                              pSerialHead->hSerialEvent,
                              NULL,
                              0) )

——————————————————————————————

按照道理,上面是這個所謂的“中斷”(dwIntID )綁定了句柄為hSerialEvent的線程。只要dwIntID 有硬件動作

才會啟動句柄為hSerialEvent的線程。但是這個中斷是假的,不知道微軟是怎么弄的,搞定這個,串口的架構基本是算完成了。

——————引用何宗健老師的回答

InterruptInitialize函數要調用成功,必須要求目前的event沒有對象正在等待。因此,如果你先創建了線程,並且已經執行了WaitForSingleObject一句的話,肯定就不能滿足這個要求了。

 


  其實InterruptInitialize函數在WinCE里面是有完整源代碼的,在/WINCE600/PRIVATE/WINCEOS/COREOS/NK/KERNEL/intrapi.c中,你可以順藤摸瓜,看看它是如何把中斷跟event連上的。

 

 


     中斷是硬件與軟件打交道的重要方法,因此,大多數驅動程序都涉及到對中斷的處理,本文就驅動程序的開發人員以及BSP的開發人員的角度,來談談Windows CE中中斷的處理過程。

如果一個驅動程序要處理一個中斷,那么驅動程序需要首先建立一個事件,可以使用CreateEvent函數,然后調用InterruptInitialize將該事件與中斷號綁定,這一步就會使能該中斷,OAL中的OEMInerrupteEnable就會被調用,如果該函數不返回true的話,InterruptInitialize就會失敗。然后驅動程序中的IST就可以使用WaitForSingleObject函數來等待中斷的發生。

      當一個硬件中斷發生之后,操作系統陷入異常,中斷向量指示進入CE的異常處理程序,該異常處理程序然后調用OAL的OEMInterruptHandler函數,該函數檢測硬件之后,將硬件中斷轉換為軟件的中斷號,返回給系統。該中斷號就是上面提到的InterruptInitialize中使用的那個中斷號。系統得到該中斷號之后,就會找到該中斷號對應的事件,並喚醒等待相應事件的線程(IST),然后IST就可以在用戶態進行中斷處理。處理完成之后,IST需要調用InterruptDone來告訴操作系統中斷處理結束,操作系統再次調用OAL中的OEMInterruptDone函數,最后完成中斷的處理。

在上面的描述中,驅動程序需要使用的是InterruptInitialize,WaitForSingleObject和InterruptDone三個函數,而BSP開發者需要主要考慮的是OEMInerrupteEnable,OEMInterruptHandler,OEMInterruptDone三個函數。當然,這是上面提到的一些函數,實際上BSP開發者在中斷部分還需要考慮Init以及Disable等等函數,這里就不再討論了。

——對於這個問題我還是無法解釋,怎么想也不會使用這種注冊表的一些參數來啊,要徹底明白,只能看看InterruptInitialize源碼到底是怎么回事了。

————————————————————

wogoyixikexie@gliet 說:
WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);

        SerialEventHandler(pSerialHead);//開始執行串口線程
CSDN-廣州天河wince6.0 說:
???

wogoyixikexie@gliet 說:
要有信號來才會執行這個線程,可是我發現這個
SetEvent(pSerialHead->hSerialEvent);
在StopDispatchThread函數里面,而StopDispatchThread是被COM_Deinit函數調用的
wogoyixikexie@gliet 說:
那么這樣導致了關閉驅動的時候才執行上面那個串口發送接收相關的線程SerialEventHandler
wogoyixikexie@gliet 說:
我看后實在不解,不過它是能正常工作的
wogoyixikexie@gliet 說:
我想求個讓人信服的理由
加密助手 說:
--- 系統提示: 以下會話未被加密 ---
CSDN-廣州天河wince6.0 說:
兩個事件是不一樣的吧
wogoyixikexie@gliet 說:
一樣的
wogoyixikexie@gliet 說:
這個線程句柄就是用來控制線程是否執行的
wogoyixikexie@gliet 說:
如果沒有信號,就等待
CSDN-廣州天河wince6.0 說:
WaitReturn = WaitForSingleObject(pSerialHead->hSerialEvent, INFINITE);
是在那個函數中
CSDN-廣州天河wince6.0 說:
threadrun(_)?
wogoyixikexie@gliet 說:
SerialDispatchThread
wogoyixikexie@gliet 說:
這個是MDD的線程
wogoyixikexie@gliet 說:
PDD的線程是和硬件中斷相關的
wogoyixikexie@gliet 說:
真是神了,這樣使用注冊表一個配置產生了一個線程
加密助手 說:
--- 系統提示: 以下會話未被加密 ---
CSDN-廣州天河wince6.0 說:
不太清楚它為什么要這樣子做
wogoyixikexie@gliet 說:
呵呵,先放一下了,以后再補充了。
CSDN-廣州天河wince6.0 說:
好像這個線程一直都是阻塞的

——————————————————————————————————————————————————

對於這個MDD中斷的線程問題,真搞不懂為什么會這樣,先放下了,以后有機會再補充。了解的朋友們請在

博客后面指點一下啊。感激不盡。

轉載請標明:作者wogoyixikexie@gliet.桂林電子科技大學一系科協,原文地址:http://blog.csdn.net/gooogleman——如有錯誤,希望能夠留言指出;如果你有更加好的方法,也請在博客后面留言,我會感激你的批評和分享。


發表於 @ 2009年01月14日 11:51:00|評論(2)|收藏

新一篇: 吳宇森.赤壁 | 舊一篇: windowsCE.net 420串口驅動分析Ricky_hu 發表於2009年1月16日 20:07:19  IP: Serial port that the driver manages. This key specifies the serial adapter that the driver manages. It also specifies the object to use inside the driver DLL. It is only useful for multi-object drivers, such as serial drivers, PC card drivers, built-in drivers, infrared drivers, and so on. You cannot use this key for single object drivers.

typedef struct __HWOBJ {
ULONG BindFlags; // Flags controlling MDD behaviour. Se above.
DWORD dwIntID; // Interrupt Identifier used if THREAD_AT_INIT or THREAD_AT_OPEN
PHW_VTBL pFuncTbl;
} HWOBJ, *PHWOBJ;

看看pdd中這個函數CSerialPDD * CreateSerialObject()

這個中斷是軟件中斷,不是物理的硬件中斷 文章鏈接:http://blog.csdn.net/gooogleman/archive/2009/01/14/3774536.aspx 發表時間:2009年1月16日 20:07:19">舉報
"DeviceArrayIndex"
Serial port that the driver manages. This key specifies the serial adapter that the driver manages. It also specifies the object to use inside the driver DLL. It is only useful for multi-object drivers, such as serial drivers, PC card drivers, built-in drivers, infrared drivers, and so on. You cannot use this key for single object drivers.

typedef struct __HWOBJ {
ULONG BindFlags; // Flags controlling MDD behaviour. Se above.
DWORD dwIntID; // Interrupt Identifier used if THREAD_AT_INIT or THREAD_AT_OPEN
PHW_VTBL pFuncTbl;
} HWOBJ, *PHWOBJ;

看看pdd中這個函數CSerialPDD * CreateSerialObject()

這個中斷是軟件中斷,不是物理的硬件中斷

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/gooogleman/archive/2009/01/15/3774536.aspx


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com