snd pcm open打开流程
发布日期:2021-09-29 04:02:19
浏览次数:23
分类:技术文章
本文共 13313 字,大约阅读时间需要 44 分钟。
分享一下我老师大神的人工智能教程!零基础,通俗易懂!
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
aplay.c ==> main ==> snd_pcm_open(&handle, pcm_name, stream, open_mode); // 打开一路pcm,刷新config配置 如果是"default",同时type等于SND_CONFIG_TYPE_COMPOUND那么这里对应"empty" static const char *const build_in_pcms[] = { "adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat", "linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share", "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul", NULL }; _snd_pcm_empty_open和snd_pcm_open_named_slave ==> snd_pcm_open_conf(pcmp, name, root, conf, stream, mode); ==> open_func = snd_dlobj_cache_lookup(open_name);将获得lib库中_snd_pcm_empty_open函数 所以open_func将等于_snd_pcm_empty_open _snd_pcm_empty_open _snd_pcm_asym_open _snd_pcm_plug_open _snd_pcm_softvol_open _snd_pcm_dmix_open _snd_pcm_hw_open ==> snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode | (nonblock ? SND_PCM_NONBLOCK : 0), 0, sync_ptr_ioctl); ==> snd_ctl_hw_open filename等于"/dev/snd/controlC0" ==> snd_open_device(filename, fmode); ctl->ops = &snd_ctl_hw_ops; ctl->private_data = hw; ctl->poll_fd = fd; *handle = ctl; filename等于"/dev/snd/pcmC0D0p" ==> fd = snd_open_device(filename, fmode); ==> return snd_pcm_hw_open_fd(pcmp, name, fd, 0, sync_ptr_ioctl); ==> snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode); pcm->ops = &snd_pcm_hw_ops; pcm->fast_ops = &snd_pcm_hw_fast_ops; static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; void *ptr; int err; if (hw->sync_ptr == NULL) { // 如果还没有mmap,那么执行mmap映射内核空间驱动使用的声音缓冲区 ptr = mmap(NULL, page_align(sizeof(struct sndrv_pcm_mmap_control)), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); if (ptr == MAP_FAILED || ptr == NULL) { err = -errno; SYSMSG("control mmap failed"); return err; } hw->mmap_control = ptr; // 声卡驱动头部填充了一个结构体sndrv_pcm_mmap_control,类似qvfb显示原理. // struct sndrv_pcm_mmap_control { // sndrv_pcm_uframes_t appl_ptr; /* RW: appl ptr (0...boundary-1) */ // sndrv_pcm_uframes_t avail_min; /* RW: min available frames for wakeup */ // }; } else { hw->mmap_control->avail_min = 1; } snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); return 0; } snd_pcm_mmap switch (i->type) { case SND_PCM_AREA_MMAP: // 表示为数据区分配驱动内存,在snd_pcm_hw_channel_info中设置了type ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset); /* mmap ==> snd_pcm_mmap_data ==> snd_pcm_default_mmap // mmap the DMA buffer on RAM static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_ops = &snd_pcm_vm_ops_data; // vma操作函数,当应用程序向该area读写不存在的内存数据时, area->vm_private_data = substream; // 将执行snd_pcm_vm_ops_data中的fault // 函数snd_pcm_mmap_data_fault进一步以页为单位申请内存空间,所以如果用户程序需要64k,那么将执行16次,每次申请4k空间[luther.gliethttp]. area->vm_flags |= VM_RESERVED; atomic_inc(&substream->mmap_count); return 0; } */ if (ptr == MAP_FAILED) { SYSERR("mmap failed"); return -errno; } i->addr = ptr; ==> snd_pcm_mmap_control static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { struct snd_pcm_runtime *runtime; long size; if (!(area->vm_flags & VM_READ)) return -EINVAL; runtime = substream->runtime; size = area->vm_end - area->vm_start; if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))) return -EINVAL; area->vm_ops = &snd_pcm_vm_ops_control; // 当对( area->vm_start,area->vm_end)之间空间操作,发生 area->vm_private_data = substream; // 缺页时,内核将调用该vm_ops方法来处理fault异常, area->vm_flags |= VM_RESERVED; // 进而执行snd_pcm_mmap_control_fault申请1个page空间 return 0; } ==> writei_func = snd_pcm_writei; ==> playback(argv[optind++]); ==> playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, name); ==> pcm_write(audiobuf, l); ==> writei_func(handle, data, count);就是调用上面的snd_pcm_writei ==> snd_pcm_writei ==> _snd_pcm_writei ==> pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size); ==> snd_pcm_plugin_writei ==> snd_pcm_write_areas(pcm, areas, 0, size, snd_pcm_plugin_write_areas); ==> avail = snd_pcm_avail_update(pcm); // 获取可用缓冲区位置偏移索引值 ==> func()就是snd_pcm_plugin_write_areas函数发送1024帧音频数据,一帧对应一次完整采样,比如stereo立体声 ,24bits量化,那么这里一帧对应3*2字节数据,即一次完整采样所需空间[luther.gliethttp]. ==> plugin->write(pcm, areas, offset, frames, slave_areas, slave_offset, &slave_frames); 即调用snd_pcm_linear_write_areas函数将areas中的frames频数据拷贝到slave_areas内存区 ==> pcm->fast_ops->mmap_commit(pcm->fast_op_arg, offset, frames); ==> snd_pcm_dmix_mmap_commit ==> snd_pcm_dmix_sync_area /* * synchronize shm ring buffer with hardware */ static void snd_pcm_dmix_sync_area(snd_pcm_t *pcm) ==> /* add sample areas here */ src_areas = snd_pcm_mmap_areas(pcm); dst_areas = snd_pcm_mmap_areas(dmix->spcm); // 添加 ==> mix_areas(dmix, src_areas, dst_areas, appl_ptr, slave_appl_ptr, transfer); if (dmix->interleaved) { // 可以将缓冲中的音频数据填充到硬件中[luther.gliethttp] /* * process all areas in one loop * it optimizes the memory accesses for this case */ do_mix_areas(size * channels, (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels, (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels, dmix->u.dmix.sum_buffer + dst_ofs * channels, sample_size, sample_size, sizeof(signed int)); return; } ==> do_mix_areas(size * channels, (unsigned char *)dst_areas[0].addr + sample_size * dst_ofs * channels, (unsigned char *)src_areas[0].addr + sample_size * src_ofs * channels, dmix->u.dmix.sum_buffer + dst_ofs * channels, sample_size, sample_size, sizeof(signed int)); 这里的do_mix_areas在i386中,使用下面完全用汇编实现的拷贝函数MIX_AREAS_32完成数据从src到dst的快速拷贝, 每拷贝一次,声卡就会发出一点声音[luther.gliethttp] /* * for plain i386, 32-bit version (24-bit resolution) */ static void MIX_AREAS_32(unsigned int size, volatile signed int *dst, signed int *src, volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) _snd_pcm_asym_open _snd_pcm_dmix_open snd_pcm_plugin_avail_update ==> snd_pcm_avail_update(slave); ==> pcm->fast_ops->avail_update(pcm->fast_op_arg); ==> snd_pcm_dmix_avail_update ==> snd_pcm_mmap_playback_avail(pcm); alsa_sound_init #define CONFIG_SND_MAJOR 116 /* standard configuration */ static int major = CONFIG_SND_MAJOR; module_init(alsa_sound_init) alsa_sound_init ==> register_chrdev(major, "alsa", &snd_fops) // 主设备号为116的所有设备都为alsa设备,节点方法集为snd_fops static const struct file_operations snd_fops = // alsa的设备名为pcmC0D1c或pcmC0D1p等[luther.gliethttp]. { .owner = THIS_MODULE, .open = snd_open }; snd_open ==> __snd_open(inode, file); ==> __snd_open unsigned int minor = iminor(inode); mptr = snd_minors[minor]; file->f_op = fops_get(mptr->f_ops); file->f_op->open(inode, file); const struct file_operations snd_pcm_f_ops[2] = { { // alsa使用到的SNDRV_PCM_STREAM_PLAYBACK放音方法集[luther.gliethttp] .owner = THIS_MODULE, .write = snd_pcm_write, .aio_write = snd_pcm_aio_write, .open = snd_pcm_playback_open, .release = snd_pcm_release, .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 = dummy_get_unmapped_area, }, { // alsa使用到的SNDRV_PCM_STREAM_CAPTURE录音方法集 .owner = THIS_MODULE, .read = snd_pcm_read, .aio_read = snd_pcm_aio_read, .open = snd_pcm_capture_open, .release = snd_pcm_release, .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 = dummy_get_unmapped_area, } }; ========================================================================= snd_intel8x0_probe ==> snd_intel8x0_create ==> request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_SHARED, card->shortname, chip) snd_intel8x0_interrupt snd_intel8x0_update snd_open ==> snd_pcm_playback_open ==> snd_pcm_open ==> snd_pcm_open_file ==> snd_pcm_open_substream ==> substream->ops->open(substream)即snd_intel8x0_playback_ops.open ==> snd_intel8x0_playback_open ==> snd_intel8x0_pcm_open static int snd_intel8x0_pcm_open(struct snd_pcm_substream *substream, struct ichdev *ichdev) { struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int err; ichdev->substream = substream; runtime->hw = snd_intel8x0_stream; // 声卡配置硬件信息[luther.gliethttp] runtime->hw.rates = ichdev->pcm->rates; snd_pcm_limit_hw_rates(runtime); if (chip->device_type == DEVICE_SIS) { runtime->hw.buffer_bytes_max = 64*1024; runtime->hw.period_bytes_max = 64*1024; } if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; runtime->private_data = ichdev; return 0; } ioctl(SNDRV_PCM_IOCTL_HW_PARAMS) ==> snd_pcm_f_ops.unlocked_ioctl即:snd_pcm_playback_ioctl ==> snd_pcm_playback_ioctl ==> snd_pcm_playback_ioctl1 ==> snd_pcm_common_ioctl1 case SNDRV_PCM_IOCTL_HW_PARAMS: return snd_pcm_hw_params_user(substream, arg); ==> snd_pcm_hw_params_user ==> snd_pcm_hw_params ==> substream->ops->hw_params即snd_intel8x0_playback_ops.hw_params ==> snd_intel8x0_hw_params ==> snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params), params_channels(hw_params), ichdev->pcm->r[dbl].slots); ioctl(SNDRV_PCM_IOCTL_PREPARE) ==> snd_pcm_playback_ioctl ==> snd_pcm_playback_ioctl1 ==> snd_pcm_common_ioctl1 ==> snd_pcm_prepare // prepare the PCM substream to be triggerable ==> snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, f_flags); ==> snd_pcm_action_single(ops, substream, state); ops->pre_action(substream, state); ops->do_action(substream, state); ops->post_action(substream, state); 上面ops就是之前提到的snd_pcm_action_prepare ==> snd_pcm_do_prepare调用snd_pcm_do_reset(substream, 0);复位 substream->ops->prepare(substream);即snd_intel8x0_playback_ops.prepare ==> snd_intel8x0_pcm_prepare static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream) { struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct ichdev *ichdev = get_ichdev(substream); ichdev->physbuf = runtime->dma_addr; // dma缓冲区地址 ichdev->size = snd_pcm_lib_buffer_bytes(substream); // 将帧缓冲大小转为字节空间大小[luther.gliethttp] ichdev->fragsize = snd_pcm_lib_period_bytes(substream); if (ichdev->ichd == ICHD_PCMOUT) { snd_intel8x0_setup_pcm_out(chip, runtime); // 为play模式设置ac97寄存器[luther.gliethttp] if (chip->device_type == DEVICE_INTEL_ICH4) ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; } snd_intel8x0_setup_periods(chip, ichdev); // 设置PCI总线ac97的bank地址空间[luther.gliethttp] return 0; } ==> snd_intel8x0_setup_pcm_out static void snd_intel8x0_setup_pcm_out(struct intel8x0 *chip, struct snd_pcm_runtime *runtime) { unsigned int cnt; int dbl = runtime->rate > 48000; // 一共有如下几种设备:enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE }; spin_lock_irq(&chip->reg_lock); switch (chip->device_type) { case DEVICE_ALI: cnt = igetdword(chip, ICHREG(ALI_SCR)); cnt &= ~ICH_ALI_SC_PCM_246_MASK; if (runtime->channels == 4 || dbl) cnt |= ICH_ALI_SC_PCM_4; else if (runtime->channels == 6) cnt |= ICH_ALI_SC_PCM_6; iputdword(chip, ICHREG(ALI_SCR), cnt); break; case DEVICE_SIS: cnt = igetdword(chip, ICHREG(GLOB_CNT)); cnt &= ~ICH_SIS_PCM_246_MASK; if (runtime->channels == 4 || dbl) cnt |= ICH_SIS_PCM_4; else if (runtime->channels == 6) cnt |= ICH_SIS_PCM_6; iputdword(chip, ICHREG(GLOB_CNT), cnt); break; default: cnt = igetdword(chip, ICHREG(GLOB_CNT)); cnt &= ~(ICH_PCM_246_MASK | ICH_PCM_20BIT); if (runtime->channels == 4 || dbl) cnt |= ICH_PCM_4; else if (runtime->channels == 6) cnt |= ICH_PCM_6; else if (runtime->channels == 8) cnt |= ICH_PCM_8; if (chip->device_type == DEVICE_NFORCE) { /* reset to 2ch once to keep the 6 channel data in alignment, * to start from Front Left always */ if (cnt & ICH_PCM_246_MASK) { iputdword(chip, ICHREG(GLOB_CNT), cnt & ~ICH_PCM_246_MASK); spin_unlock_irq(&chip->reg_lock); msleep(50); /* grrr... */ spin_lock_irq(&chip->reg_lock); } } else if (chip->device_type == DEVICE_INTEL_ICH4) { if (runtime->sample_bits > 16) cnt |= ICH_PCM_20BIT; } iputdword(chip, ICHREG(GLOB_CNT), cnt); break; } spin_unlock_irq(&chip->reg_lock); } ioctl(SNDRV_PCM_IOCTL_START) ==> snd_pcm_playback_ioctl ==> snd_pcm_playback_ioctl1 ==> snd_pcm_common_ioctl1 ==> snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); ==> snd_pcm_action_single // state等于SNDRV_PCM_STATE_RUNNING static struct action_ops snd_pcm_action_start = { .pre_action = snd_pcm_pre_start, .do_action = snd_pcm_do_start, .undo_action = snd_pcm_undo_start, .post_action = snd_pcm_post_start }; ops->pre_action(substream, state); ops->do_action(substream, state); ops->post_action(substream, state); 上面ops就是之前提到的snd_pcm_action_start ==> snd_pcm_do_start ==> substream->ops->trigger(substream, SNDRV_PCM_TRIGGER_START);即snd_intel8x0_playback_ops.trigger ==> snd_intel8x0_pcm_trigger启动ac97数据传输 以上都只是执行一次[luther.gliethttp] 只要发送音频数据,就会执行该ioctl更新pointer ioctl(SNDRV_PCM_IOCTL_HWSYNC) ==> snd_pcm_playback_ioctl ==> snd_pcm_playback_ioctl1 ==> snd_pcm_common_ioctl1 ==> snd_pcm_hwsync case SNDRV_PCM_STATE_RUNNING: if ((err = snd_pcm_update_hw_ptr(substream)) < 0) break; ==> snd_pcm_update_hw_ptr ==> snd_pcm_update_hw_ptr_post ==> snd_pcm_update_hw_ptr_pos ==> substream->ops->pointer(substream);即snd_intel8x0_playback_ops.pointer ==> snd_intel8x0_pcm_pointer // 更新dma缓冲区数据最后可用数据索引值[luther.gliethttp]
给我老师的人工智能教程打call!
转载地址:https://blog.csdn.net/hddghhfd/article/details/84024306 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
不错!
[***.144.177.141]2024年02月29日 05时44分52秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
oracle错误1327,Oracle中的PGA监控报警分析(r11笔记第97天)
2019-04-21
php函数内的循环,PHP 循环列出目录内容的函数代码
2019-04-21
oracle树状排序,Oracle树状结构查询
2019-04-21
深度linux内核升级,深度操作系统 2020.11.11 更新发布:内核升级
2019-04-21
sql 拆解函数_SQL入门50题详解(含知识点讲解及代码运行步骤拆解)
2019-04-21
java和python交互 jni_Python基于pyjnius库实现访问java类
2019-04-21
mysql表名长度_JavaWeb之MySQL(一)
2019-04-21
mysql服务器语法_Mysql语法
2019-04-21
python双击py一闪_Python脚本在双击.py时无法正常运行
2019-04-21
redis logfile为空_关于Redis(二)
2019-04-21
git更换_git命令
2019-04-21
hp-ux 查看系统负载_Linux性能调优 | 平均负载的理解和分析
2019-04-21
elementui的tree组件页面显示不出数据_vue路由及组件
2019-04-21