您的位置:首页 > 其它

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

2010-06-10 14:23 465 查看
二.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部分.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐