刚刚进入公司就要做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里面有三种:
<