您的位置:首页 > 其它

wince5.0中es1371声卡单层驱动源码分析

2009-07-11 15:53 615 查看
刚刚进入公司就要做WINCE驱动,实话是我从来没有接触过着东西。只好硬着头皮学习,不管学得怎么样我努力,还是记录下自己的一点心得,今天闲来分析下wince5.0自带的声卡驱动。源代码在%_WINCEROOT%/PUBLIC/Common/OAK/DRIVER/WAVEDEV下面。其中UNDEFINED是单层驱动,我就先学习这个。
先看看wavmain.cpp,wavmain.cpp的第一个函数DllMain是本驱动的入口函数,即系统加载驱动到进程空间,这时候Op的取值是DLL_PROCESS_ATTACH,DisableThreadLibraryCalls取消DLL_THREAD_DETACH和DLL_THREAD_ATTACH的通知消息,这样可以减少某些程序占用的空间,在第二次加载这个dll的时候,不再调用DllMain,因为进程是共享地址空间的。在释放进程空间的时候,DllMain又会被调用,这时候Op的取值是DLL_PROCESS_DETACH。在新线程被创建的时候,DllMain被调用,Op取值为DLL_THREAD_ATTACH,当第二次创建线程的时候,DllMain会被再次调用,建立新的dll映射,因为线程的是独占地址空间的。同样,当线程结束时,DllMain被调用,Op取值为DLL_THREAD_DETACH。另外在使用TerminateProcess或者TerminateThread结束进程或结束线程的时候,DllMain将不被调用。
wavmain.cpp定义了一个OPENHANDLES结构,这个结构是在WAV_Open上创建,而且会传递到WAV_IOControl、WAV_Read和WAV_Write等函数。
接下来看看WAV_Init函数,CalibrateStallCounter用于延迟校准,不用关心。在wavdrv.h里面有如下定义:
typedef class CDriverContext * PDriverContext, * HDRIVER;
所以HDRIVER是CDriverContext类的指针。在wavdrv.h里面,CDriverContext定义了所有与声卡驱动程序有关的变量与函数。WAV_Init定义了一个CDriverContext类的指针pDriver,当pDriver为NULL,说明类实例创建失败,程序返回pDriver的NULL值;当pDriver不为NULL,说明类实例创建成功,再调用CDriverContext的Initialize函数初始化驱动程序,失败则返回FALSE,WAV_Init返回NULL,初始化成功Initialize返回TRUE,WAV_Init则返回成功CDriverContext的实例pDriver。
那么,CDriverContext类的Initialize究竟做些什么呢?
Initialize函数定义在drvrctxt.cpp里面,在每个函数前面都有一个宏:MYTHIS_CHECK,它定义在debug.h里面,用于调试,我们不用关心。首先,Initialize创建了一个CRegKey的实例regkey,CRegKey类声明在cregkey.h里面,它定义了所有与注册表相关的操作,其构造函数定义在cregkey.cpp里面,初始化一个注册表。然后,Initialize实例化了一个CES1371类,CES1371类声明在es1371.h里面,其成员函数定义在es1371.cpp里面,CES1371定义了所有与声卡硬件操作相关的变量和函数,所以这部分要参考ES1371声卡的datasheet进行分析,要复杂得多。这里实例化CES1371类,它的构造函数将被调用,我们不妨来分析下CES1371::CES1371 (void),InitializeCriticalSection函数初始化零界资源m_csPageLock,然后初始化各个成员变量,NUM_DMACHANNELS在es1371.h里面定义为3,因为在datasheet 2.2节里面有说明:AudioPCI 97 essentially implements a 3 channel DMA controller.在es1371.h里还定义了DMACHANNEL结构,DMACHANNEL描述了DMA的全部信息。m_dmachannel是DMACHANNEL的结构数组,CES1371构造函数用一个for循环来初始化3个DMA通道的状态和DMA是否可用。DMA的状态在es1371.h里面有三种:
#define DMA_STOPPED 0
#define DMA_RUNNING 1
#define DMA_PAUSED 2
这里ulDMAChannelState初始化为DMA_STOPPED,fAllocated初始化为FALSE。为了可读性,es1371.h把这3个DMA通道控制重命名为:
#define ES1371_DAC0 0
#define ES1371_DAC1 1
#define ES1371_ADC 2
ulDirection表示数据进出的方向,es1371.h定义了它的取值:
#define DMADIR_IN 1
#define DMADIR_OUT 2
例如:ES1371_ADC初始化为DMADIR_IN,表示ES1371_ADC是一个输入通道。
ulInitialSize表示预分配给各个通道的大小,es1371.h也定义了它的取值:
#define PRE_ALLOC_BUFFER_SIZE_DAC0 0x2000 //8kb
#define PRE_ALLOC_BUFFER_SIZE_DAC1 0x10000 //64kb needed by dmusic
#define PRE_ALLOC_BUFFER_SIZE_ADC 0x8000 //32kb
ucIntMask表示中断掩码,es1371.h定义了它的取值:
#define ES1371_INT_ADC 0x01
#define ES1371_INT_DAC1 0x02
#define ES1371_INT_DAC0 0x04
其他的初始化可以根据成员变量名称看出它所表示的意思了,IST中断事件的句柄m_hISTInterruptEvent初始化为NULL,IST线程m_hISThread初始化为NULL,退出IST与否m_bExitIST初始化为FALSE,地址映射与否m_fIsMapped也初始化为FALSE,电源状态初始化为0。至此CES1371构造函数的分析告一段落。
我们接着分析CDriverContext的Initialize函数。创建CES1371类实例m_pDevice之后,调用了CES1371的MapDevice函数,我们看看这个函数在es1371.cpp里面的定义。m_pDevice的参数是一个CRegKey实例的指针,MapDevice可以通过读取Active键的相应子键获取PCI物理基地址,然后将它映射为系统虚拟地址。首先调用DDKReg_GetWindowInfo函数,获取PCI总线号、接口类型等信息存入DDKWINDOWINFO结构的wini里面,DDKWINDOWINFO结构如下:
typedef struct _DDKWINDOWINFO_tag {
DWORD cbSize;
DWORD dwBusNumber;
DWORD dwInterfaceType;
DWORD dwNumIoWindows;
DEVICEWINDOW ioWindows[MAX_DEVICE_WINDOWS];
DWORD dwNumMemWindows;
DEVICEWINDOW memWindows[MAX_DEVICE_WINDOWS];
} DDKWINDOWINFO, *PDDKWINDOWINFO;
然后保存在m_pDevice里。接下来检查wini.dwNumIoWindows的值,不为1就返回FALSE。
同理,调用DDKReg_GetIsrInfo函数从注册表中获取中断IRQ、系统中断SysIntr、可安装中断的处理器DLL以及其DLL入口等信息,然后填充到DDKPCIINFO结构的isri中,进行一些判断。再调用DDKReg_GetPciInfo函数从注册表中获取PCI设备号、功能号、设备标识、开发标识等信息,然后填充到DDKPCIINFO结构的pcii中,做个判断并保存部分信息到m_pDevice中,这里不再赘述。
接下来调用LoadIntChainHandler函数,加载处理特定硬件中断的ISP,即可安装中断的处理器DLL。如果加载成功,调用TransBusAddrToStatic函数,将PCI总线地址转换为一个物理系统地址,然后创建一个静态的、独立于进程的虚拟地址映射。KernelLibIoControl函数创建可安装中断的ISR处理器。对照es1371的datasheet可以知道,ES1371_dSTATUS_OFF代表的是中断片选状态寄存器的第8:0位
ES1371_INTSTAT_PENDING定义为0x80000000,这个位设置为1时,代表有中断。
接下来调用HalTranslateBusAddress函数,将PCI物理总线地址转换为物理系统地址。调用MmMapIoSpace函数,将PCI物理地址空间映射为一个不分页的、进程相关的虚拟地址空间,m_fIsMapped置为TRUE。调用CreateEvent函数,把创建的事件赋予m_hISTInterruptEvent。调用InterruptInitialize函数,初始化硬件中断。用HalAllocateCommonBuffer函数初始化DMA通道缓冲区。最后用InitHardware函数初始化es1371声卡硬件。在分析InitHardware函数的时候,必须对照ES1371声卡的硬件规范,否则就不可能理解它的源码。
现在对照ES1371 datasheet仔细分析InitHardware函数的实现。根据从注册表获取的设备号判断是不是5880或者1371,如果是,要进一步进行初始化。ES1371_bINTSUMM_OFF对应中断片选状态寄存器的第31:24位,HwRegRMW函数将29位置1,其他置0,这个时候没有中断产生。RtlConvertLongToLargeInteger函数将LONG型数据转换成LargeInteger。KeDelayExecutionThread延迟liTimeout。READ_PORT_UCHAR宏将从m_pPciAddr读到的字节返回给ucDummyRead。ES1371_bGPIO_OFF对应于中断片选控制寄存器的23:16位,HwRegRMW将中断片选控制寄存器的22位置1,其他置0。接下来HwRegRMW将中断片选控制寄存器全部置0。接下来,HwRegRMW将中断片选控制寄存器的7:0位低三位置1,其他的置0。然后HwRegRMW将中断片选控制寄存器的31:24位全部置0。以下同理,不再赘述。
回到CDriverContext::Initialize上边来,地址空间映射完后,就可以调用CES1371的AudioInitialize来初始化声音设备硬件了。下面我们分析es1371.h下面的AudioInitialize函数的定义。AudioInitialize用CreateThread函数创建了一个中断服务线程m_hISThread,并用CeSetThreadPriority函数给这个线程赋予210的优先级。IST_Startup函数和IST函数都定义在es1371.cpp中。
接下来,CDriverContext::Initialize调用了CDriverContext::AudioInitializeMixerState函数,这个函数定义在mixdrv.cpp里面。AudioInitializeMixerState函数初始化混音器的状态。太复杂了,我暂时看不懂,以后慢慢研究了,至此这个驱动也分析得差不多了,还有个IOControl函数有待分析和跟踪,以后再分析了。
祝大家周末愉快!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: