- 論壇徽章:
- 0
|
翻譯:creator
sz111@126.com
第五章
PCM[color="#000000"]接口
[color="#000000"]概述
PCM[color="#000000"]中間層是ALSA[color="#000000"]中作用非常大的。它是唯一需要在每個驅(qū)動中都需要實現(xiàn)的low-level[color="#000000"]的硬件接口。
為了訪問PCM[color="#000000"]層,你需要包含[color="#000000"]。除此之外,如果你要操作hw_param[color="#000000"]相關的函數(shù),還需要包含[color="#000000"]。
每個card[color="#000000"]設備可以最多擁有4[color="#000000"]個pcm[color="#000000"]實例。一個pcm[color="#000000"]實例對應予一個pcm[color="#000000"]設備文件。組件的號碼限制主要是和Linux[color="#000000"]的可用的設備號多少有關。假如允許64bit[color="#000000"]的設備號,我們可以擁有更多的pcm[color="#000000"]實例。
[color="#000000"] 一個pcm[color="#000000"]實例包含pcm
[color="#000000"]放音和錄音流,而每個pcm[color="#000000"]流由一個或多個pcm[color="#000000"]子流組成。一些聲卡支持多重播放的功能。例如:emu10k1[color="#000000"]就包含一個32[color="#000000"]個立體聲子流的PCM[color="#000000"]放音設備。事實上,每次被打開的時候,一個可用的子流會自動的被選中和打開。同時,當一個子流已經(jīng)存在,并且已經(jīng)被打開,當再次被打開的時候,會被阻塞,或者根據(jù)打開文件的模式不同返回一個EAGAIND[color="#000000"]的錯誤信息。你也不需要知道你的驅(qū)動細節(jié)部分,PCM[color="#000000"]中間層會處理那些工作。
[color="#000000"]代碼示例
下面的代碼沒有包含硬件接口程序,主要顯示一個如何構建一個PCM[color="#000000"]接口的骨架。
[color="#000000"]Example5-1.PCM[color="#000000"]示例代碼
#include
[color="#000000"] ....
[color="#000000"] /*[color="#000000"]硬件定義*/
static
struct snd_pcm_hardware snd_mychip_playback_hw = {
.info
= (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP_VALID),
.formats
= SNDRV_PCM_FORMAT_S16_LE,
.rates
= SNDRV_PCM_RATE_8000_48000[color="#000000"],
.rate_min
= 8000,
.rate_max
= 48000,
.channels_min
= 2,
.channels_max
= 2,
.buffer_bytes_max
= 32768,
.period_bytes_min
= 4096,
.period_bytes_max
= 32768,
.periods_min
= 1,
.periods_max
= 1024,
[color="#000000"] };
[color="#000000"] /*[color="#000000"]硬件定義*/
static
struct snd_pcm_hardware snd_mychip_capture_hw = {
.info
= (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_BLOCK_TRANSFER
|
SNDRV_PCM_INFO_MMAP_VALID),
.formats
= SNDRV_PCM_FORMAT_S16_LE,
.rates
= SNDRV_PCM_RATE_8000_48000[color="#000000"],
.rate_min
= 8000,
.rate_max
= 48000,
.channels_min
= 2,
.channels_max
= 2,
.buffer_bytes_max
= 32768,
.period_bytes_min
= 4096,
.period_bytes_max
= 32768,
.periods_min
= 1,
.periods_max
= 1024,
[color="#000000"] };
[color="#000000"]
[color="#000000"] /*[color="#000000"]播放open[color="#000000"]函數(shù)*/
static
int snd_mychip_playback_open(struct snd_pcm_substream *substream)
[color="#000000"] {
struct
mychip *chip = snd_pcm_substream_chip(substream);
struct
snd_pcm_runtime *runtime = substream->runtime;
[color="#000000"]
runtime->hw
= snd_mychip_playback_hw;
/*[color="#000000"]其他硬件初始化的部分在這里完成*/
return
0;
[color="#000000"] }
[color="#000000"] /*[color="#000000"]播放close[color="#000000"]函數(shù)*/
static
int snd_mychip_playback_close(struct snd_pcm_substream *substream)
[color="#000000"] {
struct
mychip *chip = snd_pcm_substream_chip(substream);
//[color="#000000"]硬件相關代碼寫在這
return
0;
[color="#000000"] }
[color="#000000"] /*[color="#000000"]錄音open[color="#000000"]函數(shù)*/
static
int snd_mychip_capture_open(struct snd_pcm_substream *substream)
[color="#000000"] {
struct
mychip *chip = snd_pcm_substream_chip(substream);
struct
snd_pcm_runtime *runtime = substream->runtime;
runtime->hw
= snd_mychip_capture_hw;
/*[color="#000000"]其他硬件初始化的部分在這里完成*/
return
0;
[color="#000000"] }
/*[color="#000000"]錄音close[color="#000000"]函數(shù)*/
static
int snd_mychip_capture_close(struct snd_pcm_substream *substream)
[color="#000000"] {
struct
mychip *chip = snd_pcm_substream_chip(substream);
//[color="#000000"]硬件相關代碼寫在這
return
0;
[color="#000000"] }
/*hw_params[color="#000000"]函數(shù)*/
static
int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,
struct
snd_pcm_hw_params *hw_params)
[color="#000000"] {
return
snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
[color="#000000"] }
[color="#000000"] /*hw_free[color="#000000"]函數(shù)*/
static
int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
[color="#000000"] {
return
snd_pcm_lib_free_pages(substream);
[color="#000000"] }
/*prepare函數(shù)*/
static
int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
{
struct
mychip *chip = snd_pcm_substream_chip(substream);
struct
snd_pcm_runtime *runtime = substream->runtime;
/*在此做設定一些硬件配置
*例如....
*/
mychip_set_sample_format(chip,
runtime->format);
mychip_set_sample_rate(chip,
runtime->rate);
mychip_set_channels(chip,
runtime->channels);
mychip_set_dma_setup(chip,
runtime->dma_addr,
chip->buffer_size,
chip->period_size);
return
0;
}
/*trigger函數(shù)*/
static
int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
switch(cmd){
case
SNDRV_PCM_TRIGGER_START:
//啟動一個PCM引擎
break;
case
SNDRV_PCM_TRIGGER_STOP:
//停止一個PCM引擎
break;
default:
return
-EINVAL;
}
}
/*pointer函數(shù)*/
static
snd_pcm_uframes_t
snd_mychip_pcm_pointer(struct
snd_pcm_substream *substream)
{
struct
mychip *chip = snd_pcm_substream_chip(substream)
unsigned
int current_ptr;
/*得到當前緩沖區(qū)的硬件位置*/
current_ptr
= mychip_get_hw_pointer(chip);
return
current_ptr;
}
/*操作函數(shù)*/
static
struct snd_pcm_ops snd_mychip_playback_ops = {
.open
= snd_mychip_playback_open,
.close
= snd_mychip_playback_close,
.ioctl
= snd_pcm_lib_ioctl,
.hw_params
= snd_mychip_pcm_hw_params,
.hw_free
= snd_mychip_pcm_hw_free,
.prepare
= snd_mychip_pcm_prepare,
.trigger
= snd_mychip_pcm_trigger,
.pointer
= snd_mychip_pcm_pointer,
};
/*操作函數(shù)*/
static
struct snd_pcm_ops snd_mychip_capture_ops = {
.open
= snd_mychip_capture_open,
.close
= snd_mychip_capture_close,
.ioctl
= snd_pcm_lib_ioctl,
.hw_params
= snd_mychip_pcm_hw_params,
.hw_free
= snd_mychip_pcm_hw_free,
.prepare
= snd_mychip_pcm_prepare,
.trigger
= snd_mychip_pcm_trigger,
.pointer
= snd_mychip_pcm_pointer,
};
/*關于錄音的定義在這里省略....*/
/*創(chuàng)建一個pcm設備*/
static
int __devinit snd_mychip_new_pcm(struct mychip *chip)
{
struct
snd_pcm *pcm;
int
err;
if
((err = snd_pcm_new(chip->card, “My Chip”, 0 , 1, 1,&pcm)
return
err;
pcm->private_data
= chip;
strcpy(pcm->name,”My
Chip”);
chip->pcm
= pcm;
/*設定操作函數(shù)*/
snd_pcm_set_ops(pcm,SNDRV_PCM_STREAM_PLAYBACK,
&snd_mychip_playback_ops);
snd_pcm_set_ops(pcm,SNDRV_PCM_STREAM_CAPTURE,
&snd_mychip_capture_ops);
/*預分配緩沖區(qū)
*注意:可能會失敗
*/
snd_pcm_lib_preallocate_pages_for_all(pcm,SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024,64*1024);
return
0;
}
構造函數(shù)
通過snd_pcm_new()來分配一個pcm實例。更好的方式是為pcm創(chuàng)建一個構造函數(shù)。
static
int __devinit snd_mychip_new_pcm(struct mychip *chip)
{
struct
snd_pcm *pcm;
int
err;
if
((err = snd_pcm_new(chip->card, “My Chip”, 0 , 1, 1,&pcm)
return
err;
pcm->private_data
= chip;
strcpy(pcm->name,”My
Chip”);
chip->pcm
= pcm;
....
return
0;
}
snd_pcm_new()需要4個參數(shù)。第一個是card指針,第二個是標識符字符串,第三個是PCM設備索引。假如你創(chuàng)建了超過一個pcm實例,通過設備索引參數(shù)來區(qū)分pcm設備。例如:索引為1代表是第二個PCM設備。
第四個和第五個參數(shù)是表示播放和錄音的子流的數(shù)目。上面的示例都是為1。當沒有播放或錄音可以用的時候,可以設定對應的參數(shù)為0。
假如聲卡支持多個播放和錄音子流,你可以分配更多的號碼,但是它們必須可以在open()和close()函數(shù)中被正確處理。你可以通過snd_pcm_substream的成員變量number來知道那用到的是那個子流。如:
struct
snd_pcm_substream *substream;
int
index = substream->number;
pcm創(chuàng)建之后,必須設定pcm流的操作函數(shù)。
snd_pcm_set_ops(pcm,SNDRV_PCM_STREAM_PLAYBACK,
&snd_mychip_playback_ops);
snd_pcm_set_ops(pcm,SNDRV_PCM_STREAM_CAPTURE,
&snd_mychip_capture_ops);
操作函數(shù)類似如下:
/*操作函數(shù)*/
static
struct snd_pcm_ops snd_mychip_playback_ops = {
.open
= snd_mychip_playback_open,
.close
= snd_mychip_playback_close,
.ioctl
= snd_pcm_lib_ioctl,
.hw_params
= snd_mychip_pcm_hw_params,
.hw_free
= snd_mychip_pcm_hw_free,
.prepare
= snd_mychip_pcm_prepare,
.trigger
= snd_mychip_pcm_trigger,
.pointer
= snd_mychip_pcm_pointer,
};
每一個回調(diào)函數(shù)都會在“操作函數(shù)”一節(jié)中詳細介紹。
設定完操作函數(shù)之后,大部分情況要預分配緩沖區(qū)。一般情況采用如下方式分配:
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024,64*1024);
默認它會分配64KB的緩沖。關于緩沖區(qū)的管理會在“緩沖區(qū)和內(nèi)存管理”一章詳細描述。
除此之外,你還可以為這個pcm設定一些附加的信息保存在pcm->info_flags變量中。一些可用的變量定義例如SNDRV_PCM_INFO_XXX都在中,這些主要是為了硬件定義的(稍后會詳細描述)。假如你的聲卡僅僅支持半雙工,你要指定如下:
pcm->info_flags
= SNDDRV_PCM_INFO_HALF_DUPLEX;
到了析構部分?
一個pcm實例不是總是要求析構的。既然pcm中間層會自動的把pcm設備是否,你就不用特別的在調(diào)用析構函數(shù)了。
析構函數(shù)在一種情況下是必須的,就是你在內(nèi)部創(chuàng)建了一個特殊的的記錄,需要自己去釋放他們。這種情況下,你可以把pcm->private_free指向你的析構函數(shù)。
Example5-2.含有一個析構函數(shù)的PCM實例
static
void mychip_pcm_free(struct snd_pcm *pcm)
{
struct
mychip *chip = snd_pcm_chip(pcm);
/*釋放你自己的數(shù)據(jù)*/
kfree(chip->my_private_pcm_data);
//做其他事情
}
static
int __devinit snd_mychip_new_pcm(struct mychip *chip)
{
struct
snd_pcm *pcm;
/*分配你自己的數(shù)據(jù)*/
chip->myprivate_pcm_data
= kmalloc(..);
/*設定析構函數(shù)*/
pcm->private_data
= chip;
pcm->private_free
= mychip_pcm_free;
....
}
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u1/49088/showart_1006030.html |
|