免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1239 | 回复: 0
打印 上一主题 下一主题

writing-an-alsa-driver(编写一个ALSA驱动)翻译稿 第五章(1) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-20 13:59 |只看该作者 |倒序浏览

       
       
翻译: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
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP