一、ALSA Sound 初始化
1、alsa_sound_init() 入口函数
在我们创建声卡前,ALSA Sound 会先有相应的初始化,即 sound.c 的入口函数,位于 sound/core/sound.c ,主要是用于申请一个字符设备的主设备号(116) ,后面的 pcm 、control 等逻辑设备都是这个主设备号下的次设备。
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;
}
其中 struct snd_fops 定义如下,所有次设备 共用一个 open 接口,
static const struct file_operations snd_fops =
{
.owner = THIS_MODULE,
.open = snd_open,
.llseek = noop_llseek,
};
snd_open 函数定义如下:
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;
}
}
new_fops = fops_get(mptr->f_ops);
mutex_unlock(&sound_mutex);
if (!new_fops)
return -ENODEV;
replace_fops(file, new_fops);
if (file->f_op->open)
err = file->f_op->open(inode, file);
return err;
}
如上述注释所述,在 snd_open 函数中利用次设备号根据全局数组 snd_minors 找到相应的次设备 file_operations 并替换,最后调用相应次设备的 open 函数。(备注:很多设备框架都是使用这种做法) snd_minors 是定义在 sound.c 中的全局变量,表示主设备号下的次设备比如 control、pcm 设备等,定义如下:
struct snd_minor {
int type;
int card;
int device;
const struct file_operations *f_ops;
void *private_data;
struct device *dev;
struct snd_card *card_ptr;
};
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
其中包含的设备类型如下,位于 include/sound/minors.h
enum {
SNDRV_DEVICE_TYPE_CONTROL,
SNDRV_DEVICE_TYPE_SEQUENCER,
SNDRV_DEVICE_TYPE_TIMER,
SNDRV_DEVICE_TYPE_HWDEP,
SNDRV_DEVICE_TYPE_RAWMIDI,
SNDRV_DEVICE_TYPE_PCM_PLAYBACK,
SNDRV_DEVICE_TYPE_PCM_CAPTURE,
SNDRV_DEVICE_TYPE_COMPRESS,
};
2、init_soundcore() 入口函数
该入口函数是 sound_core.c 的入口函数,主要用于创建 sound_class,如下
static char *sound_devnode(struct device *dev, umode_t *mode)
{
if (MAJOR(dev->devt) == SOUND_MAJOR)
return NULL;
return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}
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;
}
subsys_initcall(init_soundcore);
二、声卡结构体与创建、注册
1、struct snd_card
结构体 snd_card 是整个 ALSA 音频驱动最顶层的一个结构体,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在 snd_card 的管理之下,声卡驱动的第一个动作就是创建一个 snd_card 结构体,其定位于 include/sound/core.h,如下:
struct snd_card {
int number;
char id[16];
char driver[16];
char shortname[32];
char longname[80];
char irq_descr[32];
char mixername[80];
char components[128];
struct module *module;
void *private_data;
void (*private_free) (struct snd_card *card);
struct list_head devices;
struct device ctl_dev;
unsigned int last_numid;
struct rw_semaphore controls_rwsem;
rwlock_t ctl_files_rwlock;
int controls_count;
int user_ctl_count;
struct list_head controls;
struct list_head ctl_files;
struct snd_info_entry *proc_root;
struct snd_info_entry *proc_id;
struct proc_dir_entry *proc_root_link;
struct list_head files_list;
struct snd_shutdown_f_ops *s_f_ops;
spinlock_t files_lock;
int shutdown;
struct completion *release_completion;
struct device *dev;
struct device card_dev;
const struct attribute_group *dev_groups[4];
bool registered;
wait_queue_head_t remove_sleep;
#ifdef CONFIG_PM
unsigned int 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
};
其中 snd_card 的 driver 字段保存着芯片的 ID 字符串,用户空间的 alsa-lib 会使用到该字符串,所以必须保证该 ID 的唯一性,shortname 字段更多地用于打印信息,longname 字段则会出现在 /proc/asound/cards 中。
2、声卡创建流程
1、创建 snd_card 实例
int snd_card_new(struct device *parent, int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret);
通过如上描述的 snd_card_new() 函数可以创建一个声卡实例,参数 & 功能描述如上,api 主要 flow 如下,
snd_card_new
--> 根据 extra_size 参数大小用 kzalloc 分配一个 snd_card,如果 extra_size > 0 则将 snd_card->private_data 指向 extra_size addr;
--> 初始化 snd_card 结构体的必要字段,id、idx 及 card->card_dev;
--> snd_ctl_create() [建立逻辑设备:Control]
--> snd_info_card_create(card) [建立 proc 文件中的 info 节点:通常就是 /proc/asound/card0]
声卡的专用数据主要用于存放一些资源信息,例如中断资源、io资源、dma资源等,根据 extra_size 参数分有两种方式创建 snd_card,
- 通过 snd_card_create 函数创建 private_data
struct my_chip_priv {
...
};
snd_card_new(dev, idx, xid, THIS_MODULE, sizeof(my_chip_priv), &card);
struct my_chip_priv *my_chip = card->private_data;
- 自己创建 priv_buf,并将 snd_card->private_data 指向它
struct my_chip_priv {
struct snd_card *card;
...
};
struct snd_card *card;
struct my_chip_priv *my_chip;
my_chip = kzalloc(sizeof(*chip), GFP_KERNEL);
snd_card_new(dev, idx, xid, THIS_MODULE, 0, &card);
my_chip->card = card;
2、设置 snd_card 的 Driver ID 和 Name
strcpy(card->driver, "My Chip");
strcpy(card->shortname, "My Own Chip 123");
sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->ioport, chip->irq);
snd_card 的 driver 字段保存着芯片的 ID 字符串,user空间的 alsa-lib 会使用到该字符串,所以必须要保证该 ID 的唯一性。shortname 字段更多地用于打印信息,longname 字段则会出现在 /proc/asound/cards 中。
3、创建声卡的功能部件(逻辑设备) 还记得 snd_card 结构体中的 devices 字段吗?在注册声卡的时候会对该 devices 链表中的所有逻辑设备都进行注册,故在注册声卡前需要调用 snd_device_new() 来生成一个 snd_device 实例,并将该实例链接到 snd_card 的 devices 链表中。
通常,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()
API 详细见下方逻辑设备描述~
4、注册声卡
int snd_card_register(struct snd_card *card);
注册声卡时需要 Call snd_card_register() 函数,如注释所述,该 api 内部会 register 所有 devices,关于该 API 的主要 Flow 如下:
snd_card_register
--> device_add(&card->card_dev) 创建 /sys/devices 下的设备;
--> snd_device_register_all(card) 通过 snd_card 的 devices 链表,遍历所有的 snd_device,
--> 并且调用 snd_device 的 ops->dev_register() 来实现格子设备的注册;
经过上述的创建声卡步骤后,声卡的逻辑结构则如下图所示:
3、声卡创建过程使用举例
/sound/arm/pxa2xx-ac97.c
三、声卡之 pcm 设备
四、声卡之 control 设备
|