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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM