wince 2440串口驱动PDD分析(作者:wogoyixikexie@gliet)
2009-01-08 14:38
615 查看
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).
现在来看看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).
[in] Handle to a registry key.
pii
[out] Pointer to a DDKISRINFO structure.
Header: Ddkreg.h.
Link Library: Coredll.lib.—————————不开源的
上面的DDKISRINFO结构体
This structure contains interrupt service routine (ISR) information.
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.
Header: Ddkreg.h.
——————————————————————————————————————————————
经过前面的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
cbSizeSize 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.
——————————————————————————————————————————————
相关文章推荐
- 2440 外部串口驱动调试(作者:wogoyixikexie@gliet)
- 2440 外部串口驱动调试(作者:wogoyixikexie@gliet)
- wince串口之MDD分析(作者:wogoyixikexie@gliet)
- 2440 外部串口驱动调试(作者:wogoyixikexie@gliet)
- wince 5.0 .2440 5.0BSP的中断过程(作者:wogoyixikexie@gliet)
- 如何删除wince的驱动(作者:wogoyixikexie@gliet)
- 转:wince 5.0 .2440 5.0BSP的中断过程(作者:wogoyixikexie@gliet)
- 2440 5.0BSP触摸屏驱动学习(作者:wogoyixikexie@gliet)
- 转---------------wince串口线程、中断等相关学习(作者:wogoyixikexie@gliet)
- 2440 5.0BSP触摸屏驱动学习(作者:wogoyixikexie@gliet)
- wince驱动异常调试方法(作者:wogoyixikexie@gliet)
- ZLG7290(wince下)驱动之不停执行同一动作的解决办法(作者:wogoyixikexie@gliet)
- wince串口线程、中断等相关学习(作者:wogoyixikexie@gliet)
- 浅谈wince驱动调试方法(作者:wogoyixikexie@gliet)
- wince驱动异常调试方法(作者:wogoyixikexie@gliet)
- ZLG7290(wince下)驱动之不停执行同一动作的解决办法(作者:wogoyixikexie@gliet)
- 写给像我一样的新手——写wince单层驱动的流程(作者:wogoyixikexie@gliet)
- wince串口打印函数是如何实现的?(作者wogoyixikexie@gliet)
- 2440 5.0BSP增加三串口(作者:wogoyixikexie@gliet)
- 如何在wince下添加和删除驱动(作者:wogoyixikexie@gliet)