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

📄 pl010ser.c

📁 EP931X系列的WinCE串口驱动源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.

Module Name:  

ser16550.c

Abstract:  

    This file implements the standard device specific functions for a 16550
    based serial device.

Functions:

    SL_Init2()
    SL_PostInit()
    SL_Deinit()
    SL_Open()
    SL_Close()
    SL_ClearDTR()
    SL_SetDTR()
    SL_ClearRTS()
    SL_SetRTS()
    SL_ClearBreak()
    SL_SetBreak()
    SL_SetBaudRate()
    SL_SetByteSize()
    SL_SetParity()
    SL_SetStopBits()
    SL_GetRxBufferSize()
    SL_GetRxStart()
    SL_GetInterruptType()
    SL_RxIntr()
    SL_PutBytes()
    SL_TxIntr()
    SL_LineIntr()
    SL_OtherIntr()
    SL_GetStatus()
    SL_Reset()
    SL_GetModemStatus()
    SL_PurgeComm()
    SL_XmitComChar()
    SL_PowerOff()
    SL_PowerOn()
    SL_SetDCB()
    SL_SetCommTimeouts()
    SL_Ioctl()
    ReadRSR()
    ReadFR()
    DumpSerialRegisters()
    LookUpValue()
    DivisorOfRate()



Notes:
    The RegCritSec is there to protect against non-atomic access of
    register pairs.  On a 16550, the main such collision comes from 
    the fact that THR and IER are overloaded as the DivLatch registers
    and their mode is controlled via the LCR.  So we need the 
    critical section protection around all access of these 3 registers.
    But we also need to watch out for read/modify/write where we are
    setting/ckearing a bit.  In general, I just go ahead and acquire
    the CS around all register accesses.
--*/

#include <windows.h>
#include <types.h>
#include <memory.h>
#include <serhw.h>
#include "pl010ser.h"
#include <ser16550.h>
#include "pl010com.h"
#include <hw16550.h>
#include <serdbg.h>
#include <excpt.h>
#include <hwdefs.h>
#include <clocks.h>


// Macros to read/write serial registers.
//#define INB(pInfo, reg) (READ_PORT_UCHAR((UCHAR *)((pInfo)->reg)))
//#define OUTB(pInfo, reg, value) (WRITE_PORT_UCHAR((UCHAR *)((pInfo)->reg), (unsigned char)(value)))

#define INB(pInfo, reg)             (*((UCHAR *)((pInfo)->reg)))
#define OUTB(pInfo, reg, value)     (*(UCHAR *)((pInfo)->reg) =  (unsigned char)(value) )

BOOL SL_SetByteSize(PVOID pHead, ULONG ByteSize);
BOOL SL_SetStopBits(PVOID pHead, ULONG StopBits);
BOOL SL_SetParity(PVOID pHead, ULONG Parity);

#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION 

__inline
VOID
ReadRSR(
       PSER_INFO  pHead
       )
{
    PAMBA_UART_INFO pSerAMBA  = &(((PSER_INFO)pHead)->serAMBA);
    PSER16550_INFO  pSer16550 = &(((PSER_INFO)pHead)->ser16550);
    ULONG LineEvents = 0;

    try {
        pSerAMBA->RSR = INB(pSerAMBA, pUART_RSR);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        pSerAMBA->RSR = 0;
    }

    if ( pSerAMBA->RSR & (AMBA_UARTRSR_OE | AMBA_UARTRSR_PE | AMBA_UARTRSR_FE)) 
    {
        // Note: Its not wise to do debug msgs in here since they will
        // pretty much guarantee that the FIFO gets overrun.
        if ( pSerAMBA->RSR & AMBA_UARTRSR_OE )
        {
            // DEBUGMSG (ZONE_WARN, (TEXT("Overrun\r\n")));
            pSer16550->DroppedBytes++;
            pSer16550->CommErrors |= CE_OVERRUN;

            // Clear state.
            OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_OE);
        }

        if ( pSerAMBA->RSR & AMBA_UARTRSR_PE )
        {
            DEBUGMSG (ZONE_WARN, (TEXT("parity\r\n")));
            pSer16550->CommErrors |= CE_RXPARITY;

            // Clear state.
            OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_PE);
        }

        if ( pSerAMBA->RSR & AMBA_UARTRSR_FE )
        {
            DEBUGMSG (ZONE_WARN, (TEXT("frame\r\n")));
            pSer16550->CommErrors |= CE_FRAME;

            // Clear state.
            OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_FE);
        }

        LineEvents |= EV_ERR;
    }

    if ( pSerAMBA->RSR & AMBA_UARTRSR_BE )
    {
        LineEvents |= EV_BREAK;

        // Clear state.
        OUTB(pSerAMBA, pUART_ECR, AMBA_UARTRSR_BE);
    }

    // Let WaitCommEvent know about this error
    if ( LineEvents )
        pSer16550->EventCallback( pSer16550->pMddHead, LineEvents );
}

// The Integrator doesn't give us a delta bits for DCD, DSR, or CTS, so we need to
// figure out of they've changed since the last read, ourselves.
//
__inline
VOID
ReadFR(
       PSER_INFO  pHead
       )
{
    PAMBA_UART_INFO pSerAMBA  = &(((PSER_INFO)pHead)->serAMBA);
    PSER16550_INFO  pSer16550 = &(((PSER_INFO)pHead)->ser16550);
    ULONG Events = 0;
    UCHAR fr = 0;

    try {
        fr = INB(pSerAMBA, pUART_FR);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        fr = 0;
    }

    // For changes, we use callback to evaluate the event
    if ((fr & AMBA_UARTFR_CTS) != (pSerAMBA->FR & AMBA_UARTFR_CTS))
    {
        Events |= EV_CTS;
    }

    if ((fr & AMBA_UARTFR_DSR) != (pSerAMBA->FR & AMBA_UARTFR_DSR))
    {
        Events |= EV_DSR;
    }

    if ((fr & AMBA_UARTFR_DCD) != (pSerAMBA->FR & AMBA_UARTFR_DCD))
    {
        Events |= EV_RLSD;
    }

    // Save the MSR value in a shadow
    pSerAMBA->FR = fr;

    // Clear modem status interrupt.
    //
    OUTB(pSerAMBA, pUART_ICR, 1);

    if ( Events )
        pSer16550->EventCallback( pSer16550->pMddHead, Events );
}

//
// Helper routine to search through a lookup table for a designated
// key.
//
ULONG
LookUpValue(
           ULONG    Key,
           PLOOKUP_TBL pTbl,
           PULONG    pErrorCode
           )
{
    ULONG   index = 0;

    *pErrorCode = 0;

    while ( index < pTbl->Size ) {
        if ( Key == pTbl->Table[index].Key )
            return(pTbl->Table[index].AssociatedValue);

        ++index;
    }

    *pErrorCode = (ULONG)-1;

    return(0);
}



//
// Helper function.  Pass in a baudrate, and the corresponding divisor
// (from the baudtable) is returned.  If no matching baudrate is found
// in baudtable, then return 0.
//
USHORT
DivisorOfRate(
             PVOID   pHead,      // @parm PVOID returned by HWinit.
             ULONG   BaudRate    // @parm     ULONG representing decimal baud rate.    
             )
{
    ULONG   errorcode = 0;
    USHORT  divisor = 0;    

    //divisor = (USHORT)LookUpValue(BaudRate,
    //                              ((PSER_INFO)pHead)->ser16550.pBaudTable, &errorcode);

    divisor = (USHORT) UART_DIVISOR(BaudRate);
 

    return(divisor);
}

//
// This is a reverse lookup table which can be used to determine
// the FIFO trigger level from the 2 bit value stored in the FCR
//
#define HIGH_WATER_SIZE     4
static const
PAIRS    HighWaterPairs[HIGH_WATER_SIZE] = {
    {SERIAL_1_BYTE_HIGH_WATER, 0},
    {SERIAL_4_BYTE_HIGH_WATER, 4},
    {SERIAL_8_BYTE_HIGH_WATER, 8},
    {SERIAL_14_BYTE_HIGH_WATER, 14}
};
static const
LOOKUP_TBL  HighWaterTable = {HIGH_WATER_SIZE, (PAIRS *) HighWaterPairs};

#define CR_NORMAL_INTS (AMBA_UARTCR_RTIE | AMBA_UARTCR_RIE | AMBA_UARTCR_MSIE | AMBA_UARTCR_UARTEN)

// Routine to clear any pending interrupts.  Called from Init and PostInit
// to make sure we atart out in a known state.
VOID
ClearPendingInts(
       PVOID   pHead // @parm PVOID returned by HWinit.
       )
{
    PSER16550_INFO  pSer16550 = &(((PSER_INFO)pHead)->ser16550);
    PAMBA_UART_INFO pSerAMBA  = &(((PSER_INFO)pHead)->serAMBA);

    EnterCriticalSection(&(pSer16550->RegCritSec));

    try {
        pSerAMBA->IIR = INB(pSerAMBA, pUART_IIR); 
        while ((pSerAMBA->IIR & AMBA_UARTIIR_RTIS) ||
               (pSerAMBA->IIR & AMBA_UARTIIR_TIS)  ||
               (pSerAMBA->IIR & AMBA_UARTIIR_RIS)  ||
               (pSerAMBA->IIR & AMBA_UARTIIR_MIS))
        {
            DEBUGMSG (ZONE_INIT, (TEXT("!!IIR %X\r\n"), pSerAMBA->IIR));

            // Clear ints.
            //
            // Read FIFO clean to deassert RIS and RTIS.
            while(!(INB(pSerAMBA, pUART_FR) & AMBA_UARTFR_RXFE))
                INB(pSerAMBA, pUART_DR);

            // Clear modem status interrupts.
            OUTB(pSerAMBA, pUART_ICR, 1);

        }    
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        DEBUGMSG (ZONE_ERROR,(TEXT("-SL_PostInit, 0x%X - ERROR\r\n"), pHead));
        // Just fall through & release CritSec
    }

    LeaveCriticalSection(&(pSer16550->RegCritSec));
}

//
/////////////////// Start of exported entrypoints ////////////////
//

//
// @doc OEM 
// @func PVOID | SL_Open | Configures 16550 for default behaviour.
//
VOID
SL_Open(
       PVOID   pHead // @parm PVOID returned by HWinit.
       )
{
    PSER16550_INFO  pSer16550 = &(((PSER_INFO)pHead)->ser16550);
    PAMBA_UART_INFO pSerAMBA  = &(((PSER_INFO)pHead)->serAMBA);

    DEBUGMSG (ZONE_OPEN,
              (TEXT("+SL_Open 0x%X\r\n"), pHead));

    // If the device is already open, all we do is increment count
    if ( pSer16550->OpenCount++ ) {
        DEBUGMSG (ZONE_OPEN,
                  (TEXT("-SL_Open 0x%X (%d opens)\r\n"),
                   pHead, pSer16550->OpenCount));
        return ;
    }

    pSerAMBA->RSR   = 0;
    pSerAMBA->LCR_H = 0;
    pSerAMBA->LCR_M = 0;
    pSerAMBA->LCR_L = 0;
    pSerAMBA->CR    = 0;
    pSerAMBA->FR    = 0;
    pSerAMBA->IIR   = 0;

    pSer16550->DroppedBytes = 0;
    pSer16550->CTSFlowOff = FALSE;  // Not flowed off yet
    pSer16550->DSRFlowOff = FALSE;  // Not flowed off yet
    pSer16550->CommErrors   = 0;
    pSer16550->ModemStatus  = 0;

    EnterCriticalSection(&(pSer16550->RegCritSec));
    try {


        DEBUGMSG (ZONE_OPEN,
                  (TEXT("SL_Open Setting DCB parameters\r\n")));

        // Get defaults from the DCB structure
        SL_SetBaudRate( pHead, pSer16550->dcb.BaudRate );
        SL_SetByteSize( pHead, pSer16550->dcb.ByteSize );
        SL_SetStopBits( pHead, pSer16550->dcb.StopBits );
        SL_SetParity(   pHead, pSer16550->dcb.Parity );

        // Set default data bits.
        pSerAMBA->LCR_H = (AMBA_UARTLCR_H_WLEN_8 | AMBA_UARTLCR_H_FEN);
        OUTB(pSerAMBA, pUART_LCR_H, pSerAMBA->LCR_H);

        // For CE 3.0, we are still supporting
        // the old style MDDs, and they don't call our PostInit, which
        // needs to happen sometime prior to this.  So for now, we go ahead
        // ahead and clear out interrupts one last time.  In 4.0, we can
        // kill the old serial MDD and assume that everyone uses the new
        // MDD and calls post init.  
        SL_PostInit(pHead);

        ReadFR((PSER_INFO)pHead);
        ReadRSR((PSER_INFO)pHead);

        // Enable ints and UART.
        pSerAMBA->CR = (CR_NORMAL_INTS | AMBA_UARTCR_TIE);
        OUTB(pSerAMBA, pUART_CR, pSerAMBA->CR);
    }
    except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        // Just get out of here.
    }

⌨️ 快捷键说明

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