Windows CE串口驱动简析(2)-PDD层实现:CReg2410Uart和TX部分(基于WinCE5.0 SMDK2410 BSP的Serial驱动)

二.PDD层
1.PDD架构
现在我们就来看看SMDK2410中串口驱动的PDD部分.
MDD层和PDD COMMON层都是由微软提供的,一般情况下我们基本无须改动.微软为开发者提供了一个CSerialPDD类作为开发工作的起点.CSerialPDD是个纯虚类,我们需要将驱动程序的PDD层定义成CSerialPDD类的继承类,其成员必须准确全面的反应目标硬件平台的特定类型串口的属性.
MDD和PDD COMMON层不需要知道用户的CSerialPDD继承类的命名和具体实现,以CSerialPDD类指针引用其继承类实例的功能.
CSerialPDD的定义如下(位于/WINCE500/PUBLIC/COMMON/OAK/INC/cserpdd.h):
class CSerialPDD : public CRegistryEdit { public : // // Interface For Initialization CSerialPDD(LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj ); virtual ~CSerialPDD(); virtual BOOL Init(); virtual void PostInit() ; PHWOBJ GetHwObject() { return m_pHwObj; }; protected: PVOID const m_pMdd; PHWOBJ const m_pHwObj; HANDLE m_hParent; // //Device Operation public: virtual BOOL Open(); virtual BOOL Close(); BOOL IsOpen() { return m_lOpenCount!=0; }; void Reset(); virtual BOOL Ioctl(DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut); private: LONG m_lOpenCount; // //Power Managment Operation public: virtual BOOL InitialPower(BOOL bInit); virtual BOOL PowerOff(); virtual BOOL PowerOn(); virtual BOOL SetDevicePowerState(CEDEVICE_POWER_STATE pwrState); virtual void SerialRegisterBackup()=0; virtual void SerialRegisterRestore()=0; virtual CEDEVICE_POWER_STATE GetDevicePowerState() { return m_PowerState; }; virtual POWER_CAPABILITIES GetPowerCapabilities() { return m_PowerCapabilities; }; BOOL IsThisPowerManaged() { return m_IsThisPowerManaged; }; BOOL IsPowerResumed ( ) { return (InterlockedExchange( &m_IsResumed,0)!=0); }; protected: CEDEVICE_POWER_STATE m_PowerState; BOOL m_IsThisPowerManaged; POWER_CAPABILITIES m_PowerCapabilities; LONG m_IsResumed; CSerialPDDPowerUpCallback * m_PowerCallbackThread; HANDLE m_PowerHelperHandle; HANDLE m_hPowerLock; static CEDEVICE_POWER_STATE SetPowerStateStatic( DWORD, CEDEVICE_POWER_STATE ); // protected: CLockObject m_HardwareLock; // // Interrupt Interface public: virtual BOOL InitialEnableInterrupt(BOOL bEnable ) ; // Enable All the interrupt may include Xmit Interrupt. virtual BOOL NotifyPDDInterrupt(INTERRUPT_TYPE interruptType); virtual INTERRUPT_TYPE GetInterruptType(); virtual BOOL EventCallback(ULONG fdwEventMask, ULONG fdwModemStatus = 0 ); virtual BOOL DataReplaced(PBYTE puData,BOOL isBadData); private: DWORD m_dwInterruptFlag; CLockObject m_InterruptLock; protected: DWORD m_dwPriority256; // // // // Special Cancellation public: virtual BOOL PurgeComm( DWORD fdwAction); // // Tx Function. virtual BOOL InitXmit(BOOL bInit) = 0; virtual void XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen) = 0; virtual void XmitComChar(UCHAR ComChar) = 0; virtual BOOL EnableXmitInterrupt(BOOL bEnable)= 0; virtual BOOL CancelXmit() = 0 ; // // Rx Function. virtual BOOL InitReceive(BOOL bInit) = 0; virtual ULONG ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen) = 0; virtual ULONG CancelReceive() = 0; // // Modem virtual BOOL InitModem(BOOL bInit) = 0; virtual void ModemInterruptHandler()= 0; // This is Used to Indicate Modem Signal Changes. virtual ULONG GetModemStatus() = 0; virtual void SetDTR(BOOL bSet)= 0; virtual void SetRTS(BOOL bSet)= 0; virtual BOOL IsCTSOff() { return ((GetModemStatus() & MS_CTS_ON)==0) ; }; virtual BOOL IsDSROff() { return ((GetModemStatus() & MS_DSR_ON)==0) ; }; // Line Function virtual BOOL InitLine(BOOL bInit) = 0; virtual void LineInterruptHandler() = 0; virtual void SetBreak(BOOL bSet) = 0 ; virtual BOOL SetBaudRate(ULONG BaudRate,BOOL bIrModule) = 0; virtual BOOL SetByteSize(ULONG ByteSize) = 0; virtual BOOL SetParity(ULONG Parity)= 0; virtual BOOL SetStopBits(ULONG StopBits)= 0; virtual BOOL SetDCB(LPDCB lpDCB); DCB GetDCB() { return m_DCB; }; protected: DCB m_DCB; // // Configuration public: virtual void SetDefaultConfiguration(); virtual BOOL GetDivisorOfRate(ULONG BaudRate,PULONG pulDivisor); COMMPROP GetCommProperties() { return m_CommPorp; }; protected: COMMPROP m_CommPorp; // // // // IR Special Handle public: virtual void SetOutputMode(BOOL UseIR, BOOL Use9Pin) { m_fIREnable = UseIR; m_f9PinEnable=Use9Pin; } virtual void GetOutputMode(BOOL* pUseIR, BOOL* pUse9Pin) { if (pUseIR) *pUseIR = m_fIREnable; if (pUse9Pin) *pUse9Pin = m_f9PinEnable; } protected: BOOL m_fIREnable; BOOL m_f9PinEnable; // // Error Handling public: virtual void SetReceiveError(ULONG); virtual ULONG GetReceivedError() ; protected: ULONG m_ulCommErrors; };
CSerialPDD类以CRegistryEdit类作为它的父类.CRegistryEdit向它的继承类提供了访问系统注册表的能力.成员函数定义在cserpdd.cpp中.
CSerailPDD类的数据成员和成员函数可以划分为以下几个部分:
(1) 初始化部分
(2) 串口设备操作部分
(3) 电源管理部分
(4) 中断接口部分
(5) 数据发送与接收部分
(6) MODEM功能部分
(7) 线路功能部分
(8) 串口配置部分
(9) IR特殊处理部分
(10) 错误处理部分
SMDK2410串口驱动PDD中CSerialPDD的继承类就是CPdd2410Uart,这是2410平台上3个UART端口都拥有的共同一个抽象类,里面实现了串口大部分操作与具体哪个UART无关.
而CPdd2410Uart的继承类CPdd2410Serial1和CPdd2410Serial2则实际对应于具体串口,分别为UART0和UART1.
CPdd2410Uart实现在pdds3c2410_ser.cpp,而CPdd2410Serial1和CPdd2410Serial2实现在ser_smdk2410.cpp.
下面就先来看看CPdd2410Uart类.
2.CPdd2410Uart
(1)CPdd2410Uart原型
该类原型如下,继承自CSerialPDD和CMiniThread类.
class CPdd2410Uart: public CSerialPDD, public CMiniThread { public: CPdd2410Uart (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj); virtual ~CPdd2410Uart(); virtual BOOL Init(); virtual void PostInit(); virtual BOOL MapHardware(); virtual BOOL CreateHardwareAccess(); // Power Manager Required Function. virtual void SerialRegisterBackup() { m_pReg2410Uart->Backup(); }; virtual void SerialRegisterRestore() { m_pReg2410Uart->Restore(); }; // Implement CPddSerial Function. // Interrupt virtual BOOL InitialEnableInterrupt(BOOL bEnable ) ; // Enable All the interrupt may include Xmit Interrupt. private: virtual DWORD ThreadRun(); // IST // Tx Function. public: virtual BOOL InitXmit(BOOL bInit); virtual void XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen); virtual void XmitComChar(UCHAR ComChar); virtual BOOL EnableXmitInterrupt(BOOL bEnable); virtual BOOL CancelXmit(); virtual DWORD GetWriteableSize(); protected: BOOL m_XmitFifoEnable; HANDLE m_XmitFlushDone; // // Rx Function. public: virtual BOOL InitReceive(BOOL bInit); virtual ULONG ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen); virtual ULONG CancelReceive(); virtual DWORD GetWaterMark(); virtual BYTE GetWaterMarkBit(); protected: BOOL m_bReceivedCanceled; DWORD m_dwWaterMark; // // Modem public: virtual BOOL InitModem(BOOL bInit); virtual void ModemInterruptHandler() { GetModemStatus();}; virtual ULONG GetModemStatus(); virtual void SetDTR(BOOL bSet) {;}; virtual void SetRTS(BOOL bSet); // // Line Function. virtual BOOL InitLine(BOOL bInit) ; virtual void LineInterruptHandler() { GetLineStatus();}; virtual void SetBreak(BOOL bSet) ; virtual BOOL SetBaudRate(ULONG BaudRate,BOOL bIrModule) ; virtual BOOL SetByteSize(ULONG ByteSize); virtual BOOL SetParity(ULONG Parity); virtual BOOL SetStopBits(ULONG StopBits); // // Line Internal Function BYTE GetLineStatus(); virtual void SetOutputMode(BOOL UseIR, BOOL Use9Pin) ; protected: CReg2410Uart * m_pReg2410Uart; PVOID m_pRegVirtualAddr; volatile S3C2410X_INTR_REG * m_pINTregs; DWORD m_dwIntShift; public: void DisableInterrupt(DWORD dwInt) { m_pINTregs->INTSUBMSK |= (dwInt<<m_dwIntShift); } void EnableInterrupt(DWORD dwInt) { m_pINTregs->INTSUBMSK &= ~(dwInt<<m_dwIntShift); } void ClearInterrupt(DWORD dwInt) { m_pINTregs->SUBSRCPND = (dwInt<<m_dwIntShift); } DWORD GetInterruptStatus() { return ((m_pINTregs->SUBSRCPND) >> m_dwIntShift);}; DWORD GetIntrruptMask () { return ((~(m_pINTregs->INTSUBMSK) )>> m_dwIntShift); }; protected: CRegistryEdit m_ActiveReg; // Interrupt Handler DWORD m_dwSysIntr; HANDLE m_hISTEvent; // Optional Parameter DWORD m_dwDevIndex; DWORD m_dwISTTimeout; };
(2)CReg2410Uart
在详细看CPdd2410Uart的成员函数之前,我们先来看看CPdd2410Uart会用到的另一个类CReg2410Uart.这个类封装了对串口寄存器的操作函数.原型如下:
class CReg2410Uart { public: CReg2410Uart(PULONG pRegAddr); virtual ~CReg2410Uart() { ; }; virtual BOOL Init() ; // We do not virtual Read & Write data because of Performance Concern. void Write_ULCON(ULONG uData) { WRITE_REGISTER_ULONG( m_pReg, (uData)); }; ULONG Read_ULCON() { return (READ_REGISTER_ULONG(m_pReg)); } ; void Write_UCON (ULONG uData) { WRITE_REGISTER_ULONG(m_pReg+1 , uData); }; ULONG Read_UCON() { return READ_REGISTER_ULONG(m_pReg+1 ); }; void Write_UFCON(ULONG uData) { WRITE_REGISTER_ULONG( m_pReg+2, uData);}; ULONG Read_UFCON() { return READ_REGISTER_ULONG(m_pReg + 2); }; void Write_UMCON(ULONG uData) { WRITE_REGISTER_ULONG(m_pReg + 3, uData);}; ULONG Read_UMCON() { return READ_REGISTER_ULONG(m_pReg + 3);}; ULONG Read_UTRSTAT() { return READ_REGISTER_ULONG(m_pReg + 4);}; ULONG Read_UERSTAT() { return READ_REGISTER_ULONG(m_pReg + 5);}; ULONG Read_UFSTAT() { return READ_REGISTER_ULONG(m_pReg + 6);}; ULONG Read_UMSTAT() { return READ_REGISTER_ULONG(m_pReg + 7);}; void Write_UTXH (UINT8 uData) { WRITE_REGISTER_ULONG( (m_pReg + 8), uData) ; }; UINT8 Read_URXH() { return (UINT8) READ_REGISTER_ULONG(m_pReg + 9); }; void Write_UBRDIV(ULONG uData) { WRITE_REGISTER_ULONG( m_pReg + 10, uData );}; ULONG Read_UBRDIV() { return READ_REGISTER_ULONG(m_pReg + 10); }; virtual BOOL Write_BaudRate(ULONG uData); PULONG GetRegisterVirtualAddr() { return m_pReg; }; virtual void Backup(); virtual void Restore(); #ifdef DEBUG virtual void DumpRegister(); #endif protected: volatile PULONG const m_pReg; BOOL m_fIsBackedUp; private: ULONG m_ULCONBackup; ULONG m_UCONBackup; ULONG m_UFCONBackup; ULONG m_UMCOMBackup; ULONG m_UBRDIVBackup; ULONG m_BaudRate; ULONG m_s3c2410_pclk; };  
其中Write_ULCON,Read_ULCON等就是读写相应的UART寄存器,后面的m_pReg就是操作寄存器的虚拟地址.使用的READ_REGISTER_ULONG和WRITE_REGISTER_ULONG是CEDDK函数,用来读写寄存器.
下面来看看其他几个成员函数.
[1] 构造函数CReg2410Uart
CReg2410Uart::CReg2410Uart(PULONG pRegAddr) : m_pReg(pRegAddr) { m_fIsBackedUp = FALSE; PROCESSOR_INFO procInfo; DWORD dwBytesReturned; if (!KernelIoControl(IOCTL_PROCESSOR_INFORMATION, NULL, 0, &procInfo, sizeof(PROCESSOR_INFO), &dwBytesReturned)) { m_s3c2410_pclk = DEFAULT_S3C2410X_PCLK; RETAILMSG(TRUE, (TEXT("WARNING: CReg2410Uart::CReg2410Uart failed to obtain processor frequency - using default value (%d)./r/n"), m_s3c2410_pclk)); } else { m_s3c2410_pclk = procInfo.dwClockSpeed; RETAILMSG(TRUE, (TEXT("INFO: CReg2410Uart::CReg2410Uart using processor frequency reported by the OAL (%d)./r/n"), m_s3c2410_pclk)); } }  
这个函数只是用来获得CPU时钟,同时给m_pReg附初值,也就是对应的UART寄存器地址.
[2] Init
用来初始化UART寄存器初值,都设为0.
BOOL CReg2410Uart::Init() { if (m_pReg) { // Set Value to default. Write_ULCON(0); Write_UCON(0); Write_UFCON(0); Write_UMCON(0); return TRUE; } else return FALSE; }   
[3] Backup,Restore
用来备份和回复UART寄存器内容.
void CReg2410Uart::Backup() { m_fIsBackedUp = TRUE; m_ULCONBackup = Read_ULCON(); m_UCONBackup = Read_UCON(); m_UFCONBackup = Read_UFCON(); m_UMCOMBackup = Read_UMCON(); m_UBRDIVBackup = Read_UBRDIV(); } void CReg2410Uart::Restore() { if (m_fIsBackedUp) { Write_ULCON(m_ULCONBackup ); Write_UCON( m_UCONBackup ); Write_UFCON( m_UFCONBackup ); Write_UMCON( m_UMCOMBackup ); Write_UBRDIV( m_UBRDIVBackup); m_fIsBackedUp = FALSE; } }
[4] Write_BaudRate
写波特率,首先判断UCON(UART控制寄存器)BIT10选择分频时钟源,然后写UBRDIV(波特率分频寄存器)来设置波特率.
CReg2410Uart::Write_BaudRate(ULONG BaudRate) { DEBUGMSG(ZONE_INIT, (TEXT("SetBaudRate -> %d/r/n"), BaudRate)); if ( (Read_UCON() & CS_MASK) == CS_PCLK ) { Write_UBRDIV( (int)(m_s3c2410_pclk/16.0/BaudRate) -1 ); return TRUE; } else { // TODO: Support external UART clock. //OUTREG(pHWHead,UBRDIV,( (int)(S2410UCLK/16.0/BaudRate) -1 )); RETAILMSG(TRUE, (TEXT("ERROR: The s3c2410x serial driver doesn't support an external UART clock./r/n"))); ASSERT(FALSE); return(FALSE); } }   
另外还有个DumpRegister,用来输出寄存器值,供调试使用,这里就不多说了.
下面就来看看CPdd2410Uart的成员函数.
3.CPdd2410Uart构造函数
初始化了一些变量,如CReg2410Uart类对象,事件及其他标志等.注释如下:
CPdd2410Uart::CPdd2410Uart (LPTSTR lpActivePath, PVOID pMdd, PHWOBJ pHwObj ) : CSerialPDD(lpActivePath,pMdd, pHwObj) , m_ActiveReg(HKEY_LOCAL_MACHINE,lpActivePath) , CMiniThread (0, TRUE) { m_pReg2410Uart = NULL;//CReg2410Uart类对象 m_pINTregs = NULL;//中断寄存器地址 m_dwIntShift = 0;//INTSUBMSK寄存器的掩码位,从注册表中读得(InterruptBitsShift项),这里和注册表都设置为0,表明UART0的RXD中断 m_dwSysIntr = MAXDWORD;//逻辑中断号,初始为MAXDWORD,0xFFFF m_hISTEvent = NULL;//IST事件 m_dwDevIndex = 0;//注册表中的DeviceArrayIndex,编号 m_pRegVirtualAddr = NULL;//UART寄存器虚拟地址 m_XmitFlushDone = CreateEvent(0, FALSE, FALSE, NULL);//发送完成事件 m_XmitFifoEnable = FALSE;//发送fifo是否使能 m_dwWaterMark = 8 ;//接收FIFO触发中断的字节数,为什么叫WaterMark? } 
4.析构函数~CPdd2410Uart()
主要就是关闭线程,关闭事件,禁止中断,释放地址空间资源,如CReg2410Uart类对象空间,寄存器地址空间等.
CPdd2410Uart::~CPdd2410Uart() { InitModem(FALSE); if (m_hISTEvent) { m_bTerminated=TRUE; ThreadStart(); SetEvent(m_hISTEvent); ThreadTerminated(1000); InterruptDisable( m_dwSysIntr ); CloseHandle(m_hISTEvent); }; if (m_pReg2410Uart) delete m_pReg2410Uart; if (m_XmitFlushDone) CloseHandle(m_XmitFlushDone); if (m_pRegVirtualAddr != NULL) { MmUnmapIoSpace((PVOID)m_pRegVirtualAddr,0UL); } if (m_pINTregs!=NULL) { MmUnmapIoSpace((PVOID)m_pINTregs,0UL); } }  
5.Init
该函数首先调用基类的Init函数,CSerialPDD的Init成员函数主要负责初始化串口驱动程序的电源管理.Init同时并检查注册表是否打开和m_XmitFlushDone,都为TRUE则进一步初始化.
然后通过GetIsrInfo获得逻辑中断号,创建IST事件(m_hISTEvent),初始化m_dwSysIntr对应的中断.触发事件为m_hISTEvent.
接着读注册表获取如DeviceArrayIndex,InterruptBitsShift,ISTTimeouts等信息.
最后调用MapHardware和CreateHardwareAccess来映射硬件寄存器和创建特定UART的CReg2410Uart类对象.
BOOL CPdd2410Uart::Init() { RETAILMSG(TRUE, (TEXT("++CPdd2410Uart::Init() for GEC2410 Serial/r/n"))); if ( CSerialPDD::Init() && IsKeyOpened() && m_XmitFlushDone!=NULL) { // IST Setup . 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_2410UART_INTBIT_VAL_NAME,(PBYTE)&m_dwIntShift,sizeof(DWORD))) { RETAILMSG(1,(TEXT("Registery does not have %s set. Drivers fail!!!/r/n"),PC_REG_2410UART_INTBIT_VAL_NAME)); m_dwIntShift =0; return FALSE; } if (!GetRegValue(PC_REG_2410UART_IST_TIMEOUTS_VAL_NAME,(PBYTE)&m_dwISTTimeout, PC_REG_2410UART_IST_TIMEOUTS_VAL_LEN)) { m_dwISTTimeout = INFINITE; } if (!MapHardware() || !CreateHardwareAccess()) { return FALSE; } return TRUE; } return FALSE; }
接下来就来看看MapHardware和CreateHardwareAccess这两个函数.
[1] MapHardware
该函数首先判断m_pRegVirtualAddr是否为NULL,如果为NULL说明未初始化,第一次运行时应该为NULL,接着使用DDKWINDOWINFO的结构通过读注册表还获得InterfaceType和MemBase,MemLen(注册表中设置的是50000000,即UART0寄存器物理地址)然后调用TranslateBusAddr将一个总线相对地址转换成系统物理地址.使用MmMapIoSpace映射到IO虚拟地址空间.最后将2410中断寄存器物理地址映射到IO虚拟地址空间.之所以有这样的转换过程,是因为2410串口的母总线是Busenum.
BOOL CPdd2410Uart::MapHardware() { if (m_pRegVirtualAddr !=NULL) return TRUE; // Get IO Window From Registry DDKWINDOWINFO dwi; if ( GetWindowInfo( &dwi)!=ERROR_SUCCESS || dwi.dwNumMemWindows < 1 || dwi.memWindows[0].dwBase == 0 || dwi.memWindows[0].dwLen < 0x2c) return FALSE; DWORD dwInterfaceType; if (m_ActiveReg.IsKeyOpened() && m_ActiveReg.GetRegValue( DEVLOAD_INTERFACETYPE_VALNAME, (PBYTE)&dwInterfaceType,sizeof(DWORD))) { dwi.dwInterfaceType = dwInterfaceType; } // Translate to System Address. PHYSICAL_ADDRESS ioPhysicalBase = { dwi.memWindows[0].dwBase, 0}; ULONG inIoSpace = 0; if (TranslateBusAddr(m_hParent,(INTERFACE_TYPE)dwi.dwInterfaceType,dwi.dwBusNumber, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) { // Map it if it is Memeory Mapped IO. m_pRegVirtualAddr = MmMapIoSpace(ioPhysicalBase, dwi.memWindows[0].dwLen,FALSE); } ioPhysicalBase.LowPart = S3C2410X_BASE_REG_PA_INTR ; ioPhysicalBase.HighPart = 0; inIoSpace = 0; if (TranslateBusAddr(m_hParent,(INTERFACE_TYPE)dwi.dwInterfaceType,dwi.dwBusNumber, ioPhysicalBase,&inIoSpace,&ioPhysicalBase)) { m_pINTregs = (S3C2410X_INTR_REG *) MmMapIoSpace(ioPhysicalBase,sizeof(S3C2410X_INTR_REG),FALSE); } return (m_pRegVirtualAddr!=NULL && m_pINTregs!=NULL); }
[2] CreateHardwareAccess
这个函数就是生成CReg2410Uart类对象,针对具体的串口(注册表中设置的是UART0),这样调用该对象的成员函数就是对UART0寄存器进行读写操作了.
BOOL CPdd2410Uart::CreateHardwareAccess() { if (m_pReg2410Uart) return TRUE; if (m_pRegVirtualAddr!=NULL) { m_pReg2410Uart = new CReg2410Uart((PULONG)m_pRegVirtualAddr); if (m_pReg2410Uart && !m_pReg2410Uart->Init()) { // FALSE. delete m_pReg2410Uart ; m_pReg2410Uart = NULL; } } return (m_pReg2410Uart!=NULL); }  
6.PostInit
该函数也是初始化函数,在Init之后调用,首先设置一个CRITICAL_SECTION防止重入写冲突,然后设置UCON为0,禁止UART中断.如果此时SUBSRCPND有中断请求,则调用InitReceive和InitLine进行接收并使能中断结束后清除中断标志位.InitReceive和InitLine留在后面的内容分析.当确定SUBSRCPND无中断请求时,调用基类的PostInit和设置线程优先级(从注册表项Priority获得),并启动线程.
CSerialPDD::PostInit功能是初始化中断处理和初始化MODEM.
void CPdd2410Uart::PostInit() { DWORD dwCount=0; m_HardwareLock.Lock(); m_pReg2410Uart->Write_UCON(0); // Set to Default; DisableInterrupt(S2410UART_INT_RXD|S2410UART_INT_TXD|S2410UART_INT_ERR); // Mask all interrupt. while ((GetInterruptStatus() & (S2410UART_INT_RXD|S2410UART_INT_TXD|S2410UART_INT_ERR))!=0 && dwCount <MAX_RETRY) { // Interrupt. InitReceive(TRUE); InitLine(TRUE); ClearInterrupt(S2410UART_INT_RXD|S2410UART_INT_TXD|S2410UART_INT_ERR); dwCount++; } ASSERT((GetInterruptStatus() & (S2410UART_INT_RXD|S2410UART_INT_TXD|S2410UART_INT_ERR))==0); // IST Start to Run. m_HardwareLock.Unlock(); CSerialPDD::PostInit(); CeSetPriority(m_dwPriority256); #ifdef DEBUG if ( ZONE_INIT ) m_pReg2410Uart->DumpRegister(); #endif ThreadStart(); // Start IST. }
7.ThreadRun
这个就是IST线程,创建自CMiniThread类,CMiniThread类封装了对线程的基本操作,如ThreadStart,ThreadStop等.详情可参考/WINCE500/PUBLIC/COMMON/OAK/INC/Cmthread.h.
像通常的驱动IST一样,首先等待事件m_hISTEvent的发生,发生后去获取中断状态来判断是哪个中断发生了,根据不同的状态设置不同的标志.然后调用NotifyPDDInterrupt(基类CSerailPDD函数实现)向串口驱动程序报告串口中断事件,接着清除中断标志位.最后调用InterruptDone结束本次中断IST过程.
当其他中断(RX,TX)没有发生时,IST会默认INTR_MODEM来轮询MODEM.也就是最后else里做的工作.代码如下:
DWORD CPdd2410Uart::ThreadRun() { while ( m_hISTEvent!=NULL && !IsTerminated()) { if (WaitForSingleObject( m_hISTEvent,m_dwISTTimeout)==WAIT_OBJECT_0) { m_HardwareLock.Lock(); while (!IsTerminated() ) { DWORD dwData = (GetInterruptStatus() & (S2410UART_INT_RXD|S2410UART_INT_TXD|S2410UART_INT_ERR)); DWORD dwMask = (GetIntrruptMask() & (S2410UART_INT_RXD|S2410UART_INT_TXD|S2410UART_INT_ERR)); DEBUGMSG(ZONE_THREAD, (TEXT(" CPdd2410Uart::ThreadRun INT=%x, MASK =%x/r/n"),dwData,dwMask)); dwMask &= dwData; if (dwMask) { DEBUGMSG(ZONE_THREAD, (TEXT(" CPdd2410Uart::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 & S2410UART_INT_RXD)!=0) interrupts |= INTR_RX; if ((dwMask & S2410UART_INT_TXD)!=0) interrupts |= INTR_TX; if ((dwMask & S2410UART_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(" CPdd2410Uart::ThreadRun timeout INT=%x,MASK=%d/r/n"),m_pINTregs->SUBSRCPND,m_pINTregs->INTSUBMSK)); #ifdef DEBUG if ( ZONE_THREAD ) m_pReg2410Uart->DumpRegister(); #endif } } return 1; }  
8. InitialEnableInterrupt
这个函数比较简单,用来初始化允许或者禁止RXD和ERR中断.
BOOL CPdd2410Uart::InitialEnableInterrupt(BOOL bEnable ) { m_HardwareLock.Lock(); if (bEnable) EnableInterrupt(S2410UART_INT_RXD | S2410UART_INT_ERR ); else DisableInterrupt(S2410UART_INT_RXD | S2410UART_INT_ERR ); m_HardwareLock.Unlock(); return TRUE; }
9.InitXmit
InitXmit负责初始化串口的数据发送,该函数有一个bool型参数bEnable,TRUE表示初始化数据发送,FALSE表示解除初始化串口数据发送.
如果为TRUE,则初始化UART寄存器,设置中断方式(level触发,传输模式-中断请求或轮询),复位发送FIFO,禁止FIFO,Tx FIFO中断触发方式(4字节),最后重新使能FIFO.
如果为FALSE,则等待UTRSTA寄存器FIFO状态为空,即数据都已发送完毕.
代码如下:
BOOL CPdd2410Uart::InitXmit(BOOL bInit) { if (bInit) { m_HardwareLock.Lock(); DWORD dwBit = m_pReg2410Uart->Read_UCON(); // Set TxINterrupt To Level. // dwBit |= (1<<9); // Set Interrupt Tx Mode. dwBit &= ~(3<<2); dwBit |= (1<<2); m_pReg2410Uart->Write_UCON(dwBit ); dwBit = m_pReg2410Uart->Read_UFCON(); // Reset Xmit Fifo. dwBit |= (1<<2); dwBit &= ~(1<<0); m_pReg2410Uart->Write_UFCON( dwBit); // Set Trigger level to 4. dwBit &= ~(3<<6); dwBit |= (1<<6); m_pReg2410Uart->Write_UFCON(dwBit); // Enable Xmit FIFO. dwBit &= ~(1<<2); dwBit |= (1<<0); m_pReg2410Uart->Write_UFCON(dwBit); // Xmit Fifo Reset Done.. m_HardwareLock.Unlock(); } else { // Make Sure data has been trasmit out. // We have to make sure the xmit is complete because MDD will shut donw the device after this return DWORD dwTicks = 0; DWORD dwUTRState; while (dwTicks < 1000 && (((dwUTRState = m_pReg2410Uart->Read_UTRSTAT())>>1) & 3)!=3 ) { // Transmitter empty is not true DEBUGMSG(ZONE_THREAD|ZONE_WRITE,(TEXT("CPdd16550::InitXmit! Wait for UTRSTAT=%x clear./r/n"), dwUTRState)); Sleep(5); dwTicks +=5; } } return TRUE; }  
10.GetWriteableSize
GetWriteableSize用来获取FIFO中可以写入的字节数.首先读取UFSTAT来获得当前FIFO状态,如果为FULL则返回0,无可写字节数.如果不为0,则计算出当前可写字节数,并返回.
代码如下:
DWORD CPdd2410Uart::GetWriteableSize() { DWORD dwWriteSize = 0; DWORD dwUfState = m_pReg2410Uart->Read_UFSTAT() ; if ((dwUfState& (1<<9))==0) { // It is not full. dwUfState = ((dwUfState>>4) & 0xf); // It is fifo count. if (dwUfState < SER2410_FIFO_DEPTH_TX-1) dwWriteSize = SER2410_FIFO_DEPTH_TX-1 - dwUfState; } return dwWriteSize; }
11. XmitInterruptHandler
XmitInterruptHandler被串口驱动程序的IST线程调用用以处理串口的数据发送.

中断发生后,调用的过程是这样的:中断发生后,m_hISTEvent事件触发,IST线程ThreadRun获取中断类型调用NotifyPDDInterrupt(cserpdd.c)通知PDD,NotifyPDDInterrupt会调用MDD层中的SerialEventHandler(mdd.c),该函数会根据不同的状态调用相应的事件处理程序,如发送数据处理XmitInterruptHandler.
XmitInterruptHandler首先判断是否有发送数据,如果没有直接关闭中断返回.如果有则判断是否需要流控制,没有流控制仅仅关闭中断;如果需要流控制,则FIFO可写字节数,将该数目的字节写入UTXH(发送Buffer寄存器)中,写满后打开中断返回.最后清除中断标志位.
void CPdd2410Uart::XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen) { PREFAST_DEBUGCHK(pBuffLen!=NULL); m_HardwareLock.Lock(); if (*pBuffLen == 0) { EnableXmitInterrupt(FALSE); } else { DEBUGCHK(pTxBuffer); PulseEvent(m_XmitFlushDone); DWORD dwDataAvaiable = *pBuffLen; *pBuffLen = 0; if ((m_DCB.fOutxCtsFlow && IsCTSOff()) ||(m_DCB.fOutxDsrFlow && IsDSROff())) { // We are in flow off DEBUGMSG(ZONE_THREAD|ZONE_WRITE,(TEXT("CPdd16550::XmitInterruptHandler! Flow Off, Data Discard./r/n"))); EnableXmitInterrupt(FALSE); } else { DWORD dwWriteSize = GetWriteableSize(); DEBUGMSG(ZONE_THREAD|ZONE_WRITE,(TEXT("CPdd16550::XmitInterruptHandler! WriteableSize=%x to FIFO,dwDataAvaiable=%x/r/n"), dwWriteSize,dwDataAvaiable)); for (DWORD dwByteWrite=0; dwByteWrite<dwWriteSize && dwDataAvaiable!=0;dwByteWrite++) { m_pReg2410Uart->Write_UTXH(*pTxBuffer); pTxBuffer ++; dwDataAvaiable--; } DEBUGMSG(ZONE_THREAD|ZONE_WRITE,(TEXT("CPdd16550::XmitInterruptHandler! Write %d byte to FIFO/r/n"),dwByteWrite)); *pBuffLen = dwByteWrite; EnableXmitInterrupt(TRUE); } ClearInterrupt(S2410UART_INT_TXD); } m_HardwareLock.Unlock(); }
12. XmitComChar
XmitComChar供驱动程序发送单个字符,主要是软件流控制情况下的X-ON和X-OFF流控字符,以及应用程序以IOCTL_SERIAL_IMMEDIATE_CHAR为操作码调用COM_IOControl函数向串口发送立即字符.
如果FIFO not full,则向UTXH发送字符数据,否则允许发送中断,等FIFO中数据发送完毕后在发送(等待m_XmitFlushDone事件).
void CPdd2410Uart::XmitComChar(UCHAR ComChar) { // This function has to poll until the Data can be sent out. BOOL bDone = FALSE; do { m_HardwareLock.Lock(); if ( GetWriteableSize()!=0 ) { // If not full m_pReg2410Uart->Write_UTXH(ComChar); bDone = TRUE; } else { EnableXmitInterrupt(TRUE); } m_HardwareLock.Unlock(); if (!bDone) WaitForSingleObject(m_XmitFlushDone, (ULONG)1000); } while (!bDone); }
13. EnableXmitInterrupt
EnableXmitInterrupt是暂时地关闭及重新开启串口的发送中断.
BOOL CPdd2410Uart::EnableXmitInterrupt(BOOL fEnable) { m_HardwareLock.Lock(); if (fEnable) EnableInterrupt(S2410UART_INT_TXD); else DisableInterrupt(S2410UART_INT_TXD); m_HardwareLock.Unlock(); return TRUE; }
14. CancelXmit
CancelXmit取消串口的数据发送,直接调用InitXmit(TRUE)重新初始化,如reset fifo等.    
BOOL CPdd2410Uart::CancelXmit() { return InitXmit(TRUE); }
下一篇接着来看接收RX及MODEM,LINE,IR部分.

你可能感兴趣的:(thread,windows,null,interface,WinCE,initialization)