linux-alsa详解1 基本知识

1 alsa的简单介绍

ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构。

在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。linux内核中alsa的软件结构如下:

用户空间的alsa-lib对应用程序提供统一的API接口,这样可以隐藏了驱动层的实现细节,简化了应用程序的实现难度,内核空间中,alsa-soc其实是对alsa-driver的进一步封装,他针对嵌入式设备提供了一些列增强的功能。1

1.1 alsa的设备文件结构

1 $ cd /dev/snd
2 $ ls -l
3 crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0
4 crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0
5 crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c
6 crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p
7 crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p
8 crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq
9 crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer

可以看到以下设备文件:

1 controlC0 -->              用于声卡的控制,例如通道选择,混音,麦克控制,音量加减,开关等
2 midiC0D0  -->              用于播放midi音频
3 pcmC0D0c -->               用于录音的pcm设备
4 pcmC0D0p -->               用于播放的pcm设备
5 seq  -->                   音序器
6 timer -->                       定时器

其中,C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则,从上面的列表可以看出,我的声卡下挂了6个设备,根据声卡的实际能力,驱动实际上可以挂上更多种类的设备,在include/sound/core.h中,定义了以下设备类型,通常更关心的是pcm和control这两种设备.。

 1 #define    SNDRV_DEV_TOPLEVEL    ((__force snd_device_type_t) 0)
 2 #define    SNDRV_DEV_CONTROL    ((__force snd_device_type_t) 1)
 3 #define    SNDRV_DEV_LOWLEVEL_PRE    ((__force snd_device_type_t) 2)
 4 #define    SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)
 5 #define    SNDRV_DEV_PCM        ((__force snd_device_type_t) 0x1001)
 6 #define    SNDRV_DEV_RAWMIDI    ((__force snd_device_type_t) 0x1002)
 7 #define    SNDRV_DEV_TIMER        ((__force snd_device_type_t) 0x1003)
 8 #define    SNDRV_DEV_SEQUENCER    ((__force snd_device_type_t) 0x1004)
 9 #define    SNDRV_DEV_HWDEP        ((__force snd_device_type_t) 0x1005)
10 #define    SNDRV_DEV_INFO        ((__force snd_device_type_t) 0x1006)
11 #define    SNDRV_DEV_BUS        ((__force snd_device_type_t) 0x1007)
12 #define    SNDRV_DEV_CODEC        ((__force snd_device_type_t) 0x1008)
13 #define    SNDRV_DEV_JACK          ((__force snd_device_type_t) 0x1009)
14 #define    SNDRV_DEV_COMPRESS    ((__force snd_device_type_t) 0x100A)
15 #define    SNDRV_DEV_LOWLEVEL    ((__force snd_device_type_t) 0x2000)

1.2 linux/sound 下alsa目录

各主要子目录的作用:

 1 core       该目录包含了ALSA驱动的中间层,它是整个ALSA驱动的核心部分
 2 core/oss   包含模拟旧的OSS架构的PCM和Mixer模块
 3 core/seq   有关音序器相关的代码
 4 include    ALSA驱动的公共头文件目录,该目录的头文件需要导出给用户空间的应用程序使用,通常,驱动模块私有的头文件不应放置在这里
 5 drivers    放置一些与CPU、BUS架构无关的公用代码
 6 i2c        ALSA自己的I2C控制代码
 7 pci        pci声卡的顶层目录,子目录包含各种pci声卡的代码
 8 isa        isa声卡的顶层目录,子目录包含各种isa声卡的代码
 9 soc        针对system-on-chip体系的中间层代码
10 soc/codecs 针对soc体系的各种codec的代码,与平台无关

2 声卡结构体

2.1 结构体snd_card 

snd_card可以说是整个ALSA音频驱动最顶层的一个结构,整个声卡的软件逻辑结构开始于该结构,几乎所有与声音相关的逻辑设备都是在snd_card的管理之下,声卡驱动的第一个动作通常就是创建一个snd_card结构体。

定义位于:include\sound\core.h

 1 /* main structure for soundcard */
 2 
 3 struct snd_card {
 4     int number;            /* number of soundcard (index to //soundcard的序号,通常为0
 5                                 snd_cards) */
 6 
 7     char id[16];            /* id string of this card */ //card的标识符,通常是字符串形式。
 8     char driver[16];        /* driver name */
 9     char shortname[32];        /* short name of this soundcard */
10     char longname[80];        /* name of this soundcard */ //会在具体驱动中设置,主要反映在/proc/asound/cards中
11     char mixername[80];        /* mixer name */
12     char components[128];        /* card components delimited with
13                                 space */
14     struct module *module;        /* top-level module */
15 
16     void *private_data;        /* private data for soundcard */声卡的私有数据,可以在创建声卡时通过参数指定数据的大小
17     void (*private_free) (struct snd_card *card); /* callback for freeing of
18                                 private data */
19     struct list_head devices;    /* devices */记录该声卡下所有逻辑设备的链表
20 
21     unsigned int last_numid;    /* last used numeric ID */
22     struct rw_semaphore controls_rwsem;    /* controls list lock */
23     rwlock_t ctl_files_rwlock;    /* ctl_files list lock */
24     int controls_count;        /* count of all controls */
25     int user_ctl_count;        /* count of all user controls */
26     struct list_head controls;    /* all controls for this card */ //记录该声卡下所有控制单元的链表
27     struct list_head ctl_files;    /* active control files *//用于管理该card下的active的control设备
28     struct mutex user_ctl_lock;    /* protects user controls against
29                        concurrent access */
30 
31     struct snd_info_entry *proc_root;    /* root for soundcard specific files */
32     struct snd_info_entry *proc_id;    /* the card id */
33     struct proc_dir_entry *proc_root_link;    /* number link to real id */
34 
35     struct list_head files_list;    /* all files associated to this card */
36     struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown
37                                 state */
38     spinlock_t files_lock;        /* lock the files for this card */
39     int shutdown;            /* this card is going down */
40     int free_on_last_close;        /* free in context of file_release */
41     wait_queue_head_t shutdown_sleep;
42     atomic_t refcount;        /* refcount for disconnection */
43     struct device *dev;        /* device assigned to this card */ //和card相关的设备
44     struct device *card_dev;    /* cardX object for sysfs */ //card用于在sys中显示,用于代表该card
45 
46 #ifdef CONFIG_PM
47     unsigned int power_state;    /* power state */
48     struct mutex power_lock;    /* power lock */
49     wait_queue_head_t power_sleep;
50 #endif
51 
52 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
53     struct snd_mixer_oss *mixer_oss;
54     int mixer_oss_change_count;
55 #endif
56 }

snd_card的driver字段保存着芯片的ID字符串,user空间的alsa-lib会使用到该字符串,所以必须要保证该ID的唯一性.shortname字段更多地用于打印信息,longname字段则会出现在/proc/asound/cards中。

3 声卡的创建

3.1 声卡创建函数snd_card_new

定义位于:sound\core\init.c

创建并初始化声卡结构体

  1 /**
  2  *  snd_card_new - create and initialize a soundcard structure
  3  *  @parent: the parent device object
  4  *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]//一个整数值,该声卡的编号
  5  *  @xid: card identification (ASCII string)//字符串,声卡的标识符
  6  *  @module: top level module for locking
  7  *  @extra_size: allocate this extra size after the main soundcard structure//该参数决定在创建snd_card实例时,需要同时额外分配的私有数据的大小,该数据的指针最终会赋值给snd_card的private_data数据成员
  8  *  @card_ret: the pointer to store the created card instance//返回所创建的snd_card实例的指针
  9  *
 10  *  Creates and initializes a soundcard structure.
 11  *
 12  *  The function allocates snd_card instance via kzalloc with the given
 13  *  space for the driver to use freely.  The allocated struct is stored
 14  *  in the given card_ret pointer.
 15  *
 16  *  Return: Zero if successful or a negative error code.
 17  */
 18 int snd_card_new(struct device *parent, int idx, const char *xid,
 19             struct module *module, int extra_size,
 20             struct snd_card **card_ret)
 21 {
 22     struct snd_card *card;//创建一个声卡实例
 23     int err;
 24 
 25     if (snd_BUG_ON(!card_ret))
 26         return -EINVAL;
 27     *card_ret = NULL;
 28 
 29     if (extra_size < 0)
 30         extra_size = 0;
 31     card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);////根据extra_size参数的大小分配内存,该内存区可以作为芯片的专有数据使用
 32     if (!card)
 33         return -ENOMEM;
 34     if (extra_size > 0)
 35         card->private_data = (char *)card + sizeof(struct snd_card);
 36     if (xid)
 37         strlcpy(card->id, xid, sizeof(card->id));////拷贝声卡的ID字符串
 38     err = 0;
 39     mutex_lock(&snd_card_mutex);
 40     if (idx < 0) /* first check the matching module-name slot */// //如果传入的声卡编号为-1,自动分配一个索引编号
 41         idx = get_slot_from_bitmask(idx, module_slot_match, module);
 42     if (idx < 0) /* if not matched, assign an empty slot */
 43         idx = get_slot_from_bitmask(idx, check_empty_slot, module);
 44     if (idx < 0)
 45         err = -ENODEV;
 46     else if (idx < snd_ecards_limit) {
 47         if (test_bit(idx, snd_cards_lock))
 48             err = -EBUSY;    /* invalid */
 49     } else if (idx >= SNDRV_CARDS)
 50         err = -ENODEV;
 51     if (err < 0) {
 52         mutex_unlock(&snd_card_mutex);
 53         dev_err(parent, "cannot find the slot for index %d (range 0-%i), error: %d\n",
 54              idx, snd_ecards_limit - 1, err);
 55         kfree(card);
 56         return err;
 57     }
 58     set_bit(idx, snd_cards_lock);        /* lock it */
 59     if (idx >= snd_ecards_limit)
 60         snd_ecards_limit = idx + 1; /* increase the limit */
 61     mutex_unlock(&snd_card_mutex);
 62     /*初始化snd_card结构中必要的字段*/
 63     card->dev = parent;
 64     card->number = idx;
 65     card->module = module;
 66     INIT_LIST_HEAD(&card->devices);
 67     init_rwsem(&card->controls_rwsem);
 68     rwlock_init(&card->ctl_files_rwlock);
 69     mutex_init(&card->user_ctl_lock);
 70     INIT_LIST_HEAD(&card->controls);
 71     INIT_LIST_HEAD(&card->ctl_files);
 72     spin_lock_init(&card->files_lock);
 73     INIT_LIST_HEAD(&card->files_list);
 74 #ifdef CONFIG_PM
 75     mutex_init(&card->power_lock);
 76     init_waitqueue_head(&card->power_sleep);
 77 #endif
 78 
 79     device_initialize(&card->card_dev);
 80     card->card_dev.parent = parent;
 81     card->card_dev.class = sound_class;
 82     card->card_dev.release = release_card_device;
 83     card->card_dev.groups = card->dev_groups;
 84     card->dev_groups[0] = &card_dev_attr_group;
 85     err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
 86     if (err < 0)
 87         goto __error;
 88 
 89     snprintf(card->irq_descr, sizeof(card->irq_descr), "%s:%s",
 90          dev_driver_string(card->dev), dev_name(&card->card_dev));
 91 
 92     /* the control interface cannot be accessed from the user space until */
 93     /* snd_cards_bitmask and snd_cards are set with snd_card_register */
 94     err = snd_ctl_create(card);//建立逻辑设备:Control
 95     if (err < 0) {
 96         dev_err(parent, "unable to register control minors\n");
 97         goto __error;
 98     }
 99     err = snd_info_card_create(card);//建立proc文件中的info节点:通常就是/proc/asound/card0
100     if (err < 0) {
101         dev_err(parent, "unable to create card info\n");
102         goto __error_ctl;
103     }
104     *card_ret = card;
105     return 0;
106 
107       __error_ctl:
108     snd_device_free_all(card);
109       __error:
110     put_device(&card->card_dev);
111       return err;
112 }

3.2 声卡的注册snd_card_register()

定义位于:在/sound/core/init.c中

 1 /**
 2  *  snd_card_register - register the soundcard
 3  *  @card: soundcard structure
 4  *
 5  *  This function registers all the devices assigned to the soundcard.
 6  *  Until calling this, the ALSA control interface is blocked from the
 7  *  external accesses.  Thus, you should call this function at the end
 8  *  of the initialization of the card.
 9  *
10  *  Return: Zero otherwise a negative error code if the registration failed.
11  */
12 int snd_card_register(struct snd_card *card)
13 {
14     int err;
15 
16     if (snd_BUG_ON(!card))
17         return -EINVAL;
18 
19     if (!card->card_dev) {
20         card->card_dev = device_create(sound_class, card->dev,//创建sysfs下的设备
21                            MKDEV(0, 0), card,
22                            "card%i", card->number);
23         if (IS_ERR(card->card_dev))
24             card->card_dev = NULL;
25     }
26 /*注册所有挂在该声卡下的逻辑设备,snd_device_register_all()实际上是通过snd_card的devices链表,遍历所有的snd_device,并且调用snd_device的ops->dev_register()来实现各自设备的注册的*/
27     if ((err = snd_device_register_all(card)) < 0)
28         return err;
29     mutex_lock(&snd_card_mutex);
30     if (snd_cards[card->number]) {
31         /* already registered */
32         mutex_unlock(&snd_card_mutex);
33         return 0;
34     }
35     if (*card->id) {
36         /* make a unique id name from the given string */
37         char tmpid[sizeof(card->id)];
38         memcpy(tmpid, card->id, sizeof(card->id));
39         snd_card_set_id_no_lock(card, tmpid, tmpid);
40     } else {
41         /* create an id from either shortname or longname */
42         const char *src;
43         src = *card->shortname ? card->shortname : card->longname;
44         snd_card_set_id_no_lock(card, src,
45                     retrieve_id_from_card_name(src));
46     }
47     snd_cards[card->number] = card;
48     mutex_unlock(&snd_card_mutex);
49     init_info_for_card(card);
50 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
51     if (snd_mixer_oss_notify_callback)
52         snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
53 #endif /*建立一些相应的proc和sysfs下的文件或属性节点*/
54     if (card->card_dev) {
55         err = device_create_file(card->card_dev, &card_id_attrs);
56         if (err < 0)
57             return err;
58         err = device_create_file(card->card_dev, &card_number_attrs);
59         if (err < 0)
60             return err;
61     }
62 
63     return 0;
64 }

3.3 sound_class创建

 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");//创建sound_class
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 }

sound_devnode的定义:

1 static char *sound_devnode(struct device *dev, umode_t *mode)
2 {
3     if (MAJOR(dev->devt) == SOUND_MAJOR)
4         return NULL;
5     return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
6 }
subsys_initcall(init_soundcore);

#define subsys_initcall(fn)        module_init(fn)

函数init_soundcore是sound_core.c的入口函数。

3.4 创建声卡的各种部件

主要包括:pcm 、mixer、MIDI等。之前snd_card结构体的devices字段,每一种部件的创建最终会调用snd_device_new()来生成一个snd_device实例,并把该实例链接到snd_card的devices链表中.。

通常,alsa-driver的已经提供了一些常用的部件的创建函数,而不必直接调用snd_device_new(),如下:

1 PCM  ----   snd_pcm_new()
2 RAWMIDI --  snd_rawmidi_new()
3 CONTROL --  snd_ctl_create()
4 TIMER   --  snd_timer_new()
5 INFO    --  snd_card_proc_new()
6 JACK    --  snd_jack_new()

创建之后声卡的逻辑结构如下:

4  声卡初始化

定义位于:sound\core\sound.c

sonud.c的入口函数:

申请一个字符设备的主设备号。control、pcm等逻辑设备都是这个主设备号下的次设备。

 1 /*
 2  *  INIT PART
 3  */
 4 
 5 static int __init alsa_sound_init(void)
 6 {
 7     snd_major = major;
 8     snd_ecards_limit = cards_limit;
 9     if (register_chrdev(major, "alsa", &snd_fops)) {//获取字符设备主设备号,即声卡的主设备号,其他声卡设备都是其下的次设备
10         pr_err("ALSA core: unable to register native major device number %d\n", major);
11         return -EIO;
12     }
13     if (snd_info_init() < 0) {
14         unregister_chrdev(major, "alsa");
15         return -ENOMEM;
16     }
17 #ifndef MODULE
18     pr_info("Advanced Linux Sound Architecture Driver Initialized.\n");
19 #endif
20     return 0;
21 }

次设备共用一个open接口,然后根据下面的全局数组snd_minors找到相应的次设备文件操作结构体,分别调用相应次设备的open函数。snd_fops的定义:

1 static const struct file_operations snd_fops =
2 {
3     .owner =    THIS_MODULE,
4     .open =        snd_open,
5     .llseek =    noop_llseek,
6 };

snd_open函数定义:

 1 static int snd_open(struct inode *inode, struct file *file)
 2 {
 3     unsigned int minor = iminor(inode);//声卡下的次设备
 4     struct snd_minor *mptr = NULL;
 5     const struct file_operations *new_fops;
 6     int err = 0;
 7 
 8     if (minor >= ARRAY_SIZE(snd_minors))
 9         return -ENODEV;
10     mutex_lock(&sound_mutex);
11     mptr = snd_minors[minor];//获取到具体的声卡设备,即次设备比如control/pcm设备等
12     if (mptr == NULL) {
13         mptr = autoload_device(minor);
14         if (!mptr) {
15             mutex_unlock(&sound_mutex);
16             return -ENODEV;
17         }
18     }
19     new_fops = fops_get(mptr->f_ops);//获取次设备的f_ops文件操作结构体
20     mutex_unlock(&sound_mutex);
21     if (!new_fops)
22         return -ENODEV;
23     replace_fops(file, new_fops);//用次设备的文件操作结构体替换
24 
25     if (file->f_op->open)
26         err = file->f_op->open(inode, file);//执行次设备的文件open函数
27     return err;
28 }

snd_minors是定义在sound.c中的全局变量,表示主设备号下的次设备比如control、pcm设备等,inode作为snd_minors的下表,找到对应的次设备。

static struct snd_minor *snd_minors[SNDRV_OS_MINORS];

结构体定义,包含设备类型、声卡编号、设备

1 struct snd_minor {
2     int type;            /* SNDRV_DEVICE_TYPE_XXX */
3     int card;            /* card number */
4     int device;            /* device number */
5     const struct file_operations *f_ops;    /* file operations */
6     void *private_data;        /* private data for f_ops->open */
7     struct device *dev;        /* device for sysfs */
8     struct snd_card *card_ptr;    /* assigned card instance */
9 }

设备类型:

定义位于:include\sound\minors.h

 1 enum {
 2     SNDRV_DEVICE_TYPE_CONTROL,
 3     SNDRV_DEVICE_TYPE_SEQUENCER,
 4     SNDRV_DEVICE_TYPE_TIMER,
 5     SNDRV_DEVICE_TYPE_HWDEP,
 6     SNDRV_DEVICE_TYPE_RAWMIDI,
 7     SNDRV_DEVICE_TYPE_PCM_PLAYBACK,
 8     SNDRV_DEVICE_TYPE_PCM_CAPTURE,
 9     SNDRV_DEVICE_TYPE_COMPRESS,
10 };

参考博文:

https://www.cnblogs.com/jason-lu/archive/2013/06/07/3123571.html

posted @ 2020-06-13 17:46  Action_er  阅读(5217)  评论(0编辑  收藏  举报