Linux音频驱动-Card创建

概述

在上节 Linux音频驱动-ALSA概述中介绍了整个ALSA的构成,接口以及函数入口的分析。
本节将从声卡的创建开始,也就是card的创建。card是整个声卡的最底层结构,用于管理该声卡下的所有设备包括control, pcm,timer等。在linux系统中使用struct snd_card结构代表一个card。

数据结构

声卡的主要数据结构struct snd_card
[cpp]  view plain  copy
  1. struct snd_card {  
  2.     int number;         /* number of soundcard (index to 
  3.                                 snd_cards) */  
  4.   
  5.     char id[16];            /* id string of this card */  
  6.     char driver[16];        /* driver name */  
  7.     char shortname[32];     /* short name of this soundcard */  
  8.     char longname[80];      /* name of this soundcard */  
  9.     char mixername[80];     /* mixer name */  
  10.     char components[128];       /* card components delimited with 
  11.                                 space */  
  12.     struct module *module;      /* top-level module */  
  13.   
  14.     void *private_data;     /* private data for soundcard */  
  15.     void (*private_free) (struct snd_card *card); /* callback for freeing of 
  16.                                 private data */  
  17.     struct list_head devices;   /* devices */  
  18.   
  19.     unsigned int last_numid;    /* last used numeric ID */  
  20.     struct rw_semaphore controls_rwsem; /* controls list lock */  
  21.     rwlock_t ctl_files_rwlock;  /* ctl_files list lock */  
  22.     int controls_count;     /* count of all controls */  
  23.     int user_ctl_count;     /* count of all user controls */  
  24.     struct list_head controls;  /* all controls for this card */  
  25.     struct list_head ctl_files; /* active control files */  
  26.     struct mutex user_ctl_lock; /* protects user controls against 
  27.                        concurrent access */  
  28.   
  29.     struct snd_info_entry *proc_root;   /* root for soundcard specific files */  
  30.     struct snd_info_entry *proc_id; /* the card id */  
  31.     struct proc_dir_entry *proc_root_link;  /* number link to real id */  
  32.   
  33.     struct list_head files_list;    /* all files associated to this card */  
  34.     struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown 
  35.                                 state */  
  36.     spinlock_t files_lock;      /* lock the files for this card */  
  37.     int shutdown;           /* this card is going down */  
  38.     struct completion *release_completion;  
  39.     struct device *dev;     /* device assigned to this card */  
  40.     struct device card_dev;     /* cardX object for sysfs */  
  41.     bool registered;        /* card_dev is registered? */  
  42.   
  43. #ifdef CONFIG_PM  
  44.     unsigned int power_state;   /* power state */  
  45.     struct mutex power_lock;    /* power lock */  
  46.     wait_queue_head_t power_sleep;  
  47. #endif  
  48.   
  49. #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)  
  50.     struct snd_mixer_oss *mixer_oss;  
  51.     int mixer_oss_change_count;  
  52. #endif  
  53. };  
此结构注释写的很详细,在此只需要关注重点字段,用到的时候再说。

.number:    soundcard的序号,通常为0。
.id:              card的标识符,通常是字符串形式。
.driver/shortname/longname:  会在具体驱动中设置,主要反映在/proc/asound/cards中。
.private_data:   card的私有数据。
.devices:     用于管理该card下所有的设备。
.controls:    用于管理该card下的所有control设备。 
.ctl_files:     用于管理该card下的active的control设备。
.dev:             和card相关的设备。
.card_dev:    card用于在sys中显示,用于代表该card。
registered:    代表是否在系统中注册了card。

创建并且初始化card

几乎所有的音频驱动都会在刚驱动的开始创建card,通常会使用snd_card_new函数。
[cpp]  view plain  copy
  1. int snd_card_new(struct device *parent, int idx, const char *xid,  
  2.             struct module *module, int extra_size,  
  3.             struct snd_card **card_ret)  
  4. {  
  5.     struct snd_card *card;  
  6.     int err;  
  7.   
  8.     if (snd_BUG_ON(!card_ret))  
  9.         return -EINVAL;  
  10.     *card_ret = NULL;  
  11.   
  12.     if (extra_size < 0)  
  13.         extra_size = 0;  
  14.     card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);  
  15.     if (!card)  
  16.         return -ENOMEM;  
  17.     if (extra_size > 0)  
  18.         card->private_data = (char *)card + sizeof(struct snd_card);  
  19.     if (xid)  
  20.         strlcpy(card->id, xid, sizeof(card->id));  
  21.     err = 0;  
  22.     mutex_lock(&snd_card_mutex);  
  23.     if (idx < 0) /* first check the matching module-name slot */  
  24.         idx = get_slot_from_bitmask(idx, module_slot_match, module);  
  25.     if (idx < 0) /* if not matched, assign an empty slot */  
  26.         idx = get_slot_from_bitmask(idx, check_empty_slot, module);  
  27.     if (idx < 0)  
  28.         err = -ENODEV;  
  29.     else if (idx < snd_ecards_limit) {  
  30.         if (test_bit(idx, snd_cards_lock))  
  31.             err = -EBUSY;   /* invalid */  
  32.     } else if (idx >= SNDRV_CARDS)  
  33.         err = -ENODEV;  
  34.     if (err < 0) {  
  35.         mutex_unlock(&snd_card_mutex);  
  36.         dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",  
  37.              idx, snd_ecards_limit - 1, err);  
  38.         kfree(card);  
  39.         return err;  
  40.     }  
  41.     set_bit(idx, snd_cards_lock);       /* lock it */  
  42.     if (idx >= snd_ecards_limit)  
  43.         snd_ecards_limit = idx + 1; /* increase the limit */  
  44.     mutex_unlock(&snd_card_mutex);  
  45.     card->dev = parent;  
  46.     card->number = idx;  
  47.     card->module = module;  
  48.     INIT_LIST_HEAD(&card->devices);  
  49.     init_rwsem(&card->controls_rwsem);  
  50.     rwlock_init(&card->ctl_files_rwlock);  
  51.     mutex_init(&card->user_ctl_lock);  
  52.     INIT_LIST_HEAD(&card->controls);  
  53.     INIT_LIST_HEAD(&card->ctl_files);  
  54.     spin_lock_init(&card->files_lock);  
  55.     INIT_LIST_HEAD(&card->files_list);  
  56. #ifdef CONFIG_PM  
  57.     mutex_init(&card->power_lock);  
  58.     init_waitqueue_head(&card->power_sleep);  
  59. #endif  
  60.   
  61.     device_initialize(&card->card_dev);  
  62.     card->card_dev.parent = parent;  
  63.     card->card_dev.class = sound_class;  
  64.     card->card_dev.release = release_card_device;  
  65.     card->card_dev.groups = card_dev_attr_groups;  
  66.     err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);  
  67.     if (err < 0)  
  68.         goto __error;  
  69.   
  70.     /* the control interface cannot be accessed from the user space until */  
  71.     /* snd_cards_bitmask and snd_cards are set with snd_card_register */  
  72.     err = snd_ctl_create(card);  
  73.     if (err < 0) {  
  74.         dev_err(parent, "unable to register control minors\n");  
  75.         goto __error;  
  76.     }  
  77.     err = snd_info_card_create(card);  
  78.     if (err < 0) {  
  79.         dev_err(parent, "unable to create card info\n");  
  80.         goto __error_ctl;  
  81.     }  
  82.     *card_ret = card;  
  83.     return 0;  
  84.   
  85.       __error_ctl:  
  86.     snd_device_free_all(card);  
  87.       __error:  
  88.     put_device(&card->card_dev);  
  89.     return err;  
  90. }  
1.   通过kzalloc分配一个snd_card结构。根据是否有extra_size,设置card的私有数据private_data。
2.   根据card id赋值card->id成员,此成员通话是ASCII String。
3.   如果idx= -1,系统会自动分配一个索引编号。
4.   card变量的一系列初始化,包括devices, controls等。
5.   初始化card_dev设备,设置parent, class, 设备属性,以及name。
sound_class会在init_soundcore中做初始化操作。
[cpp]  view plain  copy
  1. static int __init init_soundcore(void)  
  2. {  
  3.     int rc;  
  4.   
  5.     rc = init_oss_soundcore();  
  6.     if (rc)  
  7.         return rc;  
  8.   
  9.     sound_class = class_create(THIS_MODULE, "sound");  
  10.     if (IS_ERR(sound_class)) {  
  11.         cleanup_oss_soundcore();  
  12.         return PTR_ERR(sound_class);  
  13.     }  
  14.   
  15.     sound_class->devnode = sound_devnode;  
  16.   
  17.     return 0;  
  18. }  
6.   创建card的control设备。根据注释control接口在snd_card_register之后,用户空间才可以访问。control设备在control小节详解。
7.   调用snd_info_card_create函数在proc下创建card0目录。
[cpp]  view plain  copy
  1. int snd_info_card_create(struct snd_card *card)  
  2. {  
  3.     char str[8];  
  4.     struct snd_info_entry *entry;  
  5.   
  6.     if (snd_BUG_ON(!card))  
  7.         return -ENXIO;  
  8.   
  9.     sprintf(str, "card%i", card->number);  
  10.     if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)  
  11.         return -ENOMEM;  
  12.     entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;  
  13.     if (snd_info_register(entry) < 0) {  
  14.         snd_info_free_entry(entry);  
  15.         return -ENOMEM;  
  16.     }  
  17.     card->proc_root = entry;  
  18.     return 0;  
  19. }  
最终会根据entry的mode,创建目录。

声卡的注册

在声卡初始化,会在驱动程序中设置card,完后会调用snd_card_register注册此card到系统中去。
[cpp]  view plain  copy
  1. int snd_card_register(struct snd_card *card)  
  2. {  
  3.     int err;  
  4.   
  5.     if (snd_BUG_ON(!card))  
  6.         return -EINVAL;  
  7.   
  8.     if (!card->registered) {  
  9.         err = device_add(&card->card_dev);  
  10.         if (err < 0)  
  11.             return err;  
  12.         card->registered = true;  
  13.     }  
  14.   
  15.     if ((err = snd_device_register_all(card)) < 0)  
  16.         return err;  
  17.     mutex_lock(&snd_card_mutex);  
  18.     if (snd_cards[card->number]) {  
  19.         /* already registered */  
  20.         mutex_unlock(&snd_card_mutex);  
  21.         return 0;  
  22.     }  
  23.     if (*card->id) {  
  24.         /* make a unique id name from the given string */  
  25.         char tmpid[sizeof(card->id)];  
  26.         memcpy(tmpid, card->id, sizeof(card->id));  
  27.         snd_card_set_id_no_lock(card, tmpid, tmpid);  
  28.     } else {  
  29.         /* create an id from either shortname or longname */  
  30.         const char *src;  
  31.         src = *card->shortname ? card->shortname : card->longname;  
  32.         snd_card_set_id_no_lock(card, src,  
  33.                     retrieve_id_from_card_name(src));  
  34.     }  
  35.     snd_cards[card->number] = card;  
  36.     mutex_unlock(&snd_card_mutex);  
  37.     init_info_for_card(card);  
  38. #if IS_ENABLED(CONFIG_SND_MIXER_OSS)  
  39.     if (snd_mixer_oss_notify_callback)  
  40.         snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);  
  41. #endif  
  42.     return 0;  
  43. }  
1.   合法性判断,如果此处card不存在,panic。
2.   根据card的registered判断是否已经注册,如果注册继续。否则调用device_add添加设备,设置registered标志。
3.   调用snd_device_register_all注册所有card的设备,包括pcm, control等
[cpp]  view plain  copy
  1. int snd_device_register_all(struct snd_card *card)  
  2. {  
  3.     struct snd_device *dev;  
  4.     int err;  
  5.       
  6.     if (snd_BUG_ON(!card))  
  7.         return -ENXIO;  
  8.     list_for_each_entry(dev, &card->devices, list) {  
  9.         err = __snd_device_register(dev);  
  10.         if (err < 0)  
  11.             return err;  
  12.     }  
  13.     return 0;  
  14. }  
从card的devices中依次取出一个devices,调用__snd_device_register函数注册。
[cpp]  view plain  copy
  1. static int __snd_device_register(struct snd_device *dev)  
  2. {  
  3.     if (dev->state == SNDRV_DEV_BUILD) {  
  4.         if (dev->ops->dev_register) {  
  5.             int err = dev->ops->dev_register(dev);  
  6.             if (err < 0)  
  7.                 return err;  
  8.         }  
  9.         dev->state = SNDRV_DEV_REGISTERED;  
  10.     }  
  11.     return 0;  
  12. }  
此函数最终会调用各个devices的snd_device_ops中的dev_register函数。
4.  会在snd_cards结构体数组中查找是否已经存在。
5.  通过给定的string, 设置一个独一无二的id name,先后会通过id,shortname, longname设置此 unique id。
6.  在snd_cards结构体数组中设置此card,根据number为下标。
7.  调用init_info_for_card函数在card0下创建id。

至此,整个声卡就完成了创建过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值