#说明
文章架构分为两段:
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>