概述
在上节
Linux音频驱动-ALSA概述中介绍了整个ALSA的构成,接口以及函数入口的分析。
本节将从声卡的创建开始,也就是card的创建。card是整个声卡的最底层结构,用于管理该声卡下的所有设备包括control, pcm,timer等。在linux系统中使用struct snd_card结构代表一个card。
数据结构
声卡的主要数据结构struct snd_card
- struct snd_card {
- int number; /* number of soundcard (index to
- snd_cards) */
- char id[16]; /* id string of this card */
- char driver[16]; /* driver name */
- char shortname[32]; /* short name of this soundcard */
- char longname[80]; /* name of this soundcard */
- char mixername[80]; /* mixer name */
- char components[128]; /* card components delimited with
- space */
- struct module *module; /* top-level module */
- void *private_data; /* private data for soundcard */
- void (*private_free) (struct snd_card *card); /* callback for freeing of
- private data */
- struct list_head devices; /* devices */
- unsigned int last_numid; /* last used numeric ID */
- struct rw_semaphore controls_rwsem; /* controls list lock */
- rwlock_t ctl_files_rwlock; /* ctl_files list lock */
- int controls_count; /* count of all controls */
- int user_ctl_count; /* count of all user controls */
- struct list_head controls; /* all controls for this card */
- struct list_head ctl_files; /* active control files */
- struct mutex user_ctl_lock; /* protects user controls against
- concurrent access */
- struct snd_info_entry *proc_root; /* root for soundcard specific files */
- struct snd_info_entry *proc_id; /* the card id */
- struct proc_dir_entry *proc_root_link; /* number link to real id */
- struct list_head files_list; /* all files associated to this card */
- struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown
- state */
- spinlock_t files_lock; /* lock the files for this card */
- int shutdown; /* this card is going down */
- struct completion *release_completion;
- struct device *dev; /* device assigned to this card */
- struct device card_dev; /* cardX object for sysfs */
- bool registered; /* card_dev is registered? */
- #ifdef CONFIG_PM
- unsigned int power_state; /* power state */
- struct mutex power_lock; /* power lock */
- wait_queue_head_t power_sleep;
- #endif
- #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
- struct snd_mixer_oss *mixer_oss;
- int mixer_oss_change_count;
- #endif
- };
.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函数。
- int snd_card_new(struct device *parent, int idx, const char *xid,
- struct module *module, int extra_size,
- struct snd_card **card_ret)
- {
- struct snd_card *card;
- int err;
- if (snd_BUG_ON(!card_ret))
- return -EINVAL;
- *card_ret = NULL;
- if (extra_size < 0)
- extra_size = 0;
- card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
- if (!card)
- return -ENOMEM;
- if (extra_size > 0)
- card->private_data = (char *)card + sizeof(struct snd_card);
- if (xid)
- strlcpy(card->id, xid, sizeof(card->id));
- err = 0;
- mutex_lock(&snd_card_mutex);
- if (idx < 0) /* first check the matching module-name slot */
- idx = get_slot_from_bitmask(idx, module_slot_match, module);
- if (idx < 0) /* if not matched, assign an empty slot */
- idx = get_slot_from_bitmask(idx, check_empty_slot, module);
- if (idx < 0)
- err = -ENODEV;
- else if (idx < snd_ecards_limit) {
- if (test_bit(idx, snd_cards_lock))
- err = -EBUSY; /* invalid */
- } else if (idx >= SNDRV_CARDS)
- err = -ENODEV;
- if (err < 0) {
- mutex_unlock(&snd_card_mutex);
- dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
- idx, snd_ecards_limit - 1, err);
- kfree(card);
- return err;
- }
- set_bit(idx, snd_cards_lock); /* lock it */
- if (idx >= snd_ecards_limit)
- snd_ecards_limit = idx + 1; /* increase the limit */
- mutex_unlock(&snd_card_mutex);
- card->dev = parent;
- card->number = idx;
- card->module = module;
- INIT_LIST_HEAD(&card->devices);
- init_rwsem(&card->controls_rwsem);
- rwlock_init(&card->ctl_files_rwlock);
- mutex_init(&card->user_ctl_lock);
- INIT_LIST_HEAD(&card->controls);
- INIT_LIST_HEAD(&card->ctl_files);
- spin_lock_init(&card->files_lock);
- INIT_LIST_HEAD(&card->files_list);
- #ifdef CONFIG_PM
- mutex_init(&card->power_lock);
- init_waitqueue_head(&card->power_sleep);
- #endif
- device_initialize(&card->card_dev);
- card->card_dev.parent = parent;
- card->card_dev.class = sound_class;
- card->card_dev.release = release_card_device;
- card->card_dev.groups = card_dev_attr_groups;
- err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
- if (err < 0)
- goto __error;
- /* the control interface cannot be accessed from the user space until */
- /* snd_cards_bitmask and snd_cards are set with snd_card_register */
- err = snd_ctl_create(card);
- if (err < 0) {
- dev_err(parent, "unable to register control minors\n");
- goto __error;
- }
- err = snd_info_card_create(card);
- if (err < 0) {
- dev_err(parent, "unable to create card info\n");
- goto __error_ctl;
- }
- *card_ret = card;
- return 0;
- __error_ctl:
- snd_device_free_all(card);
- __error:
- put_device(&card->card_dev);
- return err;
- }
2. 根据card id赋值card->id成员,此成员通话是ASCII String。
3. 如果idx= -1,系统会自动分配一个索引编号。
4. card变量的一系列初始化,包括devices, controls等。
5. 初始化card_dev设备,设置parent, class, 设备属性,以及name。
sound_class会在init_soundcore中做初始化操作。
- static int __init init_soundcore(void)
- {
- int rc;
- rc = init_oss_soundcore();
- if (rc)
- return rc;
- sound_class = class_create(THIS_MODULE, "sound");
- if (IS_ERR(sound_class)) {
- cleanup_oss_soundcore();
- return PTR_ERR(sound_class);
- }
- sound_class->devnode = sound_devnode;
- return 0;
- }
7. 调用snd_info_card_create函数在proc下创建card0目录。
- int snd_info_card_create(struct snd_card *card)
- {
- char str[8];
- struct snd_info_entry *entry;
- if (snd_BUG_ON(!card))
- return -ENXIO;
- sprintf(str, "card%i", card->number);
- if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
- return -ENOMEM;
- entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
- if (snd_info_register(entry) < 0) {
- snd_info_free_entry(entry);
- return -ENOMEM;
- }
- card->proc_root = entry;
- return 0;
- }
声卡的注册
在声卡初始化,会在驱动程序中设置card,完后会调用snd_card_register注册此card到系统中去。
- int snd_card_register(struct snd_card *card)
- {
- int err;
- if (snd_BUG_ON(!card))
- return -EINVAL;
- if (!card->registered) {
- err = device_add(&card->card_dev);
- if (err < 0)
- return err;
- card->registered = true;
- }
- if ((err = snd_device_register_all(card)) < 0)
- return err;
- mutex_lock(&snd_card_mutex);
- if (snd_cards[card->number]) {
- /* already registered */
- mutex_unlock(&snd_card_mutex);
- return 0;
- }
- if (*card->id) {
- /* make a unique id name from the given string */
- char tmpid[sizeof(card->id)];
- memcpy(tmpid, card->id, sizeof(card->id));
- snd_card_set_id_no_lock(card, tmpid, tmpid);
- } else {
- /* create an id from either shortname or longname */
- const char *src;
- src = *card->shortname ? card->shortname : card->longname;
- snd_card_set_id_no_lock(card, src,
- retrieve_id_from_card_name(src));
- }
- snd_cards[card->number] = card;
- mutex_unlock(&snd_card_mutex);
- init_info_for_card(card);
- #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
- if (snd_mixer_oss_notify_callback)
- snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
- #endif
- return 0;
- }
2. 根据card的registered判断是否已经注册,如果注册继续。否则调用device_add添加设备,设置registered标志。
3. 调用snd_device_register_all注册所有card的设备,包括pcm, control等
- int snd_device_register_all(struct snd_card *card)
- {
- struct snd_device *dev;
- int err;
- if (snd_BUG_ON(!card))
- return -ENXIO;
- list_for_each_entry(dev, &card->devices, list) {
- err = __snd_device_register(dev);
- if (err < 0)
- return err;
- }
- return 0;
- }
- static int __snd_device_register(struct snd_device *dev)
- {
- if (dev->state == SNDRV_DEV_BUILD) {
- if (dev->ops->dev_register) {
- int err = dev->ops->dev_register(dev);
- if (err < 0)
- return err;
- }
- dev->state = SNDRV_DEV_REGISTERED;
- }
- return 0;
- }
4. 会在snd_cards结构体数组中查找是否已经存在。
5. 通过给定的string, 设置一个独一无二的id name,先后会通过id,shortname, longname设置此 unique id。
6. 在snd_cards结构体数组中设置此card,根据number为下标。
7. 调用init_info_for_card函数在card0下创建id。
至此,整个声卡就完成了创建过程。