wince 2440串口驱动PDD分析(作者:wogoyixikexie@gliet)

                   wince 2440串口驱动PDD分析(作者:wogoyixikexie@gliet)

    经过前面的MDD分析可知,wince串口的接口函数com_init,Com_Open等函数是放在MDD中的,应用程序直接和MDD打交道,然后把需求传到中间层(介于MDD和PDD之间)中间层再调用PDD的函数(和硬件相关的函数)来实现。PDD是硬件相关的操作,如果需要增加串口个数或者外扩串口芯片都需要在这个PDD中实现。Come on!

    在执行com_init函数的时候会间接调用到

  1. //根据串口编号创建对象,如果要增加串口个数要修改这个函数,增加case即可。
  2. CSerialPDD * CreateSerialObject(LPTSTR lpActivePath, PVOID pMdd,PHWOBJ pHwObj, DWORD DeviceArrayIndex)
  3. {
  4.     CSerialPDD * pSerialPDD = NULL;
  5.     switch (DeviceArrayIndex) {
  6.       case 0://和物理串口对应
  7.         pSerialPDD = new CPdd2440Serial1(lpActivePath,pMdd, pHwObj);//在PDD实现
  8.         break;
  9.       case 1:
  10.         pSerialPDD = new CPdd2440Serial2(lpActivePath,pMdd, pHwObj);
  11.         break;
  12.     }
  13.     if (pSerialPDD && !pSerialPDD->Init()) {
  14.         delete pSerialPDD;
  15.         pSerialPDD = NULL;
  16.     }    
  17.     return pSerialPDD;
  18. }

____________________________________________________________________________________________

if (pSerialPDD && !pSerialPDD->Init()) ——读取注册表串口部分

 

  1. BOOL CPdd2440Uart::Init()
  2. {
  3.     if ( CSerialPDD::Init() && IsKeyOpened() && m_XmitFlushDone!=NULL) { 
  4.         // IST Setup .--------Get IRQ forom regedit
  5.         DDKISRINFO ddi;
  6.         if (GetIsrInfo(&ddi)!=ERROR_SUCCESS) {
  7.             return FALSE;
  8.         }
  9.         m_dwSysIntr = ddi.dwSysintr;
  10.         if (m_dwSysIntr !=  MAXDWORD && m_dwSysIntr!=0 ) 
  11.             m_hISTEvent= CreateEvent(0,FALSE,FALSE,NULL);
  12.         
  13.         if (m_hISTEvent!=NULL)
  14.             InterruptInitialize(m_dwSysIntr,m_hISTEvent,0,0);
  15.         else
  16.             return FALSE;
  17.         
  18.         // Get Device Index.
  19.         if (!GetRegValue(PC_REG_DEVINDEX_VAL_NAME, (PBYTE)&m_dwDevIndex, PC_REG_DEVINDEX_VAL_LEN)) {
  20.             m_dwDevIndex = 0;
  21.         }
  22.         if (!GetRegValue(PC_REG_SERIALWATERMARK_VAL_NAME,(PBYTE)&m_dwWaterMark,sizeof(DWORD))) {
  23.             m_dwWaterMark = 8;
  24.         }
  25.         if (!GetRegValue(PC_REG_2440UART_INTBIT_VAL_NAME,(PBYTE)&m_dwIntShift,sizeof(DWORD))) {
  26.             RETAILMSG(1,(TEXT("Registery does not have %s set. Drivers fail!!!/r/n"),PC_REG_2440UART_INTBIT_VAL_NAME));
  27.             m_dwIntShift =0;
  28.             return FALSE;
  29.         }
  30.         if (!GetRegValue(PC_REG_2440UART_IST_TIMEOUTS_VAL_NAME,(PBYTE)&m_dwISTTimeout, PC_REG_2440UART_IST_TIMEOUTS_VAL_LEN)) {
  31.             m_dwISTTimeout = INFINITE;
  32.         }
  33.         if (!MapHardware() || !CreateHardwareAccess()) {
  34.             return FALSE;
  35.         }
  36.         
  37.         return TRUE;        
  38.     }
  39.     return FALSE;
  40. }

现在来看看它里面分配的对象,三星的BSP刚好只有两个串口驱动,另外一个做了调试串口。

  1. // CPdd2440Serial1 is only use for UART 0 which 
  2. // RTS & CTS is use GPH0 GPH1
  3. // DTR & DSR is USE GPD0 GPD1
  4. class CPdd2440Serial1 : public CPdd2440Uart {
  5. public:
  6.     CPdd2440Serial1(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj)
  7.         : CPdd2440Uart(lpActivePath, pMdd, pHwObj)
  8.         {
  9.         m_pIOPregs = NULL;
  10.         m_fIsDSRSet = FALSE;
  11.     }
  12.     ~CPdd2440Serial1() {
  13.         if (m_pIOPregs!=NULL)
  14.             MmUnmapIoSpace((PVOID)m_pIOPregs,0);
  15.     }
  16.     virtual BOOL Init() {
  17.         PHYSICAL_ADDRESS    ioPhysicalBase = { S3C2440A_BASE_REG_PA_IOPORT, 0};
  18.         ULONG               inIoSpace = 0;
  19.         if (TranslateBusAddr(m_hParent,Internal,0, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) {
  20.             // Map it if it is Memeory Mapped IO.
  21.             m_pIOPregs = (S3C2440A_IOPORT_REG *)MmMapIoSpace(ioPhysicalBase, sizeof(S3C2440A_IOPORT_REG),FALSE);
  22.         }
  23.         if (m_pIOPregs) {
  24.             DDKISRINFO ddi;
  25.             if (GetIsrInfo(&ddi)== ERROR_SUCCESS && 
  26.                     KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &ddi.dwIrq, sizeof(UINT32), &ddi.dwSysintr, sizeof(UINT32), NULL))
  27.             {   
  28.                 RegSetValueEx(DEVLOAD_SYSINTR_VALNAME,REG_DWORD,(PBYTE)&ddi.dwSysintr, sizeof(UINT32));
  29.             }
  30.             else
  31.                 return FALSE;
  32.             m_pDTRPort = (volatile ULONG *)&(m_pIOPregs->GPDDAT);
  33.             m_pDSRPort = (volatile ULONG *)&(m_pIOPregs->GPDDAT);
  34.             m_dwDTRPortNum = 0;
  35.             m_dwDSRPortNum = 1;
  36.             m_pIOPregs->GPHCON &= ~(0x3<<0 | 0x3<<2 | 0x3<<4 | 0x3<<6 );//tx,rx,rts,cts
  37.             m_pIOPregs->GPHCON |=  (0x2<<0 | 0x2<<2 | 0x2<<4 | 0x2<<6 ); 
  38.             m_pIOPregs->GPHCON |= (0x2<<0 | 0x2<<2);
  39.             m_pIOPregs->GPHUP  |= 0xf;
  40.             m_pIOPregs->GPDCON &= ~(0x3<<0 | 0x3<<2);//dtr,dsr
  41.             m_pIOPregs->GPDCON |= (0x1<<0 | 0x0<<2);
  42.             m_pIOPregs->GPDUP  |= 0x3;
  43.             return CPdd2440Uart::Init();
  44.         }
  45.         return FALSE;
  46.     };
  47.     virtual void    SetDefaultConfiguration() {
  48.         CPdd2440Uart::SetDefaultConfiguration();
  49.     }
  50.     virtual BOOL    InitModem(BOOL bInit) {
  51.         SetDTR(bInit);
  52.         return CPdd2440Uart::InitModem(bInit);
  53.     }
  54.     virtual ULONG   GetModemStatus() {
  55.         ULONG ulReturn = CPdd2440Uart::GetModemStatus();
  56.         ULONG ulEvent = 0;
  57.         m_HardwareLock.Lock();
  58.         BOOL fIsDSRSet = (((*m_pDSRPort) & (1<<m_dwDSRPortNum))==0);
  59.         if (fIsDSRSet != m_fIsDSRSet) {
  60.             ulEvent |= EV_DSR | EV_RLSD;
  61.         }
  62.         ulReturn |= (fIsDSRSet?(MS_DSR_ON|MS_RLSD_ON):0);
  63.         m_fIsDSRSet = fIsDSRSet;
  64.         m_HardwareLock.Unlock();
  65.         if (ulEvent!=0)
  66.             EventCallback(ulEvent,ulReturn);
  67.         return ulReturn;
  68.     }
  69.     virtual void    SetDTR(BOOL bSet) {
  70.         if (bSet)
  71.             *m_pDTRPort &= ~(1<<m_dwDTRPortNum);
  72.         else
  73.             *m_pDTRPort |= (1<<m_dwDTRPortNum);
  74.     };
  75. private:
  76.     volatile S3C2440A_IOPORT_REG * m_pIOPregs; 
  77.     volatile ULONG *    m_pDTRPort;
  78.     DWORD               m_dwDTRPortNum;
  79.     volatile ULONG *    m_pDSRPort;
  80.     DWORD               m_dwDSRPortNum;
  81.     BOOL                m_fIsDSRSet;
  82. };
  83. —————————————————————————————
  84. // CPdd2440Serial2 is only use for UART 2 which 
  85. // nIrDATXDEN use GPB1
  86. class CPdd2440Serial2 : public CPdd2440Uart {
  87. public:
  88.     CPdd2440Serial2(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj)
  89.         : CPdd2440Uart(lpActivePath, pMdd, pHwObj)
  90.         {
  91.         m_pIOPregs = NULL;
  92.     }
  93.     ~CPdd2440Serial2() {
  94.         if (m_pIOPregs!=NULL)
  95.             MmUnmapIoSpace((PVOID)m_pIOPregs,0);
  96.     }
  97.     virtual BOOL Init() {
  98.         PHYSICAL_ADDRESS    ioPhysicalBase = { S3C2440A_BASE_REG_PA_IOPORT, 0};
  99.         ULONG               inIoSpace = 0;
  100.         if (TranslateBusAddr(m_hParent,Internal,0, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) {
  101.             // Map it if it is Memeory Mapped IO.
  102.             m_pIOPregs =(S3C2440A_IOPORT_REG *) MmMapIoSpace(ioPhysicalBase, sizeof(S3C2440A_IOPORT_REG),FALSE);
  103.         }
  104.         if (m_pIOPregs) {
  105.             DDKISRINFO ddi;
  106.             if (GetIsrInfo(&ddi)== ERROR_SUCCESS && 
  107.                     KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &ddi.dwIrq, sizeof(UINT32), &ddi.dwSysintr, sizeof(UINT32), NULL))
  108.             {   
  109.                 RegSetValueEx(DEVLOAD_SYSINTR_VALNAME,REG_DWORD,(PBYTE)&ddi.dwSysintr, sizeof(UINT32));
  110.             }
  111.             else
  112.                 return FALSE;
  113.             m_pIOPregs->GPHCON &= ~(0x3<<12 | 0x3<<14); // clear rxd2 txd2
  114.             m_pIOPregs->GPHCON |= (0x2<<12 | 0x2<<14); //enabled rxd2 txd2
  115.             m_pIOPregs->GPHUP |= 0xc0;//disable pull up
  116.         
  117.             //m_pIOPregs->GPBCON &= ~(0x3<<2);  //GPB1 -> nIrDATXDEN
  118.             //m_pIOPregs->GPBCON |=  (0x1<<2); 
  119.             //m_pIOPregs->GPBUP  |=  (0x1<<1);
  120.             //m_pIOPregs->GPBDAT &= ~(0x1<<1);
  121.             return CPdd2440Uart::Init();
  122.         }
  123.         return FALSE;
  124.     };
  125.     virtual void    SetDefaultConfiguration() {
  126.         CPdd2440Uart::SetDefaultConfiguration();
  127.     }
  128.     virtual ULONG   GetModemStatus() {
  129.         return (CPdd2440Uart::GetModemStatus() | MS_CTS_ON);
  130.     }
  131.     virtual void    Rx_Pause(BOOL bSet) {
  132.         if(bSet)
  133.             m_pIOPregs->GPHCON = (m_pIOPregs->GPHCON & ~(0x3<<14)) | 0x0<<14;
  134.         else    
  135.             m_pIOPregs->GPHCON = (m_pIOPregs->GPHCON & ~(0x3<<14)) | 0x2<<14;
  136.     }
  137.     volatile S3C2440A_IOPORT_REG * m_pIOPregs; 
  138. };

 

——鉴于我的C++水平,我就先沉默了。看来要花点时间学习一下。

  1. // I must learn well C++ 
  2. //前面刚有人回帖说做驱动工程师不用C++,现在郁闷了吧。
  3. //对于这个KernelIoControl要有新的认识才行
  4. CReg2440Uart::CReg2440Uart(PULONG pRegAddr)
  5. :   m_pReg(pRegAddr)
  6. {
  7.     m_fIsBackedUp = FALSE;
  8.     PROCESSOR_INFO procInfo;
  9.     DWORD dwBytesReturned;
  10.     //IOCTL_PROCESSOR_INFORMATION? Why do use it?
  11.     if (!KernelIoControl(IOCTL_PROCESSOR_INFORMATION, NULL, 0, &procInfo, sizeof(PROCESSOR_INFO), &dwBytesReturned))
  12.     {
  13.         m_s3c2440_pclk = DEFAULT_S3C2440A_PCLK;
  14.         RETAILMSG(TRUE, (TEXT("WARNING: CReg2440Uart::CReg2440Uart failed to obtain processor frequency - using default value (%d)./r/n"), m_s3c2440_pclk)); 
  15.     }
  16.     else
  17.     {
  18.         m_s3c2440_pclk = procInfo.dwClockSpeed;
  19.         RETAILMSG(TRUE, (TEXT("INFO: CReg2440Uart::CReg2440Uart using processor frequency reported by the OAL (%d)./r/n"), m_s3c2440_pclk)); 
  20.     }
  21. }

现在来看看IOCTL_PROCESSOR_INFORMATION这东西到底是做什么的,在BSP下oal_ioctl_tab.h有

{ IOCTL_PROCESSOR_INFORMATION,              0,  OALIoCtlProcessorInfo       },
也就是说通过KernelIoControl执行了OALIoCtlProcessorInfo  这个函数。在别的地方也发现了IOCTL_PROCESSOR_INFORMATION

smdk2440_ARMV4I/cesysgen/oak/inc/pkfuncs.h(327)

#define IOCTL_PROCESSOR_INFORMATION   CTL_CODE(FILE_DEVICE_HAL, 25, METHOD_BUFFERED, FILE_ANY_ACCESS)

现在来看看这个CTL_CODE是什么东西,看PB帮助文档如下:

This macro creates a unique system I/O control code (IOCTL).

#define CTL_CODE(DeviceType, Function, Method, Access) (
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)

Parameters

DeviceType
Defines the type of device for the given IOCTL.

This parameter can be no bigger than a WORD value.

The values used by Microsoft are in the range 0-32767; the values 32768-65535 are reserved for use by OEMs and IHVs.

The following device types are specific to Windows CE:

  • FILE_DEVICE_HAL
  • FILE_DEVICE_CONSOLE
  • FILE_DEVICE_PSL
  • FILE_DEVICE_SERVICE

...更多信息请看PB帮助ms-help://MS.WindowsCE.500/wcehardware5/html/wce50lrfCTLCODE.htm


 

现在来看看OALIoCtlProcessorInfo 这个函数是怎么实现的。

C:/WINCE500/PLATFORM/COMMON/SRC/COMMON/IOCTL/procinfo.c

 

 

 

  1. //------------------------------------------------------------------------------
  2. //
  3. //  Function:  OALIoCtlProcessorInformation
  4. //
  5. //  Implements the IOCTL_PROCESSOR_INFORMATION handler.
  6. //
  7. BOOL OALIoCtlProcessorInfo(
  8.     UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer, 
  9.     UINT32 outSize, UINT32 *pOutSize
  10. ) {
  11.     BOOL rc = FALSE;
  12.     PROCESSOR_INFO *pInfo = (PROCESSOR_INFO*)pOutBuffer;
  13.     UINT32 length1, length2, length3;
  14.     OALMSG(OAL_FUNC, (L"+OALIoCtlProcessorInfo(...)/r/n"));
  15.     // Set required/returned size if pointer isn't NULL
  16.     if (pOutSize != NULL) *pOutSize = sizeof(PROCESSOR_INFO);
  17.     
  18.     // Validate inputs
  19.     if (pOutBuffer == NULL || outSize < sizeof(PROCESSOR_INFO)) {
  20.         NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
  21.         OALMSG(OAL_WARN, (
  22.             L"WARN: OALIoCtlProcessorInfo: Buffer too small/r/n"
  23.         ));
  24.         goto cleanUp;
  25.     }
  26.     // Verify OAL lengths
  27.     length1 = (NKwcslen(g_oalIoCtlProcessorCore) + 1) * sizeof(WCHAR);
  28.     if (length1 > sizeof(pInfo->szProcessCore)) {
  29.         OALMSG(OAL_ERROR, (
  30.             L"ERROR:OALIoCtlProcessorInfo: Core value too big/r/n"
  31.         ));
  32.         goto cleanUp;
  33.     }
  34.     length2 = (NKwcslen(g_oalIoCtlProcessorName) + 1) * sizeof(WCHAR);
  35.     if (length2 > sizeof(pInfo->szProcessorName)) {
  36.         OALMSG(OAL_ERROR, (
  37.             L"ERROR:OALIoCtlProcessorInfo: Name value too big/r/n"
  38.         ));
  39.         goto cleanUp;
  40.     }
  41.     length3 = (NKwcslen(g_oalIoCtlProcessorVendor) + 1) * sizeof(WCHAR);
  42.     if (length3 > sizeof(pInfo->szVendor)) {
  43.         OALMSG(OAL_ERROR, (
  44.             L"ERROR:OALIoCtlProcessorInfo: Vendor value too big/r/n"
  45.         ));
  46.         goto cleanUp;
  47.     }
  48.     // Copy in processor information    
  49.     pInfo->wVersion = 1;
  50.     memset(pInfo, 0, sizeof(PROCESSOR_INFO));
  51.     memcpy(pInfo->szProcessCore, g_oalIoCtlProcessorCore, length1);
  52.     memcpy(pInfo->szProcessorName, g_oalIoCtlProcessorName, length2);
  53.     memcpy(pInfo->szVendor, g_oalIoCtlProcessorVendor, length3);
  54.     pInfo->dwInstructionSet = g_oalIoCtlInstructionSet;
  55.     pInfo->dwClockSpeed  = g_oalIoCtlClockSpeed;
  56.     // Indicate success
  57.     rc = TRUE;
  58. cleanUp:
  59.     OALMSG(OAL_FUNC, (L"-OALIoCtlProcessorInfo(rc = %d)/r/n", rc));
  60.     return rc;
  61. }
  62. //---------------------------------???好复杂,看不懂。等下再看了。

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

现在来看点实际点的东西吧。

——我发现这个串口物理中断转换成逻辑(系统)中断有点奇怪。

DDKISRINFO ddi;
            if (GetIsrInfo(&ddi)== ERROR_SUCCESS &&
                    KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &ddi.dwIrq, sizeof(UINT32), &ddi.dwSysintr, sizeof(UINT32), NULL))
            {  
                RegSetValueEx(DEVLOAD_SYSINTR_VALNAME,REG_DWORD,(PBYTE)&ddi.dwSysintr, sizeof(UINT32));
            }

 

——————找到GetIsrInfo函数:C:/WINCE500/PUBLIC/COMMON/OAK/INC/cregedit.h(99)

 DWORD   GetIsrInfo( DDKISRINFO* pddi )
    {
        if( pddi && m_hDevKey )
        {
            pddi->cbSize = sizeof( DDKISRINFO );
            DWORD status = DDKReg_GetIsrInfo( m_hDevKey, pddi );
            return status;
        }
        else
        {
            return ERROR_INVALID_FUNCTION;
        }
    }

——由此可知最终还是通过DDKReg_GetIsrInfo来实现,继续跟踪。

哦,天啊,这个函数不开源的。

This function populates a DDKISRINFO structure with information from the registry. If you specify an interrupt service routine (ISR) DLL, you must also specify a handler entry point and an interrupt request (IRQ).

DWORD WINAPI DDKReg_GetIsrInfo(
  HKEY hk, 
  PDDKISRINFO pii
);

Parameters

hk
[in] Handle to a registry key.
pii
[out] Pointer to a DDKISRINFO structure.

Requirements

OS Versions: Windows CE .NET 4.0 and later.
Header: Ddkreg.h.
Link Library: Coredll.lib.—————————不开源的

上面的DDKISRINFO结构体

This structure contains interrupt service routine (ISR) information.

typedef struct _DDKISRINFO_tag {
  DWORD cbSize;
  DWORD dwIrq;
  DWORD dwSysintr;
  WCHAR szIsrDll[DEVDLL_LEN];
  WCHAR szIsrHandler[DEVENTRY_LEN];
} DDKISRINFO, *PDDKISRINFO;

Members

cbSize
Size of this structure.
dwIrq
Interrupt number. Use Irq in the registry or IRQ_UNSPECIFIED if there is no entry.
dwSysintr
Interrupt identifier. Use Sysintr in the registry or SYSINTR_NOP if there is no entry.
szIsrDll
Installable ISR DLL. Use IsrDll in the registry.
szIsrHandler
Installable ISR DLL entry point. Use IsrHandler in the registry.

Requirements

OS Versions: Windows CE .NET 4.0 and later.
Header: Ddkreg.h.

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

你可能感兴趣的:(function,File,Microsoft,null,WinCE,structure)