您的位置:首页 > 编程语言 > Go语言

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).
#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.

——————————————————————————————————————————————
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: