1. 程式人生 > >linux音訊子系統 - 驅動框架

linux音訊子系統 - 驅動框架

音訊相關術語

  • PCM(Pulse Code Modulation)
    脈衝編碼調製,對連續變化的模擬訊號進行抽樣、量化和編碼,在驅動中一般音訊流裝置都稱為pcm裝置

  • I2S
    I2S是對PCM格式的資料進行規範化,可以說是PCM的子集,I2S只有左右兩通道資料

  • TDM(Time Division Multiplexing)
    時分複用,可以用單根線傳送多通道資料

  • midi(Musical Instrument Digital Interface)
    MIDI是編曲界最廣泛的音樂標準格式,可稱為“計算機能理解的樂譜”。它用音符的數字控制訊號來記錄音樂。一首完整的MIDI音樂只有幾十KB大,而能包含數十條音樂軌道。幾乎所有的現代音樂都是用MIDI加上音色庫來製作合成的。MIDI 傳輸的不是聲音訊號, 而是音符、控制引數等指令, 它指示MIDI 裝置要做什麼,怎麼做, 如演奏哪個音符、多大音量等。

  • timer(音序器)
    音序器,又稱聲音序列發生器,可將所有MIDI通道中的演奏資訊同時自動播放演奏

驅動架構圖

OSS(Open Sound System),linux以前的驅動架構,現在已廢棄不用

linux目前的音訊框架為ALSA(Advanced Linux Sound Architecture),框架如下圖所示,分三層,其中ASoc適用於目前的嵌入式裝置,在ALSA-core基礎上又分出一個小架構

這裡寫圖片描述

其中alsa-core層的主要結構如下,一個音效卡用snd_card來表示,在一個音效卡上可能集成了好幾個子裝置,每個子裝置負責不用的功能,分為pcm/control/midi/timer等

這裡寫圖片描述

音效卡結構體

一個音效卡下是由多個裝置的,總音效卡用結構體snd_card來表示,各個子裝置用結構體snd_device來抽象,當然對於具體的子裝置,還會有相應的結構體來表示,這個會在子裝置的文章中詳細介紹

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 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 */---------snd_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 */---控制裝置snd_device加入此連結串列 struct list_head ctl_files; /* active control files */ struct mutex user_ctl_lock; /* protects user controls against concurrent access */ 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 */ int free_on_last_close; /* free in context of file_release */ wait_queue_head_t shutdown_sleep; atomic_t refcount; /* refcount for disconnection */ struct device *dev; /* device assigned to this card */ struct device *card_dev; /* cardX object for sysfs */ #ifdef CONFIG_PM unsigned int power_state; /* power state */ struct mutex power_lock; /* power lock */ wait_queue_head_t power_sleep; #endif #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) struct snd_mixer_oss *mixer_oss; int mixer_oss_change_count; #endif };

snd_device

(include/sound/core.h)

struct snd_device {
    struct list_head list;      /* list of registered devices */---加入音效卡的連結串列節點
    struct snd_card *card;      /* card which holds this device */-對應的音效卡
    snd_device_state_t state;   /* state of the device */
    snd_device_type_t type;     /* device type */
    void *device_data;      /* device structure */------------對於具體子裝置的私有資料
    struct snd_device_ops *ops; /* operations */------------------對子設備註冊的操作
};

音效卡註冊

snd_card_register

(sound/core/init.c)

int snd_card_register(struct snd_card *card)
{
    int err;

    if (snd_BUG_ON(!card))
        return -EINVAL;

    if (!card->card_dev) {
        card->card_dev = device_create(sound_class, card->dev,
                           MKDEV(0, 0), card,
                           "card%i", card->number);---註冊音效卡
        if (IS_ERR(card->card_dev))
            card->card_dev = NULL;
    }

    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 defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
    if (snd_mixer_oss_notify_callback)
        snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
    if (card->card_dev) {
        err = device_create_file(card->card_dev, &card_id_attrs);
        if (err < 0)
            return err;
        err = device_create_file(card->card_dev, &card_number_attrs);
        if (err < 0)
            return err;
    }

    return 0;
}

snd_device_register_all

int snd_device_register_all(struct snd_card *card)
{
    struct snd_device *dev;
    int err;

    if (snd_BUG_ON(!card))
        return -ENXIO;
    list_for_each_entry(dev, &card->devices, list) {----遍歷音效卡的裝置連結串列
        if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
            if ((err = dev->ops->dev_register(dev)) < 0)
                return err;-----------------呼叫每個設備註冊函式,進行註冊
            dev->state = SNDRV_DEV_REGISTERED;
        }
    }
    return 0;
}

change log

date content linux version
2017/11/20 origin linux 3.10