s3c2450下AC97驱动研究
http://blog.csdn.net/alien75/article/details/4656922
?
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)傳遞給窗口的消息。就是說要基于窗口類的程序才有用,可以認此種情況不能用于控制臺程序,第二種情況則可以
?
總結
以上是生活随笔為你收集整理的s3c2450下AC97驱动研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: INT102 算法笔记
- 下一篇: Auto.js进行自动化熄灭屏幕操作(超