snd_soc_codec_device 之 snd_soc_new_pcms

转自:

snd_soc_codec_device 之 snd_soc_new_pcms


以wolfsen的wm8900 codec为例,在wm8900.c中可以看到

struct snd_soc_codec_device soc_codec_dev_wm8900 = {

.probe = wm8900_probe,
.remove = wm8900_remove,
.suspend = wm8900_suspend,
.resume = wm8900_resume,
};

EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900);


本文先介绍第一个回调函数probe

probe函数传入的只有一个参数是struct platform_device *pdev,结构体在platform_device.h中定义,

函数platform_get_drvdata()从probe的参数获取SoC Device,得到的socdev会在后面设备驱动注册使用


1、snd_soc_new_pcms

接下来是Register pcms,函数snd_soc_new_pcms()有三个参数struct snd_soc_device *socdev、 int idx、 const char *xid,

第一个socdev就是上一步获得到的SoC Device;后面的 int型idx 和 指针型 xid,会在注册声卡时使用,函数是snd_card_create()


(1)snd_card_create

声卡注册函数snd_card_create先调用kzalloc分配一块专有内存,如果分配不到,注册失败返回;

然后拷贝声卡的ID字符串

  1. if (xid)
  2. strlcpy(card->id, xid, sizeof(card->id));

接下来是在slot中,为新建声卡分配一个索引号,这一部分的操作用mutex_lock、mutex_unlock保护起来

分配索引的代码

  1. if (idx < 0) {    //idx == -1,表示自动分配索引号
  2. for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
  3. if (~snd_cards_lock & idx & 1<<idx2) {
  4. if (module_slot_match(module, idx2)) {
  5. idx = idx2;
  6. break;
  7. }
  8. }
  9. }
  10. if (idx < 0) {   //idx == -1,表示自动分配索引号
  11. for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++)
  12. if (~snd_cards_lock & idx & 1<<idx2) {
  13. if (!slots[idx2] || !*slots[idx2]) {
  14. idx = idx2;
  15. break;
  16. }
  17. }
  18. }
  19. 其中,SNDRV_CARDS定义在croe.h中,表示最多支持声卡数目

  1. #ifdef CONFIG_SND_DYNAMIC_MINORS
  2. #define SNDRV_CARDS 32
  3. #else
  4. #define SNDRV_CARDS 8
  5. #endif

for循环里面的if (~snd_cards_lock & idx & 1<<idx2),是在slots中查找当前没有被使用的id,

snd_cards_lock 在/sound/core的init.c中定义

static unsigned int snd_cards_lock;/* locked for registering/using */

for循环结束后如果id还是-1,直接返回,注册失败;

  1. if (idx < 0)
  2. err = -ENODEV;
  3. else if (idx < snd_ecards_limit) {
  4. if (snd_cards_lock & (1 << idx))
  5. err = -EBUSY;/* invalid */
  6. } else if (idx >= SNDRV_CARDS)
  7. err = -ENODEV;

 snd_ecards_limit在sound/core的sound.c中被赋值snd_ecards_limit = cards_limit,而static int cards_limit = 1;

所以第二个if是判别如果idx为0,再次检查该id的声卡是否在被使用,处于busy状态;

第三个是检查id号是否超过最大支持声卡数目,这个在上面提到过。


到此,如果没有被返回,说明已经成功自动分配了一个id给字符设备了,接下来把snd_cards_lock这个全局变量赋值相应的id位,

表示这个id已经被使用了,如果id刚好是最大的临界值,把id上限加1。


后面是初始化snd_card结构中必要的字段,

  1. card->number = idx;
  2. card->module = module;
  3. INIT_LIST_HEAD(&card->devices);
  4. init_rwsem(&card->controls_rwsem);
  5. rwlock_init(&card->ctl_files_rwlock);
  6. INIT_LIST_HEAD(&card->controls);
  7. INIT_LIST_HEAD(&card->ctl_files);
  8. spin_lock_init(&card->files_lock);
  9. INIT_LIST_HEAD(&card->files_list);
  10. init_waitqueue_head(&card->shutdown_sleep);
  11. #ifdef CONFIG_PM
  12. mutex_init(&card->power_lock);
  13. init_waitqueue_head(&card->power_sleep);
  14. #endif

建立逻辑设备:Control

  1. /* the control interface cannot be accessed from the user space until */  
  2. /* snd_cards_bitmask and snd_cards are set with snd_card_register */  
  3. err = snd_ctl_create(card);  

建立proc文件中的info节点:通常就是/proc/asound/card0

  1. err = snd_info_card_create(card);  

把第一步分配的内存指针放入private_data字段中:

  1. if (extra_size > 0)   
  2.     card->private_data = (char *)card + sizeof(struct snd_card);  

(2)soc_new_pcm

根据card->num_links,创建所有的pcm,主要是播放流playback 和录音流record ,每次创建新PCM都是调用

static int soc_new_pcm(struct snd_soc_device *socdev,
struct snd_soc_dai_link *dai_link, int num)


第一个参数讲过了,第二个是设备和cpu连接的结构体,在include/sound的soc.h中定义,第三个num是PCM的id


在调用snd_pcm_new之前,先生成设备字符串

sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
num);


生成的结果new_name作为 char *id 参数传给snd_pcm_new

int snd_pcm_new(struct snd_card *card, const char *id, int device,
int playback_count, int capture_count,
       struct snd_pcm ** rpcm)

函数返回新建生成的snd_pcm指针rpcm


其实这些都是在填充结构体struct snd_pcm,上面已经做了一部分,包括id、pcm名称、card、device等的填充,代码的接下部分是

填充pcm的ops部分,

soc_pcm_ops.mmap = platform->pcm_ops->mmap;
soc_pcm_ops.pointer = platform->pcm_ops->pointer;
soc_pcm_ops.ioctl = platform->pcm_ops->ioctl;
soc_pcm_ops.copy = platform->pcm_ops->copy;
soc_pcm_ops.silence = platform->pcm_ops->silence;
soc_pcm_ops.ack = platform->pcm_ops->ack;
soc_pcm_ops.page = platform->pcm_ops->page;


填充完ops,就调用snd_pcm_set_ops把ops和音频流关联起来

if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);

if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);


在终端用ls /dev/snd可以看到这部分完成的结果,如

  • controlC0 --> 用于声卡的控制,例如通道选择,混音,麦克风的控制等
  • midiC0D0 --> 用于播放midi 音频
  • pcmC0D0c --〉用于录音的pcm 设备
  • pcmC0D0p --〉用于播放的pcm 设备
  • seq --〉音序器
  • timer --〉定时器

最后把codec->card, codec_dai, pcm关联起来,完成这部分函数功能。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值