"本文转载自:[DroidPhone]的Linux ALSA声卡驱动之二:声卡的创建"
1.snd_card结构体
1.1 snd_card是什么
snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。
1.2 snd_card的定义
snd_card的定义位于该头文件中:include/sound/core.h
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 irq_descr[32]; /* Interrupt description */
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 */
struct device ctl_dev; /* control device */
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 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 */
const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */
bool registered; /* card_dev is registered? */
wait_queue_head_t remove_sleep;
#ifdef CONFIG_PM
unsigned int power_state; /* power state */
wait_queue_head_t power_sleep;
#endif
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
struct snd_mixer_oss *mixer_oss;
int mixer_oss_change_count;
#endif
};
- struct list_head devices:记录该声卡下所有逻辑设备的链表;
- struct list_head controls:记录该声卡下所有的控制单元的链表;
- void *private_data:声卡的私有数据,可以在创建声卡时通过参数指定数据的大小。
2.声卡的建立流程
2.1 第一步,创建snd_card的一个实例
- /sound/core/init.c
int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
- parent: the parent device object;
- idx:一个整数数值,该声卡的编号;
- xid:字符串,声卡的标示符;
- module: top level module for locking;
- extra_size: 该参数决定在建立snd_card实例时,须要同时额外分配数据的大小,该数据的指针最终会赋值给snd_card的private_data数据成员;
- card_ret: 返回所建立的snd_card实例指针。
2.2 第二步,建立声卡芯片专用数据
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
2.3 第三步,设置Driver的ID和名字
- /sound/pci/hda/hda_intel.c
strcpy(card->driver, "HDA-Intel");
strlcpy(card->shortname, driver_short_names[chip->driver_type],
sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname),
"%s at 0x%lx irq %i",
card->shortname, bus->addr, bus->irq);
snd_card的driver字段保存着芯片的ID字符串,user空间的alsa-lib会使用到该字符串,因此必需要保证该ID的惟一性,shortname字段更多的用于打印信息,longname字段会出如今/proc/asound/cards/中。
0 [PCH ]: HDA-Intel - HDA Intel PCH
HDA Intel PCH at 0xbf210000 irq 128
2.4 第四步,创建声卡的功能部件(逻辑设备),例如PCM,Mixer,MIDI等
这时候可以创建声卡的各种功能部件了,还记得开头的snd_card结构体的devices字段吗?每一种部件的创建最终会调用snd_device_new()来生成一个snd_device实例,并把该实例链接到snd_card的devices链表中。
通常,alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),比如:
PCM --- snd_pcm_new() --- /sound/core/pcm.c
RAWMIDI --- snd_rawmidi_new() --- /sound/core/rawmidi.c
CONTROL --- snd_ctl_create() --- /sound/core/control.c
TIMER --- snd_timer_new() --- /sound/core/timer.c
JACK --- snd_jack_new() --- /sound/core/jack.c
...
2.5 第五步,注册声卡
- /sound/pci/hda/hda_intel.c
err = snd_card_register(chip->card);
if (err < 0)
goto out_free;
...
out_free:
snd_card_free(card);
return err;
3.声卡建立实例分析
- /sound/arm/pxa2xx-ac97.c
驱动程序通常由probe回调函数开始:
static int pxa2xx_ac97_probe(struct platform_device *dev)
{
struct snd_card *card;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
if (dev->id >= 0) {
dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
ret = -ENXIO;
goto err_dev;
}
// step 1: 创建声卡
ret = snd_card_new(&dev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
if (ret < 0)
goto err;
//step 3:设置Driver的ID
strlcpy(card->driver, dev->dev.driver->name, sizeof(card->driver));
// snd_pcm_new():创建pcm设备
ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm);
if (ret)
goto err;
//setp 2
ret = pxa2xx_ac97_hw_probe(dev);
if (ret)
goto err;
//step 4:创建一个 AC97 总线组件
ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
if (ret)
goto err_remove;
memset(&ac97_template, 0, sizeof(ac97_template));
ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);
if (ret)
goto err_remove;
//step 3:设置Driver的名字
snprintf(card->shortname, sizeof(card->shortname),
"%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97));
snprintf(card->longname, sizeof(card->longname),
"%s (%s)", dev->dev.driver->name, card->mixername);
if (pdata && pdata->codec_pdata[0])
snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
//step 5:注册声卡
ret = snd_card_register(card);
if (ret == 0) {
platform_set_drvdata(dev, card);
return 0;
}
err_remove:
pxa2xx_ac97_hw_remove(dev);
err:
if (card)
snd_card_free(card);
err_dev:
return ret;
}
经过以上的创建步骤之后,声卡的逻辑结构如下图所示:
- 声卡的软件逻辑结构
4.主要方法解析
4.1 snd_card_new
- /sound/core/init.c
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;
//根据extra_size大小分配内存,该内存区可做为芯片专有数据使用
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
if (!card)
return -ENOMEM;
//把第一步分配的内存指针放入private_data字段中:
if (extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
//拷贝声卡ID字符串
if (xid)
strlcpy(card->id, xid, sizeof(card->id));
err = 0;
mutex_lock(&snd_card_mutex);
//若是传入的声卡编号为-1,自动分配一个索引编号
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);
//初始化snd_card中必要的字段
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);
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
init_waitqueue_head(&card->power_sleep);
#endif
init_waitqueue_head(&card->remove_sleep);
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_groups;
card->dev_groups[0] = &card_dev_attr_group;
err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
if (err < 0)
goto __error;
snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
dev_driver_string(card->dev), dev_name(&card->card_dev));
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
//创建逻辑设备:control
err = snd_ctl_create(card);
if (err < 0) {
dev_err(parent, "unable to register control minors\n");
goto __error;
}
//创建proc文件中的info节点,一般就是/proc/asound/card0/
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;
}
4.2 snd_card_register
- /sound/core/init.c
int snd_card_register(struct snd_card *card)
{
int err;
if (snd_BUG_ON(!card))
return -EINVAL;
//首先建立sysfs下的设备
if (!card->registered) {
err = device_add(&card->card_dev);
if (err < 0)
return err;
card->registered = true;
}
//经过snd_device_register_all()注册全部挂在该声卡下的逻辑设备,snd_device_register_all()其实是经过snd_card的device链表,遍历全部snd_device,而且调用snd_device的ops->dev_register()来实现各自设备的注册的
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 snd_info_card_register(card); /* register pending info */
}
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;
}