wince 2440串口驱动PDD分析(作者:wogoyixikexie@gliet)
经过前面的MDD分析可知,wince串口的接口函数com_init,Com_Open等函数是放在MDD中的,应用程序直接和MDD打交道,然后把需求传到中间层(介于MDD和PDD之间)中间层再调用PDD的函数(和硬件相关的函数)来实现。PDD是硬件相关的操作,如果需要增加串口个数或者外扩串口芯片都需要在这个PDD中实现。Come on!
在执行com_init函数的时候会间接调用到
- //根据串口编号创建对象,如果要增加串口个数要修改这个函数,增加case即可。
- CSerialPDD * CreateSerialObject(LPTSTR lpActivePath, PVOID pMdd,PHWOBJ pHwObj, DWORD DeviceArrayIndex)
- {
- CSerialPDD * pSerialPDD = NULL;
- switch (DeviceArrayIndex) {
- case 0://和物理串口对应
- pSerialPDD = new CPdd2440Serial1(lpActivePath,pMdd, pHwObj);//在PDD实现
- break;
- case 1:
- pSerialPDD = new CPdd2440Serial2(lpActivePath,pMdd, pHwObj);
- break;
- }
- if (pSerialPDD && !pSerialPDD->Init()) {
- delete pSerialPDD;
- pSerialPDD = NULL;
- }
- return pSerialPDD;
- }
____________________________________________________________________________________________
if (pSerialPDD && !pSerialPDD->Init()) ——读取注册表串口部分
- BOOL CPdd2440Uart::Init()
- {
- if ( CSerialPDD::Init() && IsKeyOpened() && m_XmitFlushDone!=NULL) {
- // IST Setup .--------Get IRQ forom regedit
- DDKISRINFO ddi;
- if (GetIsrInfo(&ddi)!=ERROR_SUCCESS) {
- return FALSE;
- }
- m_dwSysIntr = ddi.dwSysintr;
- if (m_dwSysIntr != MAXDWORD && m_dwSysIntr!=0 )
- m_hISTEvent= CreateEvent(0,FALSE,FALSE,NULL);
- if (m_hISTEvent!=NULL)
- InterruptInitialize(m_dwSysIntr,m_hISTEvent,0,0);
- else
- return FALSE;
- // Get Device Index.
- if (!GetRegValue(PC_REG_DEVINDEX_VAL_NAME, (PBYTE)&m_dwDevIndex, PC_REG_DEVINDEX_VAL_LEN)) {
- m_dwDevIndex = 0;
- }
- if (!GetRegValue(PC_REG_SERIALWATERMARK_VAL_NAME,(PBYTE)&m_dwWaterMark,sizeof(DWORD))) {
- m_dwWaterMark = 8;
- }
- if (!GetRegValue(PC_REG_2440UART_INTBIT_VAL_NAME,(PBYTE)&m_dwIntShift,sizeof(DWORD))) {
- RETAILMSG(1,(TEXT("Registery does not have %s set. Drivers fail!!!/r/n"),PC_REG_2440UART_INTBIT_VAL_NAME));
- m_dwIntShift =0;
- return FALSE;
- }
- if (!GetRegValue(PC_REG_2440UART_IST_TIMEOUTS_VAL_NAME,(PBYTE)&m_dwISTTimeout, PC_REG_2440UART_IST_TIMEOUTS_VAL_LEN)) {
- m_dwISTTimeout = INFINITE;
- }
- if (!MapHardware() || !CreateHardwareAccess()) {
- return FALSE;
- }
- return TRUE;
- }
- return FALSE;
- }
现在来看看它里面分配的对象,三星的BSP刚好只有两个串口驱动,另外一个做了调试串口。
- // CPdd2440Serial1 is only use for UART 0 which
- // RTS & CTS is use GPH0 GPH1
- // DTR & DSR is USE GPD0 GPD1
- class CPdd2440Serial1 : public CPdd2440Uart {
- public:
- CPdd2440Serial1(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj)
- : CPdd2440Uart(lpActivePath, pMdd, pHwObj)
- {
- m_pIOPregs = NULL;
- m_fIsDSRSet = FALSE;
- }
- ~CPdd2440Serial1() {
- if (m_pIOPregs!=NULL)
- MmUnmapIoSpace((PVOID)m_pIOPregs,0);
- }
- virtual BOOL Init() {
- PHYSICAL_ADDRESS ioPhysicalBase = { S3C2440A_BASE_REG_PA_IOPORT, 0};
- ULONG inIoSpace = 0;
- if (TranslateBusAddr(m_hParent,Internal,0, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) {
- // Map it if it is Memeory Mapped IO.
- m_pIOPregs = (S3C2440A_IOPORT_REG *)MmMapIoSpace(ioPhysicalBase, sizeof(S3C2440A_IOPORT_REG),FALSE);
- }
- if (m_pIOPregs) {
- 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));
- }
- else
- return FALSE;
- m_pDTRPort = (volatile ULONG *)&(m_pIOPregs->GPDDAT);
- m_pDSRPort = (volatile ULONG *)&(m_pIOPregs->GPDDAT);
- m_dwDTRPortNum = 0;
- m_dwDSRPortNum = 1;
- m_pIOPregs->GPHCON &= ~(0x3<<0 | 0x3<<2 | 0x3<<4 | 0x3<<6 );//tx,rx,rts,cts
- m_pIOPregs->GPHCON |= (0x2<<0 | 0x2<<2 | 0x2<<4 | 0x2<<6 );
- m_pIOPregs->GPHCON |= (0x2<<0 | 0x2<<2);
- m_pIOPregs->GPHUP |= 0xf;
- m_pIOPregs->GPDCON &= ~(0x3<<0 | 0x3<<2);//dtr,dsr
- m_pIOPregs->GPDCON |= (0x1<<0 | 0x0<<2);
- m_pIOPregs->GPDUP |= 0x3;
- return CPdd2440Uart::Init();
- }
- return FALSE;
- };
- virtual void SetDefaultConfiguration() {
- CPdd2440Uart::SetDefaultConfiguration();
- }
- virtual BOOL InitModem(BOOL bInit) {
- SetDTR(bInit);
- return CPdd2440Uart::InitModem(bInit);
- }
- virtual ULONG GetModemStatus() {
- ULONG ulReturn = CPdd2440Uart::GetModemStatus();
- ULONG ulEvent = 0;
- m_HardwareLock.Lock();
- BOOL fIsDSRSet = (((*m_pDSRPort) & (1<<m_dwDSRPortNum))==0);
- if (fIsDSRSet != m_fIsDSRSet) {
- ulEvent |= EV_DSR | EV_RLSD;
- }
- ulReturn |= (fIsDSRSet?(MS_DSR_ON|MS_RLSD_ON):0);
- m_fIsDSRSet = fIsDSRSet;
- m_HardwareLock.Unlock();
- if (ulEvent!=0)
- EventCallback(ulEvent,ulReturn);
- return ulReturn;
- }
- virtual void SetDTR(BOOL bSet) {
- if (bSet)
- *m_pDTRPort &= ~(1<<m_dwDTRPortNum);
- else
- *m_pDTRPort |= (1<<m_dwDTRPortNum);
- };
- private:
- volatile S3C2440A_IOPORT_REG * m_pIOPregs;
- volatile ULONG * m_pDTRPort;
- DWORD m_dwDTRPortNum;
- volatile ULONG * m_pDSRPort;
- DWORD m_dwDSRPortNum;
- BOOL m_fIsDSRSet;
- };
- —————————————————————————————
- // CPdd2440Serial2 is only use for UART 2 which
- // nIrDATXDEN use GPB1
- class CPdd2440Serial2 : public CPdd2440Uart {
- public:
- CPdd2440Serial2(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj)
- : CPdd2440Uart(lpActivePath, pMdd, pHwObj)
- {
- m_pIOPregs = NULL;
- }
- ~CPdd2440Serial2() {
- if (m_pIOPregs!=NULL)
- MmUnmapIoSpace((PVOID)m_pIOPregs,0);
- }
- virtual BOOL Init() {
- PHYSICAL_ADDRESS ioPhysicalBase = { S3C2440A_BASE_REG_PA_IOPORT, 0};
- ULONG inIoSpace = 0;
- if (TranslateBusAddr(m_hParent,Internal,0, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) {
- // Map it if it is Memeory Mapped IO.
- m_pIOPregs =(S3C2440A_IOPORT_REG *) MmMapIoSpace(ioPhysicalBase, sizeof(S3C2440A_IOPORT_REG),FALSE);
- }
- if (m_pIOPregs) {
- 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));
- }
- else
- return FALSE;
- m_pIOPregs->GPHCON &= ~(0x3<<12 | 0x3<<14); // clear rxd2 txd2
- m_pIOPregs->GPHCON |= (0x2<<12 | 0x2<<14); //enabled rxd2 txd2
- m_pIOPregs->GPHUP |= 0xc0;//disable pull up
- //m_pIOPregs->GPBCON &= ~(0x3<<2); //GPB1 -> nIrDATXDEN
- //m_pIOPregs->GPBCON |= (0x1<<2);
- //m_pIOPregs->GPBUP |= (0x1<<1);
- //m_pIOPregs->GPBDAT &= ~(0x1<<1);
- return CPdd2440Uart::Init();
- }
- return FALSE;
- };
- virtual void SetDefaultConfiguration() {
- CPdd2440Uart::SetDefaultConfiguration();
- }
- virtual ULONG GetModemStatus() {
- return (CPdd2440Uart::GetModemStatus() | MS_CTS_ON);
- }
- virtual void Rx_Pause(BOOL bSet) {
- if(bSet)
- m_pIOPregs->GPHCON = (m_pIOPregs->GPHCON & ~(0x3<<14)) | 0x0<<14;
- else
- m_pIOPregs->GPHCON = (m_pIOPregs->GPHCON & ~(0x3<<14)) | 0x2<<14;
- }
- volatile S3C2440A_IOPORT_REG * m_pIOPregs;
- };
——鉴于我的C++水平,我就先沉默了。看来要花点时间学习一下。
- // I must learn well C++
- //前面刚有人回帖说做驱动工程师不用C++,现在郁闷了吧。
- //对于这个KernelIoControl要有新的认识才行
- CReg2440Uart::CReg2440Uart(PULONG pRegAddr)
- : m_pReg(pRegAddr)
- {
- m_fIsBackedUp = FALSE;
- PROCESSOR_INFO procInfo;
- DWORD dwBytesReturned;
- //IOCTL_PROCESSOR_INFORMATION? Why do use it?
- if (!KernelIoControl(IOCTL_PROCESSOR_INFORMATION, NULL, 0, &procInfo, sizeof(PROCESSOR_INFO), &dwBytesReturned))
- {
- m_s3c2440_pclk = DEFAULT_S3C2440A_PCLK;
- RETAILMSG(TRUE, (TEXT("WARNING: CReg2440Uart::CReg2440Uart failed to obtain processor frequency - using default value (%d)./r/n"), m_s3c2440_pclk));
- }
- else
- {
- m_s3c2440_pclk = procInfo.dwClockSpeed;
- RETAILMSG(TRUE, (TEXT("INFO: CReg2440Uart::CReg2440Uart using processor frequency reported by the OAL (%d)./r/n"), m_s3c2440_pclk));
- }
- }
现在来看看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
- //------------------------------------------------------------------------------
- //
- // Function: OALIoCtlProcessorInformation
- //
- // Implements the IOCTL_PROCESSOR_INFORMATION handler.
- //
- BOOL OALIoCtlProcessorInfo(
- UINT32 code, VOID *pInpBuffer, UINT32 inpSize, VOID *pOutBuffer,
- UINT32 outSize, UINT32 *pOutSize
- ) {
- BOOL rc = FALSE;
- PROCESSOR_INFO *pInfo = (PROCESSOR_INFO*)pOutBuffer;
- UINT32 length1, length2, length3;
- OALMSG(OAL_FUNC, (L"+OALIoCtlProcessorInfo(...)/r/n"));
- // Set required/returned size if pointer isn't NULL
- if (pOutSize != NULL) *pOutSize = sizeof(PROCESSOR_INFO);
- // Validate inputs
- if (pOutBuffer == NULL || outSize < sizeof(PROCESSOR_INFO)) {
- NKSetLastError(ERROR_INSUFFICIENT_BUFFER);
- OALMSG(OAL_WARN, (
- L"WARN: OALIoCtlProcessorInfo: Buffer too small/r/n"
- ));
- goto cleanUp;
- }
- // Verify OAL lengths
- length1 = (NKwcslen(g_oalIoCtlProcessorCore) + 1) * sizeof(WCHAR);
- if (length1 > sizeof(pInfo->szProcessCore)) {
- OALMSG(OAL_ERROR, (
- L"ERROR:OALIoCtlProcessorInfo: Core value too big/r/n"
- ));
- goto cleanUp;
- }
- length2 = (NKwcslen(g_oalIoCtlProcessorName) + 1) * sizeof(WCHAR);
- if (length2 > sizeof(pInfo->szProcessorName)) {
- OALMSG(OAL_ERROR, (
- L"ERROR:OALIoCtlProcessorInfo: Name value too big/r/n"
- ));
- goto cleanUp;
- }
- length3 = (NKwcslen(g_oalIoCtlProcessorVendor) + 1) * sizeof(WCHAR);
- if (length3 > sizeof(pInfo->szVendor)) {
- OALMSG(OAL_ERROR, (
- L"ERROR:OALIoCtlProcessorInfo: Vendor value too big/r/n"
- ));
- goto cleanUp;
- }
- // Copy in processor information
- pInfo->wVersion = 1;
- memset(pInfo, 0, sizeof(PROCESSOR_INFO));
- memcpy(pInfo->szProcessCore, g_oalIoCtlProcessorCore, length1);
- memcpy(pInfo->szProcessorName, g_oalIoCtlProcessorName, length2);
- memcpy(pInfo->szVendor, g_oalIoCtlProcessorVendor, length3);
- pInfo->dwInstructionSet = g_oalIoCtlInstructionSet;
- pInfo->dwClockSpeed = g_oalIoCtlClockSpeed;
- // Indicate success
- rc = TRUE;
- cleanUp:
- OALMSG(OAL_FUNC, (L"-OALIoCtlProcessorInfo(rc = %d)/r/n", rc));
- return rc;
- }
- //---------------------------------???好复杂,看不懂。等下再看了。
——————————————————————————————————————————————————————
现在来看点实际点的东西吧。
——我发现这个串口物理中断转换成逻辑(系统)中断有点奇怪。
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.
——————————————————————————————————————————————