ALSA作为Liunx现在主流的音频体系结构,提供了内核的驱动框架,也提供了应用层的alsa-lib库,应用层主要调用alsa-lib的API函数就可以实现对声卡的控制。ALSA也提供了alsa-utils的应用程序,方便我们进行音频播放控制等。本系列是根据openwrt21.02上搭配wm8960进行实验的。
controlC0 用于card0声卡的控制
pcmC0D0c 用于card0 device0录音的pcm设备
pcmC0D0p 用于card0 device0播放的pcm设备
timer 定时器
一个声卡可以有多个设备 一个设备可以有多个播放录音通道,每个设备节点都对应着一个fops,这些设备得主设备号是一样得,则对应同一个fops。
/sound/core/sound.c
static int snd_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct snd_minor *mptr = NULL;
const struct file_operations *new_fops;
int err = 0;
if (minor >= ARRAY_SIZE(snd_minors))
return -ENODEV;
mutex_lock(&sound_mutex);
mptr = snd_minors[minor];
if (mptr == NULL) {
mptr = autoload_device(minor);
if (!mptr) {
mutex_unlock(&sound_mutex);
return -ENODEV;
}
}
/*根据次设备号,获得snd下面fops结构体*/
new_fops = fops_get(mptr->f_ops);
mutex_unlock(&sound_mutex);
if (!new_fops)
return -ENODEV;
/*把老的fops结构体换成重新获得的fops结构体*/
replace_fops(file, new_fops);
/*打开新的fops*/
if (file->f_op->open)
err = file->f_op->open(inode, file);
return err;
}
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};
static int __init alsa_sound_init(void)
{
snd_major = major;
snd_ecards_limit = cards_limit;
if (register_chrdev(major, "alsa", &snd_fops)) {
pr_err("ALSA core: unable to register native major device number %d\n", major);
return -EIO;
}
if (snd_info_init() < 0) {
unregister_chrdev(major, "alsa");
return -ENOMEM;
}
#ifndef MODULE
pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
#endif
return 0;
}
也就是当应用层打开snd下面的各个设备节点时,驱动首先跑一个snd_open 再根据里面次设备号来确定具体打开哪个节点
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;
//根据extra_size参数的大小分配内存,该内存区可以作为芯片的专有数据使用
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);
//如果传入的声卡编号为-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);
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 */
err = snd_ctl_create(card);//建立逻辑设备:Control
if (err < 0) {
dev_err(parent, "unable to register control minors\n");
goto __error;
}
err = snd_info_card_create(card);//建立proc文件中的info节点:通常就是/proc/asound/card0
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;
}
int snd_card_register(struct snd_card *card)
{
int err;
if (snd_BUG_ON(!card))
return -EINVAL;
if (!card->registered) {
//创建device
err = device_add(&card->card_dev);
if (err < 0)
return err;
card->registered = true;
}
/*注册所有挂在该声卡下的逻辑设备,
snd_device_register_all()实际上是通过
snd_card的devices链表,遍历所有的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 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;
}
//sound/core/pcm_native.c
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
//sound/core/pcm.c
static int snd_pcm_dev_register(struct snd_device *device)
{
...
err = snd_pcm_add(pcm);
if (err)
goto unlock;
for (cidx = 0; cidx < 2; cidx++) {
...
/* register pcm */
err = snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,
&pcm->streams[cidx].dev);
...
}
...
}
...
}
snd_pcm_new
->_snd_pcm_new
->snd_pcm_dev_register
->snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,playback_count);
->snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
->snd_device_new(card, SNDRV_DEV_PCM, pcm,internal ? &internal_ops : &ops);
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
static int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, &card->ctl_dev);
}
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
int err;
...
dev_set_name(&card->ctl_dev, "controlC%d", card->number);
err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
if (err < 0)
put_device(&card->ctl_dev);
return err;
}
int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
struct snd_timer **rtimer)
{
struct snd_timer *timer;
int err;
static struct snd_device_ops ops = {
.dev_free = snd_timer_dev_free,
.dev_register = snd_timer_dev_register,
.dev_disconnect = snd_timer_dev_disconnect,
};
...
if (card != NULL) {
timer->module = card->module;
err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
if (err < 0) {
snd_timer_free(timer);
return err;
}
}
if (rtimer)
*rtimer = timer;
return 0;
}
当snd_device_new执行时候dev_register 就会执行 进而执行snd_register_device函数
int snd_register_device(int type, struct snd_card *card, int dev,
const struct file_operations *f_ops,
void *private_data, struct device *device)
{
...
device->devt = MKDEV(major, minor);
err = device_add(device);
if (err < 0)
goto error;
snd_minors[minor] = preg;//赋值给全局的次设备信息结构体,当snd_open时候 则根据次设备号来选择是哪一个设备的open
...
}
1.sound.c中
register_chrdev(major, "alsa", &snd_fops)
2.init.c中
snd_card_new
->snd_ctl_create
->snd_device_new
->snd_ctl_dev_register
->snd_register_device
->snd_info_card_create
3.pcm.c中
snd_pcm_new
->_snd_pcm_new
->snd_pcm_new_stream(SNDRV_PCM_STREAM_PLAYBACK)
->snd_pcm_new_stream(SNDRV_PCM_STREAM_CAPTURE)
->snd_device_new
->snd_pcm_dev_register
->snd_register_device * 2
alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new()
pcm -> snd_pcm_new()
rawmidi -> snd_rawmidi_new()
control -> snd_ctl_create()
timer -> snd_timer_new()
info -> snd_card_proc_new()
jack -> snd_jack_new()