一. ALSA 架构: D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\core
SNDRV_DEFAULT_IDX1 -1 SNDRV_DEFAULT_STR1 NULL extra_size private_data 成员数据大小 可以通过 snd_dev = (struct sagitta_snd_dev *) card->private_data; 获取card上的私有数据 int snd_card_create(int idx, const char *xid, struct module *module, int extra_size, struct snd_card **card_ret) //sound/core/init.c a. idx 为 -1, 则任意申请一个free的id,最大声卡数目为32/8 b. 申请(card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);) 并初始化 struct snd_card 的成员变量,包括设置获得的id c. snd_ctl_create(card); //控制接口? //sound/core/control.c snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //sound/core/device.c struct snd_device *dev; dev->type = type; ==SNDRV_DEV_CONTROL list_add(&dev->list, &card->devices); 添加到card->devices链表 d. snd_info_card_create(card); //sound/core/info.c d.1 在proc目录下创建card%d节点
snd_card_set_dev(card, dev); ?
int device: device number snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm) //sound/core/pcm.c pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); //创建回放以及录制的流 stream snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count) 创建 "pcm%i%c" proc 文件 for (idx = 0, prev = NULL; idx < substream_count; idx++) struct snd_pcm_substream *substream = kzalloc(sizeof(*substream), GFP_KERNEL); //创建 stream snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count) snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops) //sound/core/device.c
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, struct snd_pcm_ops *ops) struct snd_pcm_str *stream = &pcm->streams[direction]; struct snd_pcm_substream *substream; for (substream = stream->substream; substream != NULL; substream = substream->next) substream->ops = ops;
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) ?????
snd_card_register(card); int snd_card_register(struct snd_card *card) //sound/core/init.c card->card_dev = device_create(sound_class, card->dev, MKDEV(0, 0), card, "card%i", card->number); snd_device_register_all(card); //比如在dev目录下创建/dev/card%i 节点 //sound/core/device.c list_for_each_entry(dev, &card->devices, list) dev->ops->dev_register(dev) //遍历所有card上的devices节点,并调用节点的dev_register()接口注册 list_add_tail(&substream->link_list, &substream->self_group.substreams); snd_cards[card->number] = card; //snd_cards[] 全局数组 init_info_for_card(card); //proc 文件相关 device_create_file(card->card_dev, &card_id_attrs); //在sys目录下创建相关属性文件 device_create_file(card->card_dev, &card_number_attrs); //在sys目录下创建相关属性文件 //具体 pcm 的注册 (音频数据接口) int snd_pcm_dev_register(struct snd_device *device) list_add_tail(&newpcm->list, &snd_pcm_devices); sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); //playback 方式 sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); //capture 方式 /* register pcm */ //snd_pcm_f_ops[2] 提供给用户态的读写 err = snd_register_device_for_dev(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, str, dev); //sound/core/sound.c 实际创建 "pcmC%iD%ip" 和 "pcmC%iD%ic" 节点 snd_minors[minor] = preg; 为pcm回放与录像创建 sys/目录下的文件 snd_pcm_timer_init() //为每个流创建定时器相关 //sound/core/pcm_timer.c sprintf(timer->name, "PCM %s %i-%i-%i", substream->stream == SNDRV_PCM_STREAM_CAPTURE ? "capture" : "playback", tid.card, tid.device, tid.subdevice); snd_device_register(timer->card, timer) //通知机制 snd_pcm_notify() //对应于 oss 架构 list_for_each_entry(notify, &snd_pcm_notify_list, list) notify->n_register(pcm); //具体 control 的注册 控制接口(比如音量等) int snd_ctl_dev_register(struct snd_device *device) sprintf(name, "controlC%i", cardnum); snd_ctl_f_ops 提供给用户态的读写 snd_register_device_for_dev(SNDRV_DEVICE_TYPE_CONTROL, card, -1, &snd_ctl_f_ops, card, name) 实际创建 "controlC%i" 节点
补充: 涉及其余模块 PCM ---- snd_pcm_new() RAWMIDI -- snd_rawmidi_new() CONTROL -- snd_ctl_create() TIMER -- snd_timer_new() INFO -- snd_card_proc_new() JACK -- snd_jack_new()
ALSA 内核态基本是使用流程: //a. 创建 snd_card 结构体, 内部会创建 control 相关设备 snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0, &card); // 旧版 snd_card_new() //b. 创建 pcm 相关设备 for (i = 0; i < alsa_setup_info->pcm_count; i++) snd_pcm_new(alsa_cxt->card, pcm_info->name, device_idx, 0, pcm_info->capture_count, &pcm); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &alsa_model_pcm_ops); //c. 注册音频设备 snd_card_set_dev(card, dev); snd_card_register(card);
二. ASOC 架构: D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\soc machine + codec + platform machine: 指一块开发板 platform: 某个SOC 如 s3cxxxx,属于某块核心板 iis 音频的DMA 音频的接口 iis codec: 音频芯片 包含iis接口,D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out) 音频的控件(controls) PCM 接口 DAPM(动态音频电源管理) 所有的Codec驱动都要提供以下特性: Codec DAI 和 PCM的配置信息; Codec的IO控制方式(I2C,SPI等); Mixer和其他的音频控件; Codec的ALSA音频操作接口; 必要时,也可以提供以下功能: DAPM描述信息; DAPM事件处理程序; DAC数字静音控制
D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\soc\samsung //samsung的ASOC架构 涉及的接口: cpu端:iis,iic uda134x: iis,l3接口 (int l3_clk; int l3_mode; int l3_data;) int s3c24xx_uda134x_probe(struct platform_device *pdev) //machine 子模块相关 //设置l3口的gpio口属性 s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_clk, "clk") s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_mode, "mode") s3c24xx_uda134x_setup_pin(s3c24xx_uda134x_l3_pins->l3_data, "data") 创建平台设备"soc-audio" static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", .codec_name = "uda134x-codec", //codec .codec_dai_name = "uda134x-hifi", //codec dai .cpu_dai_name = "s3c24xx-iis", //cpu dai .ops = &s3c24xx_uda134x_ops, .platform_name = "samsung-audio", //dma };
static struct snd_soc_card snd_soc_s3c24xx_uda134x = { .name = "S3C24XX_UDA134X", .owner = THIS_MODULE, .dai_link = &s3c24xx_uda134x_dai_link, .num_links = 1, };
D:\kernel_code\linux-3.4.2\linux-3.4.2\sound\soc\soc-core.c //ASOC 架构 int soc_probe(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); snd_soc_register_card(card); 解析出每一项 snd_soc_dai_link 对象 创建 struct snd_soc_pcm_runtime struct snd_soc_pcm_runtime* card->rtd = devm_kzalloc(card->dev, sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), GFP_KERNEL); card->rtd[i].dai_link = &card->dai_link[i]; //struct snd_soc_dai_link list_add(&card->list, &card_list); //全局变量 card_list snd_soc_instantiate_cards(); snd_soc_instantiate_card(card); //从 card_list 链表中,依次取出 struct snd_soc_card* card 1. soc_bind_dai_link(card, i); 1. struct snd_soc_dai *cpu_dai; 从 dai_list 中查找 cpu_dai, 若与snd_soc_dai_link指定的cpu_dai_name名字相同,则赋值 rtd->cpu_dai = cpu_dai; 2. struct snd_soc_codec *codec; 从 codec_list 中查找 codec, 若与snd_soc_dai_link指定的codec_name名字相同,则赋值 rtd->codec = codec; 3. struct snd_soc_dai *codec_dai; 从 dai_list 中查找 codec_dai, 若与snd_soc_dai_link指定的codec_dai_name名字相同,则赋值 rtd->codec_dai = codec_dai; 4. struct snd_soc_platform *platform; 从 platform_list 中查找 platform, 若与snd_soc_dai_link指定的platform_name名字相同,则赋值 rtd->platform = platform; 2. list_for_each_entry(codec, &codec_list, list) //遍历 codec_list 主要功能:codec的cache相关操作 判断是否有 card->num_configs snd_soc_cache_init(codec); //sound/soc/soc-cache.c codec->cache_ops = &cache_types[i]; //全局变量 cache_types codec->cache_ops->init(codec); 3. snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card); //创建card节点以及control节点 4. list_add(&card->dapm.list, &card->dapm_list); //DAPM (动态音频电源管理) 5. snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); //如果指定 card->dapm_widgets sound/soc/soc-dapm.c 6. card->probe(card); 7. soc_probe_dai_link(card, i, order); //order: -2 ~ 2 7.1 struct snd_soc_dai* cpu_dai; /* probe the cpu_dai */ cpu_dai->driver->probe(cpu_dai); 7.2 struct snd_soc_codec *codec; /* probe the CODEC */ soc_probe_codec(card, codec); snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets, driver->num_dapm_widgets); snd_soc_dapm_new_dai_widgets(&codec->dapm, dai); driver->probe(codec); snd_soc_add_codec_controls(codec, driver->controls, driver->num_controls); snd_soc_dapm_add_routes(&codec->dapm, driver->dapm_routes, driver->num_dapm_routes); 7.3 struct snd_soc_dai *codec_dai; /* probe the CODEC DAI */ codec_dai->driver->probe(codec_dai); 7.4 struct snd_soc_platform *platform /* probe the platform */ soc_probe_platform(card, platform); snd_soc_dapm_new_controls(&platform->dapm, driver->dapm_widgets, driver->num_dapm_widgets); driver->probe(platform); snd_soc_add_platform_controls(platform, driver->controls, driver->num_controls); snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes, driver->num_dapm_routes); 7.5 soc_post_component_init(card, codec, num, 0); //DAPM 与 widgets 7.6 soc_new_pcm(rtd, num); //创建PCM //sound/soc/soc-pcm.c soc_pcm_ops->open = soc_pcm_open; ..... snd_pcm_new(rtd->card->snd_card, new_name, num, playback, capture, &pcm); //具体值根据 codec_dai->driver 的设定 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); //soc_pcm_ops 有 ASOC 提供 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops); platform->driver->pcm_new(rtd); //******重点 preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); //申请 DMA 内存 size_t size = dma_hardware.buffer_bytes_max; //全局变量 dma_hardware buf->area = dma_alloc_writecombine(pcm->card->dev, size, &buf->addr, GFP_KERNEL); 8. for 循环 card->num_aux_devs 9. snd_soc_dapm_link_dai_widgets(card); //sound/soc/soc-dapm.c snd_soc_dapm_add_route(w->dapm, &r); 10. if (card->controls) snd_soc_add_card_controls(card, card->controls, card->num_controls); snd_soc_add_controls(); snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix)); 11. if (card->dapm_routes) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); 12. snd_soc_dapm_new_widgets(&card->dapm); 13. snd_soc_dai_set_fmt(card->rtd[i].codec_dai, dai_link->dai_fmt); //设置 codec_dai 格式 snd_soc_dai_set_fmt(card->rtd[i].cpu_dai, dai_link->dai_fmt); //设置 cpu_dai 格式 dai->driver->ops->set_fmt(dai, fmt); 14. card->late_probe(card); 15. snd_soc_dapm_new_widgets(&card->dapm); 16. snd_card_register(card->snd_card); 17. snd_soc_dapm_sync(&card->dapm);
//codec 子模块相关 (sound/soc/codecs) static struct snd_soc_dai_driver uda134x_dai = { .name = "uda134x-hifi", /* playback capabilities */ /* **********struct snd_soc_pcm_stream playback & capture */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda134x_dai_ops, }; snd_soc_register_codec(&pdev->dev, &soc_codec_dev_uda134x, &uda134x_dai, 1); //涉及 codec 以及 codec_dai int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai) struct snd_soc_codec *codec; //处理 codec codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 设置 codec 的成员变量 if (codec_drv->reg_cache_size && codec_drv->reg_word_size) codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default, reg_size, GFP_KERNEL); if (codec_drv->reg_access_size && codec_drv->reg_access_default) fixup_codec_formats(&dai_drv[i].playback); fixup_codec_formats(&dai_drv[i].capture); snd_soc_register_dais(dev, dai_drv, num_dai); struct snd_soc_dai *dai; //处理 codec_dai dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); 设置 dai 的成员变量,即 codec_dai list_add(&dai->list, &dai_list); snd_soc_instantiate_cards(); list_add(&codec->list, &codec_list); snd_soc_instantiate_cards();
//cpu dai 子模块 "s3c24xx-iis" (sound/soc/samsung) ----- platform iis static struct snd_soc_dai_driver s3c24xx_i2s_dai = { .probe = s3c24xx_i2s_probe, .suspend = s3c24xx_i2s_suspend, .resume = s3c24xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, .rates = S3C24XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 2, .channels_max = 2, .rates = S3C24XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, .ops = &s3c24xx_i2s_dai_ops, }; snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai); int snd_soc_register_dai(struct device *dev, struct snd_soc_dai_driver *dai_drv) struct snd_soc_dai *dai; dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL); list_add(&dai->list, &dai_list); snd_soc_instantiate_cards();
//platform dai 子模块 "samsung-audio" (sound/soc/samsung) ----- platform dma static struct snd_soc_platform_driver samsung_asoc_platform = { .ops = &dma_ops, .pcm_new = dma_new, .pcm_free = dma_free_dma_buffers, }; snd_soc_register_platform(&pdev->dev, &samsung_asoc_platform); int snd_soc_register_platform(struct device *dev, struct snd_soc_platform_driver *platform_drv) struct snd_soc_platform *platform; platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL); list_add(&platform->list, &platform_list); snd_soc_instantiate_cards();
三. 音频数据流分析 frame_size ????? sample_size 比如一个sample为左右声道两个采样点,一个采样点16位,则1个sample为4bytes period_size 4096 1024个sample buffer period_size * period_num
struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); int (*ioctl)(struct snd_pcm_substream * substream, unsigned int cmd, void *arg); int (*hw_params)(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); int (*hw_free)(struct snd_pcm_substream *substream); int (*prepare)(struct snd_pcm_substream *substream); int (*trigger)(struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); int (*copy)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count); int (*silence)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count); struct page *(*page)(struct snd_pcm_substream *substream, unsigned long offset); int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); int (*ack)(struct snd_pcm_substream *substream); };
录音过程: static struct snd_pcm_ops alsa_model_pcm_ops = { .open = alsa_model_pcm_open, .close = alsa_model_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = alsa_model_hw_params, .hw_free = alsa_model_hw_free, .prepare = alsa_model_prepare, .trigger = alsa_model_card_trigger, .pointer = alsa_model_pointer, .page = snd_pcm_lib_get_vmalloc_page, };
struct snd_pcm_runtime *runtime; struct snd_pcm_substream *substream; 这两个结构体会在中断回调处理函数中使用 //用户态调用 open()函数时触发 int alsa_model_pcm_open(struct snd_pcm_substream *substream) alsa_model_t *alsa_cxt = snd_pcm_substream_chip(substream); //获取设置时的私有数据 struct snd_pcm_runtime *runtime = substream->runtime; //pcm运行时结构体 runtime->hw = alsa_cxt->hw_parm; //struct snd_pcm_hardware hw_parm; (音频芯片的支持参数) snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ????? //用户态调用 close()函数时触发 int alsa_model_pcm_close(struct snd_pcm_substream *substream)
snd_pcm_lib_ioctl() //alsa 架构提供, 方便用户态通过 ioctl() 接口对codec进行配置.
//用户态进行codec属性配置 比如:申请虚拟地址空间, period 大小,以及 frame 大小, 供用户态使用runtime->dma_area int alsa_model_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); //申请虚拟地址空间, period 大小,以及 frame 大小 int alsa_model_hw_free(struct snd_pcm_substream *substream) snd_pcm_lib_free_vmalloc_buffer(substream); //启动采集操作时的准备工作,用户初始化一些私有数据 int alsa_model_prepare(struct snd_pcm_substream *substream)
//用户态通过 cmd 值使能或停止采集操作。基本是开启/关闭 dma 中断 int alsa_model_card_trigger(struct snd_pcm_substream *substream, int cmd)
//内核态调用该函数获得当前 hw_ptr 的值 snd_pcm_uframes_t alsa_model_pointer(struct snd_pcm_substream *substream) spin_lock_irqsave(&alsa_cxt ->slock, flags); hwptr = alsa_cxt ->hwptr; spin_unlock_irqrestore(&alsa_cxt ->slock, flags); .page = snd_pcm_lib_get_vmalloc_page,
s3c24xx-uda134x 过程: ASOC 架构只使用如下的接口: soc_pcm_ops->open = soc_pcm_open; soc_pcm_ops->close = soc_pcm_close; soc_pcm_ops->hw_params = soc_pcm_hw_params; soc_pcm_ops->hw_free = soc_pcm_hw_free; soc_pcm_ops->prepare = soc_pcm_prepare; soc_pcm_ops->trigger = soc_pcm_trigger; soc_pcm_ops->pointer = soc_pcm_pointer;
以上调用顺序: 一种情况: alsa_model_pcm_open() alsa_model_hw_params() alsa_model_hw_free() alsa_model_hw_params() alsa_model_hw_free() alsa_model_hw_params() alsa_model_hw_free() alsa_model_pcm_close() 另一种情况: alsa_model_pcm_open() alsa_model_hw_params() alsa_model_prepare() alsa_model_prepare() alsa_model_card_trigger() //start alsa_model_pointer() alsa_model_pointer() alsa_model_pointer() alsa_model_pointer() alsa_model_pointer() . . alsa_model_card_trigger() //stop alsa_model_hw_free() alsa_model_pcm_close()
int soc_pcm_open(struct snd_pcm_substream *substream) codec_dai->driver->ops->startup(substream, codec_dai); //codec_dai cpu_dai->driver->ops->startup(substream, cpu_dai); //cpu_dai(platform) platform->driver->ops->open(substream); //dma_open(platform) dma_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); snd_soc_set_runtime_hwparams(substream, &dma_hardware); substream->hw (format, samplerate 等) runtime->private_data = prtd; //设置 runtime 的私有数据, runtime 会在中断回调处理函数中使用 rtd->dai_link->ops->startup(substream); //mechine //设置支持的参数 runtime->hw.rate_min = max(codec_dai_drv->capture.rate_min, cpu_dai_drv->capture.rate_min); runtime->hw.channels_min = max(codec_dai_drv->capture.channels_min, cpu_dai_drv->capture.channels_min); runtime->hw.formats = codec_dai_drv->capture.formats & cpu_dai_drv->capture.formats; runtime->hw.rates = codec_dai_drv->capture.rates & cpu_dai_drv->capture.rates; snd_pcm_limit_hw_rates(runtime); //????? dai->driver->playback.sig_bits soc_pcm_apply_msb(substream, codec_dai); soc_pcm_apply_msb(substream, cpu_dai); snd_pcm_hw_constraint_msbits(); /* Symmetry only applies if we've already got an active stream. */ snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, soc_dai->rate, soc_dai->rate); //????? int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) rtd->dai_link->ops->hw_params(substream, params); //machine codec_dai->driver->ops->hw_params(substream, params, codec_dai); //codec_dai cpu_dai->driver->ops->hw_params(substream, params, cpu_dai); //cpu_dai (platform) platform->driver->ops->hw_params(substream, params); //dma (platform) snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); prtd->params->ch = prtd->params->ops->request(prtd->params->channel, &dma_info); int soc_pcm_prepare(struct snd_pcm_substream *substream) rtd->dai_link->ops->prepare(substream); platform->driver->ops->prepare(substream); codec_dai->driver->ops->prepare(substream, codec_dai); cpu_dai->driver->ops->prepare(substream, cpu_dai); snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) codec_dai->driver->ops->trigger(substream, cmd, codec_dai); platform->driver->ops->trigger(substream, cmd); cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); /**********************************************************************************************/ 1. platform 相关 1.1 dma static struct snd_pcm_ops dma_ops = { .open = dma_open, .close = dma_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = dma_hw_params, .hw_free = dma_hw_free, .prepare = dma_prepare, .trigger = dma_trigger, .pointer = dma_pointer, .mmap = dma_mmap, }; static struct snd_soc_platform_driver samsung_asoc_platform = { .ops = &dma_ops, .pcm_new = dma_new, .pcm_free = dma_free_dma_buffers, };
1.2 iis static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = { .trigger = s3c24xx_i2s_trigger, .hw_params = s3c24xx_i2s_hw_params, .set_fmt = s3c24xx_i2s_set_fmt, .set_clkdiv = s3c24xx_i2s_set_clkdiv, .set_sysclk = s3c24xx_i2s_set_sysclk, };
static struct snd_soc_dai_driver s3c24xx_i2s_dai = { .probe = s3c24xx_i2s_probe, .suspend = s3c24xx_i2s_suspend, .resume = s3c24xx_i2s_resume, .playback = { .channels_min = 2, .channels_max = 2, .rates = S3C24XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 2, .channels_max = 2, .rates = S3C24XX_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,}, .ops = &s3c24xx_i2s_dai_ops, };
2. codec 相关 static struct snd_soc_codec_driver soc_codec_dev_uda134x = { .probe = uda134x_soc_probe, .remove = uda134x_soc_remove, .suspend = uda134x_soc_suspend, .resume = uda134x_soc_resume, .reg_cache_size = sizeof(uda134x_reg), .reg_word_size = sizeof(u8), .reg_cache_default = uda134x_reg, .reg_cache_step = 1, .read = uda134x_read_reg_cache, .write = uda134x_write, .set_bias_level = uda134x_set_bias_level, };
static const struct snd_soc_dai_ops uda134x_dai_ops = { .startup = uda134x_startup, .shutdown = uda134x_shutdown, .hw_params = uda134x_hw_params, .digital_mute = uda134x_mute, .set_sysclk = uda134x_set_dai_sysclk, .set_fmt = uda134x_set_dai_fmt, }; static struct snd_soc_dai_driver uda134x_dai = { .name = "uda134x-hifi", /* playback capabilities */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = UDA134X_RATES, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda134x_dai_ops, };
3. machine 相关 static struct uda134x_platform_data s3c24xx_uda134x = { .l3 = { .setdat = setdat, .setclk = setclk, .setmode = setmode, .data_hold = 1, .data_setup = 1, .clock_high = 1, .mode_hold = 1, .mode = 1, .mode_setup = 1, }, }; static struct snd_soc_ops s3c24xx_uda134x_ops = { .startup = s3c24xx_uda134x_startup, .shutdown = s3c24xx_uda134x_shutdown, .hw_params = s3c24xx_uda134x_hw_params, }; static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = { .name = "UDA134X", .stream_name = "UDA134X", .codec_name = "uda134x-codec", .codec_dai_name = "uda134x-hifi", .cpu_dai_name = "s3c24xx-iis", .ops = &s3c24xx_uda134x_ops, .platform_name = "samsung-audio", }; static struct snd_soc_card snd_soc_s3c24xx_uda134x = { .name = "S3C24XX_UDA134X", .owner = THIS_MODULE, .dai_link = &s3c24xx_uda134x_dai_link, .num_links = 1, }; /**********************************************************************************************/
1. 创建架构以及生成节点 2. 初始化时 注册音频的中断 硬件设置 申请一块buffer,涉及dma。将映射的物理地址写到dma寄存器.首地址以及大小写入寄存器. 写dma寄存器只涉及一次 dma_pool_alloc(); //大小为aver311_cxt->audio_info.audio_buf_size,映射出物理地址和虚拟地址 3. 在中断处理函数 产生中断后,通知处理音频数据的tasklet任务 void board_alsa_recv_data(void *board_alas_cxt, unsigned char *buffer, U32_T size) board_alsa_recv_data(aver311_cxt->audio_info.audio_cb_cxt, (U8_T *)aver311_cxt->audio_info.dma_buffer[index]->vaddr, aver311_cxt->audio_info.audio_buf_size); 中断到来后,dma寄存器有值,此时对应映射的虚拟地址也有数据,并处理 alsa_model_feed_data(board_alsa->alsa_handle, buffer, size); { substream = alsa_cxt->substream; runtime = alsa_cxt->substream->runtime; stride = runtime->frame_bits >> 3; //runtime->frame_bits = 32, stride = 4 oldptr = alsa_cxt->hwptr; copy_frames = length / stride; if (oldptr + copy_frames >= runtime->buffer_size) { cnt = runtime->buffer_size - oldptr; memcpy(runtime->dma_area + oldptr * stride, buf, cnt * stride); memcpy(runtime->dma_area, buf + cnt * stride, copy_frames * stride - cnt * stride); } else { //runtime->dma_area大小是buffer_size,在hw_param()函数里进行申请 memcpy(runtime->dma_area + oldptr * stride, buf, copy_frames * stride); //将音频数据拷贝到dma_area }
snd_pcm_stream_lock(substream); alsa_cxt->hwptr += copy_frames; //更新hwptr指针 if (alsa_cxt->hwptr >= runtime->buffer_size) alsa_cxt->hwptr -= runtime->buffer_size;
alsa_cxt->capture_transfer_done += copy_frames; //capture_transfer_done记录处理的数据量有没有大于period_size if (alsa_cxt->capture_transfer_done >= runtime->period_size) { alsa_cxt->capture_transfer_done -= runtime->period_size; period_elapsed = true; } snd_pcm_stream_unlock(substream);
if (period_elapsed) snd_pcm_period_elapsed(substream); //推送给用户空间 }
四. 音频控制分析 控制音量等 int uda134x_soc_probe(struct snd_soc_codec *codec) uda134x_reset(codec); snd_soc_add_codec_controls(codec, uda1340_snd_controls, ARRAY_SIZE(uda1340_snd_controls)); snd_soc_add_controls(card, codec->dev, controls, num_controls, codec->name_prefix, codec); for (i = 0; i < num_controls; i++) { const struct snd_kcontrol_new *control = &controls[i]; err = snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix)); }
五. 问题点 ????? 数据的传输过程 control DAPM (动态音频电源管理) 调试方式 用户态 tinyalsa, alsa-lib, android audio framework snd_pcm_period_elapsed() 具体作用? pointer通常在buffer-update 过程中调用,由中断函数中的snd_pcm_period_elapsed触发。即每次硬件中断,就会调用snd_pcm_period_elapsed函数来通知alsa-core来读取当前的hardware position,计算buffer中空余空间,唤醒sleep的polling thread. 1. 未播放视频时为何有音频数据? 2. 为何需要多个period, 只使用1个? 3. runtime->dma_area 作用? 4. 杂音? 6. 音频卡顿? alsa_cxt->hwptr 数据处理问题 正常时声音间隔 0.04s 卡顿时声音间隔 0.06s
************* snd_pcm_lib_malloc_pages() 返回-22,error 替换 snd_pcm_lib_alloc_vmalloc_buffer() 返回0,ok? 解决方法:必须设置申请内存类型. 比如 SNDRV_DMA_TYPE_DEV snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024); snd_pcm_lib_preallocate_pages();
cn311h: //设置 struct snd_pcm_runtime 结构体 .open = aaci_pcm_open, struct snd_pcm_runtime *runtime = substream->runtime; runtime->private_data = aacirun; runtime->hw = aaci_hw_info; /* enable DMA bits */ .close = aaci_pcm_close, .ioctl = snd_pcm_lib_ioctl, //申请dmabuffer, buffer_size .hw_params = aaci_pcm_hw_params, runtime->dma_buffer_p = bufp; runtime->dma_area = bufp->area; runtime->dma_addr = bufp->addr; runtime->dma_bytes = bufp->bytes; //snd_pcm_lib_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL); runtime->dma_bytes = size; snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); .hw_free = aaci_pcm_hw_free,
//设置私有变量的值 .prepare = aaci_pcm_capture_prepare, .trigger = aaci_pcm_capture_trigger, .pointer = aaci_pcm_pointer,
