alsa資料結構流程

來源:互聯網
上載者:User

以音效卡驅動的資料結構為切入點分析:

/* SoC card */struct snd_soc_card {    const char *name;    struct device *dev;    struct snd_card *snd_card; //在snd_soc_instantiate_card中利用snd_card_create建立音效卡    struct module *owner;    struct list_head list;    struct mutex mutex;    bool instantiated;    int (*probe)(struct snd_soc_card *card);    int (*late_probe)(struct snd_soc_card *card);    int (*remove)(struct snd_soc_card *card);    /* the pre and post PM functions are used to do any PM work before and     * after the codec and DAI's do any PM work. */    int (*suspend_pre)(struct snd_soc_card *card);    int (*suspend_post)(struct snd_soc_card *card);    int (*resume_pre)(struct snd_soc_card *card);    int (*resume_post)(struct snd_soc_card *card);    /* callbacks */    int (*set_bias_level)(struct snd_soc_card *,                  enum snd_soc_bias_level level);    int (*set_bias_level_post)(struct snd_soc_card *,                   enum snd_soc_bias_level level);    long pmdown_time;    /* CPU <--> Codec DAI links  */    struct snd_soc_dai_link *dai_link;    int num_links;    struct snd_soc_pcm_runtime *rtd;    int num_rtd;    /* optional codec specific configuration */    struct snd_soc_codec_conf *codec_conf;    int num_configs;    /*optional auxiliary devices such as amplifiers or codecs with DAI link unused*/    struct snd_soc_aux_dev *aux_dev;    int num_aux_devs;    struct snd_soc_pcm_runtime *rtd_aux;    int num_aux_rtd;    /* Card-specific routes and widgets.*/    struct snd_soc_dapm_widget *dapm_widgets;    int num_dapm_widgets;    struct snd_soc_dapm_route *dapm_routes;    int num_dapm_routes;    struct work_struct deferred_resume_work; //屬於這個card的已經探測到的裝置列表 /* lists of probed devices belonging to this card */    struct list_head codec_dev_list;    struct list_head platform_dev_list;    struct list_head dai_dev_list;    struct list_head widgets;    struct list_head paths;    struct list_head dapm_list;    /* Generic DAPM context for the card */    struct snd_soc_dapm_context dapm;#ifdef CONFIG_DEBUG_FS    struct dentry *debugfs_card_root;    struct dentry *debugfs_pop_time;#endif    u32 pop_time;    void *drvdata;};/* SoC machine DAI configuration, glues(膠水) a codec and cpu DAI together */struct snd_soc_pcm_runtime  {    struct device dev;    struct snd_soc_card *card;    struct snd_soc_dai_link *dai_link;    unsigned int complete:1;    unsigned int dev_registered:1;    /* Symmetry data - only valid if symmetry is being enforced */    unsigned int rate;    long pmdown_time;    /* runtime devices */    struct snd_pcm *pcm;    struct snd_soc_codec *codec;    struct snd_soc_platform *platform;    struct snd_soc_dai *codec_dai;    struct snd_soc_dai *cpu_dai;    struct delayed_work delayed_work;};1) struct snd_soc_codec - 由與平台無關的codec驅動實現。2) struct snd_soc_platform - 由與imx平台相關的DAI驅動實現,主要實現了音頻資料的DMA傳輸功能。3) struct snd_soc_dai_link - 將平台相關的DAI與平台無關的codec聯絡起來。 
與聲音相關的邏輯裝置都是在snd_card的管理之下,音效卡驅動的第一個動作通常就是建立一個snd_card結構體。   1. struct snd_card {     2.     int number;              /* number of soundcard (index to snd_cards) */     3.     char id[16];             /* id string of this card */     4.     char driver[16];         /* driver name */     5.     char shortname[32];      /* short name of this soundcard */     6.     char longname[80];       /* name of this soundcard */     7.     char mixername[80];      /* mixer name */     8.     char components[128];    /* card components delimited with space */     9.     struct module *module;   /* top-level module */    10.     11.     void *private_data;     /* private data for soundcard 音效卡的私人資料,可以在建立音效卡時通過參數指定資料的大小 */    12.     void (*private_free) (struct snd_card *card); /* callback for freeing of private data */    13.     struct list_head devices;   /* devices 記錄該音效卡下所有邏輯裝置的鏈表 */    14.     15.     unsigned int last_numid;    /* last used numeric ID */    16.     struct rw_semaphore controls_rwsem; /* controls list lock */    17.     rwlock_t ctl_files_rwlock;  /* ctl_files list lock */    18.     int controls_count;     /* count of all controls */    19.     int user_ctl_count;     /* count of all user controls */    20.     struct list_head controls;  /* all controls for this card 記錄該音效卡下所有的控制單元的鏈表 */    21.     struct list_head ctl_files; /* active control files */    22.     23.     struct snd_info_entry *proc_root;   /* root for soundcard specific files */    24.     struct snd_info_entry *proc_id; /* the card id */    25.     struct proc_dir_entry *proc_root_link;  /* number link to real id */    26.     27.     struct list_head files_list;    /* all files associated to this card */    28.     struct snd_shutdown_f_ops *s_f_ops; /* file operations in the shutdown state */    29.     spinlock_t files_lock;      /* lock the files for this card */    30.     int shutdown;           /* this card is going down */    31.     int free_on_last_close;     /* free in context of file_release */    32.     wait_queue_head_t shutdown_sleep;    33.     struct device *dev;     /* device assigned to this card */    34. #ifndef CONFIG_SYSFS_DEPRECATED    35.     struct device *card_dev;    /* cardX object for sysfs */    36. #endif    37.    38. #ifdef CONFIG_PM    39.     unsigned int power_state;   /* power state */    40.     struct mutex power_lock;    /* power lock */    41.     wait_queue_head_t power_sleep;    42. #endif    43.    44. #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)    45.     struct snd_mixer_oss *mixer_oss;    46.     int mixer_oss_change_count;    47. #endif    48. };  

建立音效卡的功能組件(邏輯裝置),例如PCM,Mixer,MIDI等。

每一種組件的建立最終會調用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()

  • controlC0 -->                 用於音效卡的控制,例如通道選擇,混音,麥克風的控制等
  • midiC0D0  -->                用於播放midi音頻
  • pcmC0D0c --〉               用於錄音的pcm裝置
  • pcmC0D0p --〉               用於播放的pcm裝置
  • seq  --〉                        音序器
  • timer --〉                       定時器

core               該目錄包含了ALSA驅動的中介層,它是整個ALSA驅動的核心部分i2c                ALSA自己的I2C控制碼soc                針對system-on-chip體系的中介層代碼soc/codecs         針對soc體系的各種codec的代碼,與平台無關

每個音效卡最多可以包含4個pcm的執行個體,每個pcm執行個體對應一個pcm裝置檔案。

ALSA已經為我們實現了功能強勁的PCM中介層,自己的驅動中只要實現一些底層的需要訪問硬體的函數即可。
大多數情況下,在嵌入式裝置中,一個pcm執行個體已經足夠了。一個pcm執行個體由一個playback stream和一個capture stream組成,這兩個stream又分別有一個或多個substreams組成。

                                                 pcm中介層的幾個重要的結構體的關係圖

struct snd_pcm {
    struct snd_card *card;
    struct list_head list;
    int device; /* device number */
    unsigned int info_flags;
    unsigned short dev_class;
    unsigned short dev_subclass;
    char id[64];
    char name[80];
    struct snd_pcm_str streams[2];
    struct mutex open_mutex;
    wait_queue_head_t open_wait;
    void *private_data;
    void (*private_free) (struct snd_pcm *pcm);
    struct device *dev; /* actual hw device this belongs to */
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
    struct snd_pcm_oss oss;
#endif
};

1 snd_pcm是掛在snd_card下面的一個snd_device
2 snd_pcm中的欄位:streams[2],該數組中的兩個元素指向兩個snd_pcm_str結構,分別代表playback stream和capture stream。
3 snd_pcm_str中的substream欄位,指向snd_pcm_substream結構
4 snd_pcm_substream是pcm中介層的核心,絕大部分任務都是在substream中處理,尤其是他的ops(snd_pcm_ops)欄位,
許多user空間的應用程式通過alsa-lib對驅動程式的請求都是由該結構中的函數處理。它的runtime欄位則指向snd_pcm_runtime結構,
snd_pcm_runtime記錄這substream的一些重要的軟體和硬體運行環境和參數。

struct snd_pcm_str {
    int stream;                /* stream (direction) */
    struct snd_pcm *pcm;
    /* -- substreams -- */
    unsigned int substream_count;
    unsigned int substream_opened;
    struct snd_pcm_substream *substream;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
    /* -- OSS things -- */
    struct snd_pcm_oss_stream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
    struct snd_info_entry *proc_root;
    struct snd_info_entry *proc_info_entry;
#ifdef CONFIG_SND_PCM_XRUN_DEBUG
    unsigned int xrun_debug;    /* 0 = disabled, 1 = verbose, 2 = stacktrace */
    struct snd_info_entry *proc_xrun_debug_entry;
#endif
#endif
};

struct snd_pcm_substream {
    struct snd_pcm *pcm;
    struct snd_pcm_str *pstr;
    void *private_data;        /* copied from pcm->private_data */
    int number;
    char name[32];            /* substream name */
    int stream;            /* stream (direction) */
    struct pm_qos_request_list latency_pm_qos_req; /* pm_qos request */
    size_t buffer_bytes_max;    /* limit ring buffer size */
    struct snd_dma_buffer dma_buffer;
    unsigned int dma_buf_id;
    size_t dma_max;
    /* -- hardware operations -- */
    struct snd_pcm_ops *ops;
    /* -- runtime information -- */
    struct snd_pcm_runtime *runtime;
        /* -- timer section -- */
    struct snd_timer *timer;        /* timer */
    unsigned timer_running: 1;    /* time is running */
    /* -- next substream -- */
    struct snd_pcm_substream *next;
    /* -- linked substreams -- */
    struct list_head link_list;    /* linked list member */
    struct snd_pcm_group self_group;    /* fake group for non linked substream (with substream lock inside) */
    struct snd_pcm_group *group;        /* pointer to current group */
    /* -- assigned files -- */
    void *file;
    int ref_count;
    atomic_t mmap_count;
    unsigned int f_flags;
    void (*pcm_release)(struct snd_pcm_substream *);
    struct pid *pid;
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
    /* -- OSS things -- */
    struct snd_pcm_oss_substream oss;
#endif
#ifdef CONFIG_SND_VERBOSE_PROCFS
    struct snd_info_entry *proc_root;
    struct snd_info_entry *proc_info_entry;
    struct snd_info_entry *proc_hw_params_entry;
    struct snd_info_entry *proc_sw_params_entry;
    struct snd_info_entry *proc_status_entry;
    struct snd_info_entry *proc_prealloc_entry;
    struct snd_info_entry *proc_prealloc_max_entry;
#endif
    /* misc flags */
    unsigned int hw_opened: 1;
};

建立一個pcm可以用下面一張建立pcm的調用的順序圖表進行描述:

 

                                                             建立pcm的順序圖表

snd_pcm_set_ops    設定作業該pcm的控制/操作介面函數,參數中的snd_pcm_ops結構中的函數通常就是我們驅動要實現的函數
snd_card_register    註冊音效卡,在這個階段會遍曆音效卡下的所有邏輯裝置,並且調用各裝置的註冊回呼函數,
對於pcm,就是第二步提到的snd_pcm_dev_register函數,該回呼函數建立了和使用者空間應用程式(alsa-lib)通訊所用的裝置檔案節點:/dev/snd/pcmCxxDxxp
和/dev/snd/pcmCxxDxxc (這些裝置節點由alsa-lib使用,使用者不直接使用)。

snd_pcm_dev_register調用snd_register_device_for_dev,snd_register_device_for_dev調用device_create建立裝置節點。snd_register_device_for_dev有個參數
snd_pcm_f_ops,它是一個標準的檔案系統file_operations結構數組,它的定義在sound/core/pcm_native.c中:並被記錄在snd_minors[minor]中的欄位f_ops中。
/*
 *  Register section
 */
//used in pcm.c
const struct file_operations snd_pcm_f_ops[2] = {
    {
        .owner =        THIS_MODULE,
        .write =        snd_pcm_write,
        .aio_write =    snd_pcm_aio_write,
        .open =         snd_pcm_playback_open,
        .release =      snd_pcm_release,
        .llseek =       no_llseek,
        .poll =         snd_pcm_playback_poll,
        .unlocked_ioctl =    snd_pcm_playback_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,
        .aio_read =        snd_pcm_aio_read,
        .open =        snd_pcm_capture_open,
        .release =        snd_pcm_release,
        .llseek =        no_llseek,
        .poll =        snd_pcm_capture_poll,
        .unlocked_ioctl =    snd_pcm_capture_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/sound.c中有alsa_sound_init()函數,其中有:
register_chrdev(major, "alsa", &snd_fops),參數major與之前建立pcm裝置是device_create時的major是同一個,這樣的結果是,
當應用程式open裝置檔案/dev/snd/pcmCxDxp時,會進入snd_fops的open回呼函數。

static const struct file_operations snd_fops =
{
    .owner =    THIS_MODULE,
    .open =    snd_open,
    .llseek =    noop_llseek,
};

snd_open函數,它首先從inode中取出此裝置號,然後以次裝置號為索引,從snd_minors全域數組中取出當初註冊pcm裝置時填充的snd_minor結構
,然後從snd_minor結構中取出pcm裝置的f_ops,並且把file->f_op替換為pcm裝置的f_ops,緊接著直接調用pcm裝置的f_ops->open(),然後返回。
因為file->f_op已經被替換,以後,應用程式的所有read/write/ioctl調用都會進入pcm裝置自己的回呼函數中,也就是snd_pcm_f_ops結構中定義的回調。

本博內容均由http://blog.csdn.net/droidphone原創

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.