com16550.c

来自「WinCE 3.0 BSP, 包含Inter SA1110, Intel_815」· C语言 代码 · 共 781 行 · 第 1/2 页

C
781
字号
/*++
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.
Copyright (c) 1995-2000 Microsoft Corporation.  All rights reserved.

Module Name:  

Abstract:  

Notes: 
--*/

// The bootargs logic is platform specific.  I've left it in here as an example of how the bootargs might
// be used in an actual platform.   However, this particular code is being built in the public tree as
// a platform independent driver and thus can't do this.  One possible feature would be to have this info
// passed to the driver via a registry entry or other platform independent API.
// #define EXAMINE_BOOTARGS

#include <windows.h>
#include <types.h>
#include <ceddk.h>
#include <memory.h>
#include <serhw.h>
#include <ser16550.h>
#include <hw16550.h>
#include <nkintr.h>
#include <devload.h>

#ifdef EXAMINE_BOOTARGS
    #include <bootarg.h>
    #include <oalintr.h>
    #include <pc.h>
#endif

#include <com16550.h>

#undef ZONE_INIT
#include <serdbg.h>

// #define DBGMSG
#define DBGMSG NKDbgPrintfW

#define BAUD_TABLE_SIZE 23
static const
PAIRS   BaudPairs[BAUD_TABLE_SIZE] =    {
    {50,        2307},
    {75,        1538},
    {110,       1049},
    {135,       858},
    {150,       769},
    {300,       384},
    {600,       192},
    {1200,      96},
    {1800,      64},
    {2000,      58},
    {2400,      48},
    {3600,      32},
    {4800,      24},
    {7200,      16},
    {9600,      12},
    {12800,     9},
    {14400,     8},
    {19200,     6},
    {23040,     5},
    {28800,     4},
    {38400,     3},
    {57600,     2},
    {115200,    1}
};

static const LOOKUP_TBL  BaudTable = {BAUD_TABLE_SIZE, (PAIRS *) BaudPairs};

// Miscellaneous internal routines.
PUCHAR
static
Ser_InternalMapRegisterAddresses(
                                ULONG   HWAddress,
                                ULONG   Size
                                )
{
    PUCHAR              ioPortBase; 
    ULONG               inIoSpace = 1;
    PHYSICAL_ADDRESS    ioPhysicalBase = { HWAddress, 0};


    DEBUGMSG(ZONE_FUNCTION, 
             (TEXT("Ser_InternalMapRegisterAddresses : HalTranslateBusAddress\r\n")));
    if ( HalTranslateBusAddress(Isa, 0, ioPhysicalBase, &inIoSpace, 
                                &ioPhysicalBase) ) {
        DEBUGMSG(1, 
                 (TEXT("Ser_InternalMapRegisterAddresses : HalTranslateBusAddress - OK\r\n")));
        if ( !inIoSpace ) {
            DEBUGMSG(1, 
                     (TEXT("Ser_InternalMapRegisterAddresses : ! IO Space\r\n")));
            if ( (ioPortBase = (PUCHAR)MmMapIoSpace(ioPhysicalBase, 
                                                    Size, FALSE)) == NULL ) {
                // We may as well not continue
                DEBUGMSG(ZONE_INIT | ZONE_ERROR, 
                         (TEXT("Error mapping I/O Ports\r\n")));
                return (NULL);
            }
        } else {
            DEBUGMSG(1, 
                     (TEXT("Ser_InternalMapRegisterAddresses : IO Space\r\n")));
            ioPortBase = (PUCHAR)ioPhysicalBase.LowPart;
        }
    } else {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR, 
                 (TEXT("Error translating I/O Ports.\r\n")));
        return (NULL);
    }

    DEBUGMSG(ZONE_FUNCTION, 
             (TEXT("Ser_InternalMapRegisterAddresses : %d\r\n"),
              ioPortBase ));
    return (ioPortBase);
}

static
BOOL
SerSetIRBaudRate(
                PSER_INFO   pHWHead,
                ULONG baud     // @parm     UINT16 what is the baud rate
                )
{
    DEBUGMSG (ZONE_INIT, (TEXT("Serial set IR Baud %d\r\n"),
                          baud));

    return (TRUE);
}

/*
 *  NOTE : The caller should have set pHWHead->fIRMode.  It is not
 * set here, since power on/off may need to temporarily disable
 * the intefaces without actually overwriting the current recorded
 * mode.
 */
static
void
SerSetOutputMode(
                PSER_INFO   pHWHead,
                BOOL UseIR,     // @parm     BOOL Should we use IR interface
                BOOL Use9Pin    // @parm     BOOL Should we use Wire interface
                )
{
    // If you support IR, here you need to set the interface to either IR mode
    // or normal serial. Note that it is possible for both BOOls to
    // be false (i.e. power down), but never for both to be TRUE.
}



/*++
*******************************************************************************
Routine:

    Ser_GetRegistryData

Description:

    Take the registry path provided to COM_Init and use it to find this 
    requested comm port's DeviceArrayIndex, teh IOPort Base Address, and the
   Interrupt number.
   
Arguments:

    LPCTSTR regKeyPath  the registry path passed in to COM_Init.

Return Value:

    -1 if there is an error.

*******************************************************************************
--*/
BOOL
Ser_GetRegistryData(PSER_INFO pHWHead, LPCTSTR regKeyPath)
{
#define GCI_BUFFER_SIZE 256   

    LONG    regError;
    HKEY    hKey;
    DWORD   dwDataSize = GCI_BUFFER_SIZE;

    DEBUGMSG(ZONE_INIT, (TEXT("Try to open %s\r\n"), regKeyPath));

    // We've been handed the name of a key in the registry that was generated
    // on the fly by device.exe.  We're going to open that key and pull from it
    // a value that is the name of this serial port's real key.  That key
    // will have the DeviceArrayIndex that we're trying to find.  
    hKey = OpenDeviceKey(regKeyPath);
    if ( hKey == NULL ) {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR,
                 (TEXT("Failed to open device key\r\n")));
        return ( FALSE );        
    }

    // Okay, we're finally ready to try and load our registry data.
    dwDataSize = PC_REG_DEVINDEX_VAL_LEN;
    regError = RegQueryValueEx(
                              hKey, 
                              PC_REG_DEVINDEX_VAL_NAME, 
                              NULL, 
                              NULL,
                              (LPBYTE)(&pHWHead->dwDevIndex), 
                              &dwDataSize);

    if ( regError == ERROR_SUCCESS ) {
        dwDataSize = PC_REG_SYSINTR_VAL_LEN;
        regError = RegQueryValueEx(
                                  hKey, 
                                  PC_REG_SYSINTR_VAL_NAME, 
                                  NULL, 
                                  NULL,
                                  (LPBYTE)(&pHWHead->dwSysIntr), 
                                  &dwDataSize);
    }

    if ( regError == ERROR_SUCCESS ) {
        dwDataSize = PC_REG_IOBASE_VAL_LEN;
        regError = RegQueryValueEx(
                                  hKey, 
                                  PC_REG_IOBASE_VAL_NAME, 
                                  NULL, 
                                  NULL,
                                  (LPBYTE)(&pHWHead->dwIOBase), 
                                  &dwDataSize);
    }

    if ( regError == ERROR_SUCCESS ) {
        dwDataSize = PC_REG_IOLEN_VAL_LEN;
        regError = RegQueryValueEx(
                                  hKey, 
                                  PC_REG_IOLEN_VAL_NAME, 
                                  NULL, 
                                  NULL,
                                  (LPBYTE)(&pHWHead->dwIOLen), 
                                  &dwDataSize);
    }

    RegCloseKey (hKey);

    if ( regError != ERROR_SUCCESS ) {
        DEBUGMSG(ZONE_INIT | ZONE_ERROR,
                 (TEXT("Failed to get serial registry values, Error 0x%X\r\n"),
                  regError));
        return ( FALSE );
    }

    DEBUGMSG (1|ZONE_INIT,
              (TEXT("SerInit - Devindex %d, SysIntr %d, IOB %X, IOLen %X \r\n"),
               pHWHead->dwDevIndex, pHWHead->dwSysIntr, pHWHead->dwIOBase, pHWHead->dwIOLen));
    return ( TRUE ); 
}



/*
 @doc OEM 
 @func PVOID | SerInit | Initializes device identified by argument.
 *  This routine sets information controlled by the user
 *  such as Line control and baud rate. It can also initialize events and
 *  interrupts, thereby indirectly managing initializing hardware buffers.
 *  Exported only to driver, called only once per process.
 *
 @rdesc The return value is a PVOID to be passed back into the HW
 dependent layer when HW functions are called.
 */
static
PVOID
SerInit(
       ULONG   Identifier, // @parm Device identifier.
       PVOID   pMddHead,   // @parm First argument to mdd callbacks.
       PHWOBJ  pHWObj      // @parm Pointer to our own HW OBJ for this device
       )
{
    PSER_INFO   pHWHead;

#ifdef EXAMINE_BOOTARGS    
    PVOID       *ppBootArgs = NULL;  // Pointer to pointer to bootargs.
    PHYSICAL_ADDRESS PhysicalAddress = {0,0};
#endif    

    // Note on defaults.  While the PC typcially considers COM1 to be at 
    // 3F8, irq4 and COM2 to be 2F8, irq3, NKPC uses COM1 internally for the
    // debugger.  So, when NK tells me to start "COM1" it really means the
    // first one that's available, which is what the PC calls COM2.  Confused?
    // The end result is that COM1 on NK is what the PC considers to be COM2.
    // But wait, there's more.  On a Puzzle, the debugger is on COM2 and the
    // COM1 for NK is ACTUALLY COM1.  So PCs need 2F8 for their port base
    // and Puzzles need 3F8.


    // Allocate for our main data structure and one of it's fields.
    pHWHead = (PSER_INFO)LocalAlloc( LMEM_ZEROINIT|LMEM_FIXED ,
                                     sizeof(SER_INFO) );
    if ( !pHWHead )
        return( NULL );

    if ( ! Ser_GetRegistryData(pHWHead, (LPCTSTR)Identifier) ) {
        DEBUGMSG (ZONE_INIT|ZONE_ERROR,
                  (TEXT("SerInit - Unable to read registry data.  Failing Init !!! \r\n")));
        goto ALLOCFAILED;
    }

    pHWHead->pBaseAddress = Ser_InternalMapRegisterAddresses(pHWHead->dwIOBase, 
                                                             pHWHead->dwIOLen);

#ifdef EXAMINE_BOOTARGS        
    // Allocate a pointer to our bootargs since they may indicate that we don't have
    // access to the hardware resource.

    // First, map the bootargs pointer itself.  Note that I'm reading/writing
    // directly on the physical address.  I can do this since I know this is CEPC and 
    // know the adress is not in IO space.  For OEM platforms you would want to do
    // HalTranslateBusAddress first.
    PhysicalAddress.LowPart = BOOT_ARG_PTR_LOCATION & ~0x80000000;

    if ( ppBootArgs = MmMapIoSpace(PhysicalAddress, sizeof( PVOID ), TRUE ) ) {
        DEBUGMSG (ZONE_INIT,
                  (TEXT("SerInit - ppBootArgs (%X) at %X\r\n"), 
                   PhysicalAddress.LowPart, ppBootArgs ));
    } else {
        DEBUGMSG (ZONE_INIT | ZONE_ERROR,
                  (TEXT("SerInit - ppBootArgs failure at %X\r\n"), PhysicalAddress.LowPart ));
        goto ALLOCFAILED;
    }

    // Now map the bootargs structure itself
    PhysicalAddress.LowPart = (DWORD) *ppBootArgs;
    if ( pHWHead->pBootArgs = MmMapIoSpace(PhysicalAddress, sizeof(BOOT_ARGS), TRUE ) ) {
        DEBUGMSG (ZONE_INIT,
                  (TEXT("SerInit - pBootArgs (%X) at %X\r\n"), 
                   PhysicalAddress.LowPart, pHWHead->pBootArgs ));
    } else {
        DEBUGMSG (ZONE_INIT | ZONE_ERROR,
                  (TEXT("SerInit - pBootArgs failure at %X\r\n"), 
                   (DWORD)PhysicalAddress.LowPart));
        goto ALLOCFAILED;
    }

    // We no longer need ppBootArgs
    MmUnmapIoSpace( ppBootArgs, sizeof(PVOID) );
#endif // EXAMINE_BOOTARGS

    pHWHead->pMddHead     = pMddHead;
    pHWHead->pHWObj = pHWObj;
    pHWHead->cOpenCount   = 0;

    // Legacy - We have 2 identical fields becausw registry used to contain IRQ
    pHWHead->pHWObj->dwIntID = pHWHead->dwSysIntr;
    DEBUGMSG (1|ZONE_INIT,
              (TEXT("SerInit - SYSINTR %d\r\n"),  pHWHead->pHWObj->dwIntID));

    // Set up our Comm Properties data    
    pHWHead->CommProp.wPacketLength       = 0xffff;
    pHWHead->CommProp.wPacketVersion     = 0xffff;
    pHWHead->CommProp.dwServiceMask      = SP_SERIALCOMM;
    pHWHead->CommProp.dwReserved1         = 0;
    pHWHead->CommProp.dwMaxTxQueue        = 16;
    pHWHead->CommProp.dwMaxRxQueue        = 16;
    pHWHead->CommProp.dwMaxBaud       = BAUD_115200;
    pHWHead->CommProp.dwProvSubType      = PST_RS232;
    pHWHead->CommProp.dwProvCapabilities =
    PCF_DTRDSR | PCF_RLSD | PCF_RTSCTS |
    PCF_SETXCHAR |
    PCF_INTTIMEOUTS |
    PCF_PARITY_CHECK |
    PCF_SPECIALCHARS |
    PCF_TOTALTIMEOUTS |
    PCF_XONXOFF;
    pHWHead->CommProp.dwSettableBaud      =
    BAUD_075 | BAUD_110 | BAUD_150 | BAUD_300 | BAUD_600 |
    BAUD_1200 | BAUD_1800 | BAUD_2400 | BAUD_4800 |
    BAUD_7200 | BAUD_9600 | BAUD_14400 |
    BAUD_19200 | BAUD_38400 | BAUD_56K | BAUD_128K |
    BAUD_115200 | BAUD_57600 | BAUD_USER;
    pHWHead->CommProp.dwSettableParams    =
    SP_BAUD | SP_DATABITS | SP_HANDSHAKING | SP_PARITY |
    SP_PARITY_CHECK | SP_RLSD | SP_STOPBITS;
    pHWHead->CommProp.wSettableData       =
    DATABITS_5 | DATABITS_6 | DATABITS_7 | DATABITS_8;
    pHWHead->CommProp.wSettableStopParity =
    STOPBITS_10 | STOPBITS_20 |
    PARITY_NONE | PARITY_ODD | PARITY_EVEN | PARITY_SPACE |
    PARITY_MARK;

    pHWHead->fIRMode  = FALSE;   // Select wired by default

#ifdef EXAMINE_BOOTARGS

⌨️ 快捷键说明

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