LINUX的ALSA驱动框架+ALSA_LIB代码阅读一条龙

#说明

文章架构分为两段:

1. 应用层的alsa_lib+ 部分alsa驱动代码阅读分析;

2. 专门的alsa驱动代码阅读分析.

由于篇幅过长,代码的各个结构体的出处,对应的函数等内容此文章未贴出,可以通过下面的两个链接阅读原文.

alsa_lib代码阅读

https://gitee.com/suiren/s5p6818_alsa_driver_simplify/blob/master/note/alsa_app_read.c

alsa驱动框架代码阅读

https://gitee.com/suiren/s5p6818_alsa_driver_simplify/blob/master/note/alsa_read2.c

#app + alsa_lib 执行流程总结.

1. app为要执行的alsa测试程序, 代码获取及编译方法可参考该blog.

Alsa_lib及alsa测试应用程序静态编译说明_芝麻狐RX的博客-CSDN博客

default 选择默认的pcm设备, audio.wav为音频文件. 通过这一条命令, 使用情景分析法,阅读alsa_lib,alsa框架和驱动代码.
./app default audio.wav

2.   snd_pcm_open (&capture_handle, argv[1],SND_PCM_STREAM_PLAYBACK,0);
     依次打开 opt/share/alsa/alsa.conf,opt/share/alsa/cards/aliases.conf, opt/share/alsa/pcm/default.conf,share/alsa/pcm/dmix.conf 等文件, 其中dmix.conf是非必要的.命令中的"default" 参数, 与以上文件, 找到所期望的设备节点, 即controlC0,和pcmC0D0p. 该过程对应alsa_lib中的函数    _config_search_definition(), 而真正打开pcmC0D0p设备节点的函数为 snd_pcm_hw_open().


2.1  snd_pcm_open() -> snd_pcm_hw_open() 函数会直接open() pcmC0D0p设备文件, 获得设备节点fd. open() 将调用的是驱动的snd_open().snd_open() 申请DMA通道, 用于与i2s控制器传输音频数据.接下来会创建snd_pcm_t *pcm结构体, 由于alsa_lib在后面的执行流程里创建多个snd_pcm_t *pcm结构, 就将此处创建的pcm结构体称为pcm0吧.

3.1  snd_pcm_hw_params_malloc() 为 snd_pcm_hw_params_t *hw_params 结构体分配内存. hw_params结构体用于保存音频参数,如采样率,声道数等.


3.2  snd_pcm_hw_params_any() 初始化 hw_params. 设置hw_params的各个参数的min和max. 一般min是设置为0. max为alsa_lib的默认值.


3.3  snd_pcm_hw_params_set_format(),  snd_pcm_hw_params_set_rate_near(),  snd_pcm_hw_params_set_channels() 作用是设置hw_params内的值.


3.4  alsa_lib在使用hw_params去保存设置参数时, 不是直接地去保存其值, 而是先将判断欲设置的值, 与hw_params的期望值进行比较. 以下为个人理解.
        譬如设置采样速率rate, 音频文件的rate为44100, 而hw_params的期望min为8000, 期望max为96000, 则音频文件的rate > alsa系统的min,那么hw_params中rate的min将被替换为44100. 之所以替换min,而非max, 是避免精度损失吧.而之所以能够避免精度损失, 是因为最终hw_params的rate的
期望值会传给驱动, 驱动会根据自身的硬件能力,其或许不支持设置min的值,又或许不支持设置max的值, 从而选择best的rate值.
        以上确实是个人理解, 因为我当前使用的开发板的alsa驱动, 直接就是使用dts的默认值.....

4.   snd_pcm_hw_params() 将hw_params 传给驱动, 驱动选择best的参数并返回.


4.1  snd_pcm_hw_params() -> snd_pcm_rate_open()会又创建一个 snd_pcm_t *pcm结构体, 称其为pcm2吧.同时也会创建snd_pcm_rate_t *rate结构体.


4.2  snd_pcm_hw_params() -> snd_pcm_rate_hw_params() -> snd_pcm_hw_hw_params(),会执行ioctl(pcm_hw->fd,SNDRV_PCM_IOCTL_HW_PARAMS,params),该ioctl()对于驱动函数为snd_pcm_hw_params(), 作用是将hw_params传为驱动, 而驱动则会判断自己的best参数是否在hw_params的范围内, 若是,则修改hw_params为驱动的best参数,并返回给应用, 否则返回错误. 应用将best参数保存到rate->info.out内.


4.3  snd_pcm_hw_params() -> snd_pcm_rate_hw_params() -> snd_pcm_mmap(), 将执行 mmap(), 驱动将与i2s控制器所对应的DMA的buffer映射给应用,应用将mmap()返回的地址,保存到pcm0->mmap_channels->addr. 这里的mmap_channels也是分为mmap_channels[0]和[1]的. pcm0->running_areas所指向的内存地址也改为pcm0->mmap_channels->addr的值.同时将用户设置的pcm参数,即我们根据音频文件而设置的参数, 保存到rate->info.in.


4.3  snd_pcm_hw_params() -> snd_pcm_rate_hw_params() -> linear_init(), 将根据rate->info.in 和rate->info.out,即输入音频数据是一套参数,输出的音频数据又是另一套参数, 设置音频数据的转换函数以及转换参数.我这边使用的是转换函数linear_expand_s16, 进行采样率从44100 到48000的转换.

5    snd_pcm_prepare(), 主要就是执行 ioctl(fd, SNDRV_PCM_IOCTL_PREPARE); 该ioctl()对于驱动函数为snd_pcm_prepare(). ioctl()->snd_pcm_prepare()  判断声卡的状态为完全开启,并设置数据流的状态为SNDRV_PCM_STATE_PREPARED.

6    snd_pcm_writei(),
6.1  snd_pcm_writei()->snd_pcm_area_copy() , 将作为参数传入的音频数据, 复制到pcm2->running_areas. running_areas分为 running_areas[0]和[1], 即对应左右声道.


6.2  snd_pcm_writei() -> snd_pcm_rate_write_areas1(),使用linear_init所设置的转换函数及参数,将pcm2->running_areas的数据转换并保存到pcm0->running_areas. pcm0->running_areas所在的内存是通过mmap获得的.snd_pcm_writei() -> snd_pcm_rate_start(), 将执行 ioctl(hw->fd, SNDRV_PCM_IOCTL_START);该ioctl()对应驱动函数为snd_pcm_action(), 作用为设置好i2s控制器的i2s的传输模式,采样位深,模式为i2s模式,使能i2s的DMA传输功能. 这样i2s控制器就开始通过DMA获取音频数据,发送给声卡(codec).

############驱动相关分析############


# 应用层中执行的函数, 与驱动的函数的对应关系.
#1. snd_pcm_open()->open()->snd_open()
#2. snd_pcm_hw_params() -> snd_pcm_rate_hw_params() -> snd_pcm_hw_hw_params() -> ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) ->snd_pcm_hw_params()
#3. snd_pcm_hw_params() -> snd_pcm_rate_hw_params() -> snd_pcm_mmap() -> mmap() -> snd_pcm_mmap()
#4. snd_pcm_prepare() ->  ioctl(fd, SNDRV_PCM_IOCTL_PREPARE);
#5. snd_pcm_writei() -> ioctl(hw->fd, SNDRV_PCM_IOCTL_START);

###########音频数据传输路径###########

音频文件 => pcm_buffer(用户个人应用程序分配) => pcm_buffer(alsa_lib分配) => (音频数据格式转换) => DMA_buffer(驱动分配并映射给alsa_lib) => i2s控制器 => 声卡

音频数据格式转换, 就是采样率的转换等.

#下面为alsa_lib代码的各个结构体间的关系表.

snd_pcm_info_t info; <tag4> <ioctrl>
    /* RO/WR (control): stream direction */
    ->int stream;

snd_pcm_hw_t *hw  <tag1> <obj2>
    ->int version; <tag2>
    ->int fd; <tag2> </dev/snd/pcmC0D0p> <<tag80><ioctl>>
    ->int card, device, subdevice; <tag2>
    ->volatile struct snd_pcm_mmap_status * mmap_status; <tag10> <point to obj3>
    ->struct snd_pcm_mmap_control *mmap_control; <tag11>  <point to obj4>
    ->struct snd_pcm_sync_ptr *sync_ptr; <tag12> <point to obj5> <<tag79> <ioctl>> <<tag81><ioctl>>

snd_pcm_t *pcm; <tag3> <obj1>
    ->name "pcm0"
    ->int stream; <tag5>
    ->snd_pcm_t *op_arg; <tag6>  <point to obj1>
    ->snd_pcm_t *fast_op_arg; <tag6> <point to obj1>
    ->void *private_data; <tag7> <point to obj2>
    ->int poll_fd; <tag8> </dev/snd/pcmC0D0p>
    ->snd_pcm_rbptr_t hw;
        ->volatile snd_pcm_uframes_t *ptr; <tag14> <point to obj6>
        ->int fd ; <tag14> </dev/snd/pcmC0D0p> <<tag66> <ioctl>>
    ->snd_pcm_rbptr_t appl;
        ->volatile snd_pcm_uframes_t *ptr; <tag15> <point to obj7>
        ->int fd ; <tag15> </dev/snd/pcmC0D0p>
    ->snd_pcm_channel_info_t *mmap_channels; <tag50>
        =>mmap_channels[1] <<tag52> <ioctl>>
            ->u.mmap.fd <tag53> </dev/snd/pcmC0D0p>
            ->u.mmap.offset <tag53>
            ->void *addr; <<tag54> <mmap>> <obj14>
    ->snd_pcm_channel_area_t *running_areas; <tag51>
        =>running_areas[1]
            ->void *addr; <tag55> <cp from obj14>
    ->unsigned int rate;<obj16>        /* rate in Hz */

struct snd_pcm_sync_ptr *sync_ptr; <tag9> <obj5> <<tag16> <ioctl>>
    ->s.status <obj3>
        ->hw_ptr <obj6> <<tag67> <value set 0>>
    ->c.control <obj4>
        ->appl_ptr <obj7> <<tag67> <value set 0>>

snd_pcm_format_t sformat; <tag18> <<tag19><set value>> <obj8>

snd_pcm_plug_t *plug; <tag17> <obj10>
    ->snd_pcm_format_t sformat; <tag20> <copy from obj8>
    ->snd_pcm_t *req_slave; <tag21> <point to obj1>
    ->snd_pcm_generic_t gen;
        ->snd_pcm_t *slave; <tag21> <point to obj1> <change to >  <tag43>  <point to obj15>

snd_pcm_t *plug_pcm; <tag22> <obj9>
    ->name "pcm1"
    ->snd_pcm_t *op_arg; <tag23>  <point to obj9> <change to > <tag65> <point to obj15>
    ->snd_pcm_t *fast_op_arg; <tag23> <point to obj9> <change to > <tag65> <point to obj15>
    ->void *private_data; <tag24> <point to obj10>
    ->int poll_fd; <tag25> </dev/snd/pcmC0D0p>

snd_pcm_t *capture_handle; <tag27> <tag28> <point to obj9>
################end open######

snd_pcm_hw_params_t *hw_params; <tag26> <<tag29> <ioctl>> <<tag45> <ioctl>> <<tag46> <ioctl>> <<tag47> <ioctl>> <<tag48> <ioctl> > <<tag49><ioctl><

#############end snd_pcm_hw_params_malloc #########

snd_pcm_plug_params_t clt_params;
snd_pcm_plug_params_t slv_params; <tag32> <<tag33> <set value>>
    ->snd_pcm_format_t sformat <obj12>

snd_pcm_rate_t *rate; <tag30> <obj18>
    ->snd_pcm_generic_t gen;
        ->snd_pcm_t *slave; <tag31> <point to obj1>
    ->snd_pcm_format_t sformat; <tag34> <point to obj12>
    ->void *obj; <tag38> <point to obj17>
    ->snd_pcm_uframes_t appl_ptr; <obj20>
    ->snd_pcm_uframes_t hw_ptr; <obj19>
    ->info.in
        ->int rate; <tag90> <44100>
    ->info.out
        ->int rate; <tag56> <cp from obj16> <48000>

snd_pcm_t *params_pcm; <tag35> <obj15>
    ->name "pcm2"
    ->snd_pcm_t *op_arg; <tag36>  <point to obj15>
    ->snd_pcm_t *fast_op_arg; <tag36> <point to obj15>
    ->void *private_data; <tag39> <point to obj18>
    ->int poll_fd; <tag40> </dev/snd/pcmC0D0p>
    ->snd_pcm_rbptr_t hw;
        ->volatile snd_pcm_uframes_t *ptr; <tag41> <point to obj19>
    ->snd_pcm_rbptr_t appl;
        ->volatile snd_pcm_uframes_t *ptr; <tag42> <point to obj20>
    ->snd_pcm_channel_info_t *mmap_channels; <tag58>
        =>mmap_channels[0]
            ->void *addr; <tag60> <obj22>
        =>mmap_channels[1]
            ->void *addr; <tag61> <copy from obj22> <obj23>
    ->snd_pcm_channel_area_t *running_areas; <tag59> <obj60>
        =>running_areas[0]
            ->void *addr; <tag63> <copy from obj22>
        =>running_areas[1]
            ->void *addr; <tag64> <copy from obj23>

struct rate_linear *rate_linear; <tag37> <obj17>
    ->unsigned int pitch; <tag91>
    ->int16_t *old_sample; <tag57>
        =>old_sample[1] <tag77> <copy from obj68>

###############end snd_pcm_hw_params##############

char *buffer; <tag68> <obj28>

snd_pcm_channel_area_t areas[2]; <tag73>
    =>areas[0] <obj66>
        ->void *addr; <tag69> <copy from obj28>
    =>areas[1]
        ->void *addr; <tag69> <copy from obj28> <obj68>

const snd_pcm_channel_area_t *pcm_areas; <tag70> <tag71> <point to obj60>

char *dst =>  pcm2->running_areas ->addr; <tag72> <<tag75><cp data from obj67>>
char *src => areas[0]->addr; <tag74> <obj66> <obj67>

上面为alsa_lib+部分alsa驱动框架的代码阅读总结.

下面为alsa驱动框架的专门分析

########驱动初始化流程##########


1. alsa_sound_init()
   申请设备号116作为alsa这一类设备.

2. nx_i2s_probe()
          i2s控制器初始化, 其设备信息保存在dts. 创建i2s_dai结构体  nx_i2s_set_plat_param(), 获取dts的信息,如采样率等信息,将采样率这些信息保存到i2s_dai<obj20>中, 以位域的形式.初始化控制器的时钟, 并开启i2s接口, 但其他i2s功能依旧关闭中. snd_soc_register_dais()函数, 将i2s_dai加入cpu_cmpnt<obj2> 链表, 设置i2s_dai->driver.

3. es8316_i2c_probe()
        创建codec_dai结构体,snd_soc_register_dais()函数, 将code_dai加入codec_cmpnt<obj14>链表. 设置codec_dai->driver.

4. nx_simple_card_probe()
          解析dts, 获得simple_card节点下的cpu_dai和codec_dai的名字.
        devm_snd_soc_register_card(), 根据dts的cpu_dai和codec_dai的名字, 遍历component_list->cpu_cmpnt链表 和component_list->codec_cmpnt链表, 找到对应的cpu_dai和codec_dai.
         devm_snd_soc_register_card()-> soc_probe_link_components() , 对已经获得的cpu_dai和codec_dai进行probe,通过dai->driver->probe函数. dai->driver的指向由snd_soc_register_dais()函数设置.通过codec_dai->driver->probe(), 才真正地设置codec的寄存器,对其进行初始化.           snd_ctl_create() 设置设备节点名为 controlC%d.  对应struct snd_card *card结构体, 通过结构体可以获得cpu_dai和codec_dai,可以通过这些dai结构体,进而设置其对应的硬件的寄存器.
   snd_pcm_new_stream() 设置另一个的设备节点名为 pcmC%iD%i%c, 对应着是struct snd_pcm_str streams结构体,该结构体用于进行数据传输. platform->driver->pcm_new()=>nx_pcm_new(), 分配DMA内存, 用于保存音频数据, i2s控制器从这里获取数据.

simple_card dts:
    simple_card: sound {
        compatible = "nexell,simple-audio-card";

        simple-audio-card,dai-link@0 {
            format = "i2s";
            cpu {
                sound-dai = <&i2s_0 0>;
            };

            codec {
                sound-dai = <&es8316>;
            };
        };
    };

驱动总结:


驱动中有多条链表.
component_list 上挂接着cpu_cmpnt链表 和 codec_cmpnt链表.
cpu_cmpnt链表上挂接已注册的cpu_dai.
codec_cmpnt链表上挂接已注册的codec_dai.

        struct simple_card_data card_data结构体中struct snd_soc_pcm_runtime *rtd结构体 的包含自己所需要的cpu_dai和codec_dai.
        我认为cpu_dai对应着数据传送方式,即i2s控制器,而codec_dai对应为声卡,我这里是es8316.

component_list<list2> => list ( cpu_cmpnt<obj2>) => list (codec_cmpnt<obj14>)

cpu_cmpnt<obj2>
    ->list => list (i2s_dai<obj20>)

codec_cmpnt<obj14>
    ->list => list (codec_dai<obj21>)

#alsa驱动框架各个结构体的关系表.

component_list; <list2>
platform_list; <list3>
codec_list;   <list5>

struct platform_device *i2s_pdev <tag12>
    ->struct device dev; <obj3>
        ->void *driver_data <tag30> <point to obj7>

struct nx_i2s_snd_param *par; <tag1> <obj7>
    ->struct device *dev; <tag15> <point to obj3>
    ->struct snd_soc_dai_driver dai_drv; <tag2> <obj1>
        ->struct snd_soc_pcm_stream playback;
            ->unsigned int rates; <tag158> < value == 1<<7 >
    ->int sample_rate; <tag157> <dts> <48000>
    ->struct nx_pcm_dma_param play; <tag3> <obj50>
        ->dma_addr_t peri_addr; <tag4>
        ->struct device *dev; <tag13> <point to obj3>
        ->char *dma_ch_name; <tag14> <"i2s">


struct snd_soc_component *cmpnt; <tag5> <obj2>
    ->char *name; <tag163> <"c0055000.i2s">
    ->struct device *dev; <tag16> <point to obj3>
    ->struct snd_soc_dai_driver *dai_drv; <tag6> <point to obj1>
    ->struct list_head dai_list; <list1>
    ->struct regmap *regmap; <tag11>
    ->const struct snd_soc_component_driver *driver; <tag17> <point to obj4>
    ->struct list_head list; <tag21> <add to list2>

struct snd_soc_dai *i2s_dai; <tag7> <obj20>
    ->struct device *dev; <tag19> <point to obj3>
    ->struct snd_soc_component *component; <tag8> <point to obj2>
    ->struct snd_soc_dai_driver *driver; <tag9> <point to obj1>
    ->struct list_head list; <tag10> <add to list1>
    ->char *name <tag20> <"c0055000.i2s">
    ->void *playback_dma_data; <tag138> <point to obj50>
    ->unsigned int rate; <tag159>
    ->unsigned int channels; <tag159>
    ->unsigned int sample_bits; <tag159>

struct snd_soc_platform *soc_platform; <tag22> <obj23>
    ->struct device *dev; <tag27> <point to obj3>
    ->const struct snd_soc_platform_driver *driver; <tag28> <point to obj5>
    ->struct list_head list; <tag29> <add to list3>
    ->struct snd_soc_component component;
        ->char *name; <tag23>
        ->struct device *dev; <tag24> <point to obj3>
        ->const struct snd_soc_component_driver *driver; <tag25> <point to obj6>

static const struct snd_soc_component_driver nx_i2s_component; <obj4> <tag18>

struct snd_soc_platform_driver nx_pcm_platform; <obj5>  <tag26>
    ->struct snd_soc_component_driver component_driver; <obj6>

##########

struct i2c_client *i2c <tag32> <obj30>
    ->struct device *dev; <obj11>
        ->void *driver_data; <tag33> <point to obj9>

struct es8316_priv *es8316; <tag31> <obj9>

struct snd_soc_codec *codec; <tag34> <obj10>
    ->struct device *dev; <tag40> <point to obj11>
    ->const struct snd_soc_codec_driver *driver; <tag41> <point to obj12>
    ->struct list_head list; <tag56> <add to list5>
    ->void *control_data; <point to obj30> <tag103>
    ->struct snd_soc_component component; <obj14>
        ->char *name; <tag163> <"ES8316.0-0011">
        ->struct snd_soc_card *card;  <tag102> <point to obj17>
        ->struct snd_soc_dapm_context dapm; <obj29>
            ->struct snd_soc_card *card;  <tag101> <point to obj17>
        ->struct snd_soc_codec *codec; <tag35> <point to obj10>
        ->char *name; <tag36>
        ->struct device *dev; <tag37> <point to obj11>
        ->const struct snd_soc_component_driver *driver; <tag38> <point to obj12>
        ->struct snd_soc_dai_driver *dai_drv; <tag42> <point to obj13>
        ->struct list_head dai_list; <list4>
        ->struct list_head list; <tag55> <add to list2>

struct snd_soc_dai *codec_dai; <tag44> <obj21>
    ->struct device *dev;  <tag47> <point to obj11>
    ->struct snd_soc_component *component; <tag46> <point to obj14>
    ->struct snd_soc_dai_driver *driver; <tag48> <point to obj13>
    ->struct list_head list; <tag50> <add to list4>
    ->char *name; <tag45> <"ES8316 HiFi">
    ->struct snd_soc_codec *codec; <tag51> <point to obj10>

static struct snd_soc_codec_driver soc_codec_dev_es8316; <tag39> <obj12>
static struct snd_soc_dai_driver es8316_dai; <obj13> <tag43>

###############

struct platform_device *simple_card_pdev <tag58>
    ->struct device dev; <obj15>

struct simple_card_data *simple_card_priv; <tag59> <obj16>
    struct simple_dai_props *dai_props; <tag61>
    ->struct snd_soc_card snd_card; <obj17>
        ->struct list_head dapm_list; <list7>
        ->struct device *dev; <tag60> <point to obj15>
            ->void *driver_data; <tag75> <point to obj17>
        ->char *name; <tag62>
        ->void *drvdata <tag71> <point to obj16>
        ->struct snd_card *snd_card; <tag95> <point to obj26>
        ->struct snd_soc_dapm_context dapm;
            ->struct list_head list; <tag100> <add to list7>
            ->struct device *dev; <tag97> <point to obj15>
            ->struct snd_soc_card *card; <tag99> <point to obj17>
        ->struct snd_soc_pcm_runtime *rtd; <tag76> <obj18>
            ->struct snd_pcm *pcm; <tag120> <point to obj34>
            ->struct device *dev; <tag104> <point to obj15>
                ->struct device *parent; <tag105>
                ->void *driver_data <tag106> <point to obj18>
            ->struct snd_soc_card *card; <tag78> <point to obj17>
            ->struct snd_soc_dai_link *dai_link; <tag79> <point to obj19>
            ->struct snd_soc_dai **codec_dais; <tag80>
            ->struct snd_soc_dai *codec_dai; <tag82> <point to obj21>
            ->struct snd_soc_dai *cpu_dai; <tag81> <point to obj20>
            ->struct snd_soc_codec *codec; <tag83> <point to obj10>
            ->struct snd_soc_platform *platform; <tag84> <point to obj23>
        ->struct snd_soc_pcm_runtime *rtd_aux; <tag77> <point to obj18>
        ->struct snd_soc_dai_link *dai_link; <obj19>
            ->const char *name; <tag70> <"c0055000.i2s-ES8316 HiFi">
            ->const char *stream_name;    <tag70> <"c0055000.i2s-ES8316 HiFi">
            ->const char *cpu_dai_name; <tag69> <"c0055000.i2s">
            ->const char *codec_dai_name; <tag69> <"ES8316 HiFi">
            ->struct snd_soc_dai_link_component *codecs; <tag74>

struct snd_card *card; <tag86> <obj26>
    ->struct device *dev; <tag87> <point to obj15>
    ->struct list_head devices;
        ->struct list_head *prev; <list6>
    ->struct device ctl_dev;  <obj39>
        ->struct device *parent; <tag89> <point to obj24>
    ->struct device card_dev; <obj24>
        ->struct device *parent; <tag88> <point to obj15>

struct snd_device *clt_dev; <tag90>
    ->struct snd_card *card; <tag91> <point to obj26>
    ->void *device_data; <tag92> <point to obj26>
    ->struct list_head list; <tag93> <add to list6>

struct snd_pcm *pcm; <tag107> <obj34>
    ->void *private_data; <tag121> <point to obj18>
    ->struct snd_card *card; <tag108> <point to obj26>
    ->struct snd_pcm_str streams[2];
        =>streams[SNDRV_PCM_STREAM_PLAYBACK] <obj37>
            ->struct snd_pcm *pcm; <tag110> <point to obj34>
            ->struct snd_pcm_substream *substream; <tag115> <point to obj38>
            ->struct device dev; <obj44>
                ->struct device *parent; <tag111> <point to obj24>

struct snd_pcm_substream *substream; <tag112> <obj38>
    ->struct snd_pcm *pcm; <tag113> <point to obj34>
    ->struct snd_pcm_str *pstr; <tag114> <point to obj37>
    ->void *file; <tag150>  <point to obj58>
    ->struct snd_pcm_runtime *runtime; <tag135> <obj59>
        ->void *private_data <tag140> <point to obj51>
        ->struct snd_pcm_hardware hw; <tag145> <data copy from obj53> <<tag146><continue to set value>>
        ->unsigned int rate; <tag160>
        ->unsigned int channels <tag160>
        ->dma_addr_t dma_addr;     <tag162>
    ->void *private_data <tag136> <point to obj18>
    ->struct snd_dma_buffer dma_buffer;
        ->unsigned char *area; <tag123> <dma buffer alloc>
        ->struct snd_dma_device dev;
            ->struct device *dev; <tag122> <point to obj15>

struct snd_device *pcm_dev; <tag116>
    ->struct snd_card *card; <tag117> <point to obj26>
    ->void *device_data; <tag118> <point to obj34>
    ->struct list_head list; <tag119> <add to list6>

snd_minors[x]; <tag127> <point to obj40>
snd_minors[x2]; <tag131> <point to obj42>

struct snd_minor *clt_preg; <tag124> <obj40>
    ->void *private_data; <tag125> <point to obj26>
    ->struct device *dev; <tag126> <point to obj39>

struct snd_minor *pcm_preg; <tag128> <obj42>
    ->void *private_data; <tag129> <point to obj34>
    ->struct device *dev; <tag130> <point to obj44>

###################snd_open_device##############
struct snd_pcm_runtime *runtime; <tag132>
    ->struct snd_pcm_mmap_status *status; <tag133>
    ->struct snd_pcm_mmap_control *control; <tag134>

struct nx_pcm_runtime_data *prtd; <tag141> <obj51>
    ->struct device *dev; <tag142> <point to obj15>
    ->struct nx_pcm_dma_param *dma_param; <tag143> <point to obj50>
    ->struct dma_chan  *dma_chan; <tag144>

static struct snd_pcm_hardware nx_pcm_hardware; <obj53>

struct snd_pcm_file *pcm_file; <tag147> <obj58>
    ->struct snd_pcm_substream *substream; <tag148> <point to obj38>

struct file *file <tag149>
    ->void *private_data  <tag151> <point to obj58>

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ALSA(Advanced Linux Sound Architecture)是Linux内核中的音频驱动框架,它提供了一套完整的音频处理流程,包括音频采集、音频处理和音频播放等功能。ALSA框架的设计目标是提供一个高效、灵活、可靠的音频处理框架,让开发人员能够方便地开发音频应用程序。 ALSA框架的核心包括以下几个组件: 1. 驱动程序:驱动程序是ALSA框架的核心组件,负责管理音频设备硬件,并提供音频数据输入输出的接口。ALSA驱动程序一般由硬件厂商或开源社区开发,可以通过内核模块的形式加载到Linux内核中。 2. 应用程序接口:ALSA框架提供了一套完整的应用程序接口,包括ALSA库和ALSA命令行工具。ALSA库提供了一组API,让开发人员能够方便地访问ALSA驱动程序提供的音频数据输入输出接口。ALSA命令行工具则提供了一组命令行工具,让用户能够方便地对音频设备进行配置和管理。 3. 中间件:ALSA框架还提供了一些中间件组件,如MIDI子系统、混音器子系统等,用于提供更高级的音频处理功能。 ALSA框架的音频处理流程如下: 1. 音频采集:当音频设备接收到音频信号时,ALSA驱动程序将音频信号采集到内存中,并通过DMA(直接内存访问)将音频数据写入音频缓冲区。 2. 音频处理:ALSA驱动程序将音频信号从音频缓冲区读取到内存中,然后对音频数据进行处理。音频处理包括音频格式转换、音频采样率转换、音频混音等处理。 3. 音频播放:ALSA驱动程序将处理后的音频数据从内存中读取到音频缓冲区,并通过DMA将音频数据传输到音频设备中进行播放。 总之,ALSA框架提供了一套完整的音频处理流程,让开发人员能够方便地开发音频应用程序,并提供了一组API和命令行工具,方便用户对音频设备进行配置和管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值