- 论坛徽章:
- 0
|
翻译:creator
sz111@126.com
第五章
PCM[color="#000000"]接口
[color="#000000"]概述
PCM[color="#000000"]中间层是ALSA[color="#000000"]中作用非常大的。它是唯一需要在每个驱动中都需要实现的low-level[color="#000000"]的硬件接口。
为了访问PCM[color="#000000"]层,你需要包含[color="#000000"]。除此之外,如果你要操作hw_param[color="#000000"]相关的函数,还需要包含[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"]放音设备。事实上,每次被打开的时候,一个可用的子流会自动的被选中和打开。同时,当一个子流已经存在,并且已经被打开,当再次被打开的时候,会被阻塞,或者根据打开文件的模式不同返回一个EAGAIND[color="#000000"]的错误信息。你也不需要知道你的驱动细节部分,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"]函数*/
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"]函数*/
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"]函数*/
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"]函数*/
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"]函数*/
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"]函数*/
static
int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
[color="#000000"] {
return
snd_pcm_lib_free_pages(substream);
[color="#000000"] }
/*prepare函数*/
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函数*/
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函数*/
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;
/*得到当前缓冲区的硬件位置*/
current_ptr
= mychip_get_hw_pointer(chip);
return
current_ptr;
}
/*操作函数*/
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,
};
/*操作函数*/
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,
};
/*关于录音的定义在这里省略....*/
/*创建一个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;
/*设定操作函数*/
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);
/*预分配缓冲区
*注意:可能会失败
*/
snd_pcm_lib_preallocate_pages_for_all(pcm,SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024,64*1024);
return
0;
}
构造函数
通过snd_pcm_new()来分配一个pcm实例。更好的方式是为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;
....
return
0;
}
snd_pcm_new()需要4个参数。第一个是card指针,第二个是标识符字符串,第三个是PCM设备索引。假如你创建了超过一个pcm实例,通过设备索引参数来区分pcm设备。例如:索引为1代表是第二个PCM设备。
第四个和第五个参数是表示播放和录音的子流的数目。上面的示例都是为1。当没有播放或录音可以用的时候,可以设定对应的参数为0。
假如声卡支持多个播放和录音子流,你可以分配更多的号码,但是它们必须可以在open()和close()函数中被正确处理。你可以通过snd_pcm_substream的成员变量number来知道那用到的是那个子流。如:
struct
snd_pcm_substream *substream;
int
index = substream->number;
pcm创建之后,必须设定pcm流的操作函数。
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);
操作函数类似如下:
/*操作函数*/
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,
};
每一个回调函数都会在“操作函数”一节中详细介绍。
设定完操作函数之后,大部分情况要预分配缓冲区。一般情况采用如下方式分配:
snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024,64*1024);
默认它会分配64KB的缓冲。关于缓冲区的管理会在“缓冲区和内存管理”一章详细描述。
除此之外,你还可以为这个pcm设定一些附加的信息保存在pcm->info_flags变量中。一些可用的变量定义例如SNDRV_PCM_INFO_XXX都在中,这些主要是为了硬件定义的(稍后会详细描述)。假如你的声卡仅仅支持半双工,你要指定如下:
pcm->info_flags
= SNDDRV_PCM_INFO_HALF_DUPLEX;
到了析构部分?
一个pcm实例不是总是要求析构的。既然pcm中间层会自动的把pcm设备是否,你就不用特别的在调用析构函数了。
析构函数在一种情况下是必须的,就是你在内部创建了一个特殊的的记录,需要自己去释放他们。这种情况下,你可以把pcm->private_free指向你的析构函数。
Example5-2.含有一个析构函数的PCM实例
static
void mychip_pcm_free(struct snd_pcm *pcm)
{
struct
mychip *chip = snd_pcm_chip(pcm);
/*释放你自己的数据*/
kfree(chip->my_private_pcm_data);
//做其他事情
}
static
int __devinit snd_mychip_new_pcm(struct mychip *chip)
{
struct
snd_pcm *pcm;
/*分配你自己的数据*/
chip->myprivate_pcm_data
= kmalloc(..);
/*设定析构函数*/
pcm->private_data
= chip;
pcm->private_free
= mychip_pcm_free;
....
}
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/49088/showart_1006030.html |
|