s3c2450下AC97驱动研究

来源:互联网 发布:淘宝网二手市场在哪 编辑:程序博客网 时间:2024/06/03 23:04

AC97驱动分析

总体而言,AC97驱动是wavedev结构的驱动,上层应用通过调用WAVEAPI函数,和驱动接口HandleWaveMessage进行交互,驱动根据传递的WIDM_XXX系列值进行操作。现在以Wince5自带的waverec例程进行分析

一、流程
1、初始化过程
这个是设备启动时,由设备管理器加载驱动。调用了WAV_Init、WAV_OPEN、WAV_CLOSE三个函数;传递WIDM_GETNUMDEVS、WODM_GETNUMDEVS、WODM_GETEXTDEVCAPS、WODM_OPEN、WODM_SETVOLUME共五个控制码,如果系统设置为有开机声音还会传递WODM_WRITE、WODM_UNPREPARE、WODM_CLOSE。
在WAV_INIT中会调用CreateHWContext创建全局硬件上下文,完成初始化GPIO、分配DMA内存、初始化AC97控制器和外部codec、创建输入/输出IST

2、录音过程
(1)WIDM_OPEN
(a)获取设备上下文:DeviceContext *pDeviceContext = g_pHWContext->GetInputDeviceContext(uDeviceId); ===> InputDeviceContext对象

(b)打开输入流:pDeviceContext->OpenStream; 
第一步:创建输入流
pStreamContext = CreateStream(lpWOD); ===> InputStreamContext对象
父类DeviceContext的CreateStream为纯虚函数,它派生的的两个子类InputDeviceContext和OutputDeviceContext重载了此函数
InputDeviceContext的CreateStream只是创建了一个对象InputStreamContext
OutputDeviceContext的处理要复杂一点,它有CMidiStream、OutputStreamContextM8、OutputStreamContextM16、OutputStreamContextS8、OutputStreamContextS16五个子类,针对输入格式、声道、采样位数创建不同的子类
第二步:打开输入流
Result = pStreamContext->Open(this,lpWOD,dwFlags);
根据派生关系先隐式调用WaveStreamContext的Open,再显式调用StreamContext的Open。到这一步,录音的初始化基本完成。

(2)WIDM_PREPARE
无操作

(3)WIDM_ADDBUFFER
pStreamContext->QueueBuffer((LPWAVEHDR)dwParam1);
dwParam1对应的就是应用程序传递的参数(因不在同一地址空间,所以其地址是不一样的),将它加入到一个单链表中(具体说明见相关数据结构的第4点)

(4)WIDM_START
启动DMA输入:pStreamContext->Run();
m_pDeviceContext->StreamReadyToRender(this); ===> InputDeviceContext对象
g_pHWContext->StartInputDMA();   ===> 初次调用,在IST中每次TransferInputBuffers中会调用此函数(如果BytesTransferred为0则StopImputDMA)
第一步:DMA通道状态设置(开/关)
Codec_channel()
第二步:
m_InputDMAStatus = (DMA_DONEA | DMA_DONEB) & ~DMA_BIU; 
第三步:配置DMA输入通道
InitInputDMA()
第四步:打开输入音量
AudioMute(DMA_CH_MIC, FALSE);
第五步:开始DMA输入
AUDIO_RESET_RECORD_POINTER();
SELECT_AUDIO_DMA_INPUT_BUFFER_A(); ===> 选择BUFFER_A做为DMA输入缓冲
Codec_channel();
AUDIO_IN_DMA_ENABLE();   ===> 设置DMA控制器使能DMA传输
SELECT_AUDIO_DMA_INPUT_BUFFER_B(); ===> 选择BUFFER_B做为DMA输入缓冲    ===> A为下次传输做准备,不让DMA空置停机
到这一步,DMA传输就绪,接下来就由IST等待中断事件并进行数据处理。

(5)WIDM_UNPREPARE
无操作

(6)WIDM_CLOSE
释放输入流:pStreamContext->Release();
m_pDeviceContext->DeleteStream(this); //引用计数为0才真正释放

3、HardwareContext的IST流程
(1)等待DMA中断事件产生
(2)根据m_InputDMAStatus判断是BUFFER_A还是BUFFER_B完成DMA传输,并切换到另一个缓冲做DMA传输
(3)AUDIO_IN_DMA_ENABLE(); ===> 发出新的DMA请求
(4)InputTransferred = TransferInputBuffers(m_InputDMAStatus); ===>这是个很重要的函数,将DMA收到的数据进行Render(插值运算转换采样率,AC97源采样率44100)
a)HardwareContext::TransferInputBuffer
b)DeviceContext::TransferBuffer
c)StreamContext::Render  ===> 纯虚函数,实际调用WaveStreamContext::Render
d)StreamContext::GetNextBuffer ===> 子类都要调用此函数,目的是提高代码复用性
从StreamContext的单链表(具体说明见相关数据结构的第4点)取出一个LPWAVEHEAD(由QueueBuffer加入链表)获得成员lpData赋值给m_lpCurrData(具体说明见相关数据结构),并返回。
e)WaveStreamContext::Render2 ===> 纯虚函数,实际调用派生子类(详见相关数据结构中的说明)的函数。具体算法参见各子类的Render2(待深入研究)
将DMA中的数据render后放到m_lpCurrData中(LPWAVEHEADER),应用程序通过MM_WIN_DATA应该能从WAVEHEADER中获得此数据
(5)重复第(1)步

二、相关数据结构
1、全局硬件上下文
HardwareContext *g_pHWContext

2、设备上下文
派生关系:
InputDeviceContext <-- DeviceContext
OutputDeviceContext <-- DeviceContext

3、数据流上下文
派生关系:
InputStreamContext <-- WaveStreamContext <-- StreamContext
CMidiStream <-- StreamContext
OutputStreamContextM8(M16/S8/S16) <-- OutputStreamContext <-- WaveStreamContext <-- StreamContext

4、StreamContext的WAVEHDR单链表
LPWAVEHDR    m_lpWaveHdrHead;
LPWAVEHDR    m_lpWaveHdrCurrent;
LPWAVEHDR    m_lpWaveHdrTail;

5、StreamContext的数据缓冲区指针
PBYTE    m_lpCurrData;            // position in current buffer
PBYTE    m_lpCurrDataEnd;         // end of current buffer
在调用QueueBuffer(WIDM_ADDBUFFER)的时候进行赋值,m_lpCurrData赋值是LPWAVEHDR中的lpData,也就是应用程序接收缓冲区首地址;m_lpCurrDataEnd赋值是应用程序接收缓冲区未地址

6、DeviceContext的linklist双向链表,管理流上下文对象
LIST_ENTRY  m_StreamList;         // List of streams rendering to/from this device
根据其成员Flink查找相应StreamContext对象(Flink对应StreamContext成员m_Link)

7、DMA通道状态设置
BOOL m_InputDMARunning;
BOOL m_OutputDMARunning;

8、DMA控制器状态记录
//----- Used to track DMA controllers status -----
#define DMA_CLEAR   0x00000000
#define DMA_DONEA   0x00000008
#define DMA_STRTA   0x00000010
#define DMA_DONEB   0x00000020
#define DMA_STRTB   0x00000040
#define DMA_BIU    0x00000080 // Determines which buffer is in use: (A=0, B=1)
DWORD  m_InputDMAStatus;    // Input DMA channel's status

9、DMA缓冲区页面大小(输入/输出都有两个缓冲区A和B进行双缓冲操作)
#define AUDIO_DMA_PAGE_SIZE (4096)

10、DMA缓冲区接收数据大小数组
m_InBytes[IN_BUFFER_A];
m_InBytes[IN_BUFFER_B];

11、DMA接收缓冲区A/B首地址
PBYTE m_Input_pbDMA_PAGES[2];

12、IST中断事件
HANDLE m_hAudioInterruptInput;

三、容易混淆的概念
1、WIDM_XXX/WODM_XXX、WOM_XXX/WIM_XXX、MM_WOM_XXX/MM_WIM_XXX的区别
(1)在waveapi通过DeviceIoControl调用驱动的WAV_IOControl时传递的参数
(2)传递给回调函数waveInProc或waveOutProc的消息,回调函数是waveInOpen或waveOutOpen的参数
(3)传递给窗口的消息。就是说要基于窗口类的程序才有用,可以认此种情况不能用于控制台程序,第二种情况则可以

 

 

原创粉丝点击