Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15531225
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2009-11-16 16:47:48

snd_pcm_oss_f_reg
==> snd_pcm_oss_write
==> snd_pcm_oss_write1
==> snd_pcm_oss_make_ready
==> snd_pcm_oss_change_params
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
{
    struct snd_pcm_runtime *runtime = substream->runtime;
    struct snd_pcm_hw_params *params, *sparams;
    struct snd_pcm_sw_params *sw_params;
    ssize_t oss_buffer_size, oss_period_size; // oss_period_size采样次数
    size_t oss_frame_size; // 一次采样所有声道所需存储空间字节数,所以oss_frame_size * oss_period_size
    int err;               // 就等于oss_period_size次采样所需存储空间大小[luther.gliethttp]
    int direct;
    int format, sformat, n;
    struct snd_mask sformat_mask;
    struct snd_mask mask;

    if (mutex_lock_interruptible(&runtime->oss.params_lock))
        return -EINTR;
    sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
    params = kmalloc(sizeof(*params), GFP_KERNEL);
    sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
    if (!sw_params || !params || !sparams) {
        snd_printd("No memory\n");
        err = -ENOMEM;
        goto failure;
    }

    if (atomic_read(&substream->mmap_count))
        direct = 1;
    else
        direct = substream->oss.setup.direct;

    _snd_pcm_hw_params_any(sparams);    // mask->bits[]中内容全部置1
    _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS); // 设置integer类型
    _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0);
    // 将mask->bits[0]的bit0和bit1清0,这样最小值就是2了[luther.gliethttp]
    snd_mask_none(&mask);   // 将mask->bits[]全部清0
    if (atomic_read(&substream->mmap_count))
        snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); // 设置SNDRV_PCM_ACCESS_MMAP_INTERLEAVED对应的mask->bits[]位有效
    else {
        snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); // 设置对SNDRV_PCM_ACCESS_RW_INTERLEAVED应的mask->bits[]位有效
        if (!direct)
            snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED);
    }
    // 因为SNDRV_PCM_HW_PARAM_ACCESS对应的mask->bits[]中内容全部置1,
    // 这里mask->bits[]中可能只有第SNDRV_PCM_ACCESS_RW_INTERLEAVED位被置1,
    // 所以snd_pcm_hw_param_mask函数将access_mask->bits[] &= mask->bits[];
    // 结果可能只剩下第SNDRV_PCM_ACCESS_RW_INTERLEAVED位被置1.[luther.gliethttp]
    // 总之就是为了进一步具体细分,我们确实设置了的mask->bits[],我们没有设置的bit们,将
    // 因为数值为0,而直接在sparams对应的mask->bits[]中被忽略掉,这和防火墙的设置类似,
    // 首先关闭所有端口访问,然后打开你需要的几个端口就全部ok了[luther.gliethttp].
    err = snd_pcm_hw_param_mask(substream, sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); 《浅析alsa声卡驱动snd_pcm_hw_param_mask函数》
    if (err < 0) {
        snd_printd("No usable accesses\n");
        err = -EINVAL;
        goto failure;
    }
    // 在snd_pcm_oss_open
    // ==> snd_pcm_oss_open_file
    // ==> snd_pcm_oss_init_substream
    // ==> runtime->oss.rate = 8000;
    choose_rate(substream, sparams, runtime->oss.rate); 《浅析alsa声卡驱动choose_rate函数》
    // snd_pcm_hw_param_near函数是将一个'(''[' (min,max ']'')'range类型的范围值约束为
    // 一个只有1个值的范围值的,等效于integer整型值的唯一方法[luther.gliethttp]
    // 所以下面就是将var为SNDRV_PCM_HW_PARAM_CHANNELS的数值设置为
    // [runtime->oss.channels, runtime->oss.channels]这样一个闭区间[luther.gliethttp]
    snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, NULL); 《浅析alsa声卡驱动snd_pcm_hw_param_near函数
    format = snd_pcm_oss_format_from(runtime->oss.format); // 将AFMT_开头的oss.format数据转换为mask对应的以SNDRV_PCM_FORMAT_开头的格式索引值

    sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT);
    if (direct)     // 如果是通过mmap直接控制,那么sformat直接等于format
        sformat = format;
    else            // 否则没有通过用户mmap直接控制音频发送区
        sformat = snd_pcm_plug_slave_format(format, &sformat_mask); 《浅析alsa声卡驱动snd_pcm_plug_slave_format函数》
    if (sformat < 0 || !snd_mask_test(&sformat_mask, sformat)) {
    // 如果仍然没有符合要求的格式或者direct等于1时,没有检查sformat情况,
    // 这里snd_mask_test检查又失败,那么执行如下应急操作[luther.gliethttp]
        for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) {
            if (snd_mask_test(&sformat_mask, sformat) &&
                snd_pcm_oss_format_to(sformat) >= 0)
                break;  // 首先sformat_mask需要支持,其次sformat确实存在有效.
        }
        if (sformat > SNDRV_PCM_FORMAT_LAST) {
            snd_printd("Cannot find a format!!!\n");
            err = -EINVAL;
            goto failure;
        }
    // ok, 补救措施生效, 感觉这种补救没有什么意义, 发生错误时,比如声音不正常时,更会让人感到莫名其妙
    }
    // 设置sparams的format索引值对应的mask->bits[]位参数[luther.gliethttp]
    err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0);
    if (err < 0)
        goto failure;

    if (direct) {
    // 如果是mmap等直驱方式,那么params参数使用sparams的参数设置
        memcpy(params, sparams, sizeof(*params));
    } else {
    // 不是mmap等直驱方式,那么独立设置params参数空间[luther.gliethttp]
        _snd_pcm_hw_params_any(params);
        _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
                      SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); // 访问模式为读写交叉
        _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT, // 使用runtime->oss.format的音频格式
                      snd_pcm_oss_format_from(runtime->oss.format), 0);
        _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
                      runtime->oss.channels, 0); // 设置音频通道数目为runtime->oss.channels
        _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
                      runtime->oss.rate, 0); // 设置音频采样率为runtime->oss.rate
        // 从如下的log信息来看params对应的是client端
        // sparams对应的是slave端[luther.gliethttp]
        pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n",
             params_access(params), params_format(params),
             params_channels(params), params_rate(params));
    }
    pdprintf("slave: access = %i, format = %i, channels = %i, rate = %i\n",
         params_access(sparams), params_format(sparams),
         params_channels(sparams), params_rate(sparams));

    oss_frame_size = snd_pcm_format_physical_width(params_format(params)) * // 当前format对应的一个声道一次采样数据bits位宽度
             params_channels(params) / 8; // 所以oss_frame_size就所以一次采样所有声道所需存储空间字节数.

#ifdef CONFIG_SND_PCM_OSS_PLUGINS
    snd_pcm_oss_plugin_clear(substream); // 清空oss.plugin_first链表[luther.gliethttp] 《浅析alsa声卡驱动snd_pcm_oss_plugin_clear函数》
    if (!direct) {
        // 非直驱方式音频流
        /* add necessary plugins */
        snd_pcm_oss_plugin_clear(substream); // my god,又清一次 《浅析alsa声卡驱动snd_pcm_oss_plugin_clear函数》
        if ((err = snd_pcm_plug_format_plugins(substream, // 建立所需的所有格式转换plugin插件[luther.gliethttp] 《浅析alsa声卡驱动snd_pcm_plug_format_plugins函数》
                               params,
                               sparams)) < 0) {
            snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err);
            snd_pcm_oss_plugin_clear(substream); 《浅析alsa声卡驱动snd_pcm_oss_plugin_clear函数》
            goto failure;
        }
        if (runtime->oss.plugin_first) { // 如果plugin_first非空,说明在snd_pcm_plug_format_plugins函数中建立了格式转换plugin插件
            struct snd_pcm_plugin *plugin;
        // 内部调用snd_pcm_plugin_build建立io音频数据放音或录音plugin插件,回调处理函数为io_playback_transfer或者io_capture_transfer
            if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
                snd_printd("snd_pcm_plugin_build_io failed: %i\n", err);
                snd_pcm_oss_plugin_clear(substream); 《浅析alsa声卡驱动snd_pcm_oss_plugin_clear函数》
                goto failure;
            }
            if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                err = snd_pcm_plugin_append(plugin); // 如果是建立放音io音频数据plugin插件,那么将plugin插件追加到plugin链表末尾[luther.gliethttp] 《浅析alsa声卡驱动snd_pcm_plugin_append函数》
            } else {
                err = snd_pcm_plugin_insert(plugin); // 如果是建立录音io音频数据plugin插件,那么将plugin插件追加到plugin链表头部[luther.gliethttp] 《浅析alsa声卡驱动snd_pcm_plugin_insert函数》
            }
            if (err < 0) {
                snd_pcm_oss_plugin_clear(substream); 《浅析alsa声卡驱动snd_pcm_oss_plugin_clear函数》
                goto failure;
            }
        }
    }
#endif

/*
函数snd_pcm_oss_period_size最终将建立如下一个视图:[luther.gliethttp]
struct a { char one_period[runtime->oss.period_bytes] };
a[runtime->oss.periods]
runtime->oss.period_frames为当前可用a[]数组索引号+1,
语言描述的话,一共有periods个重复物理地址上连续的存储空间,每个存储空间的大小为period_bytes
*/
    err = snd_pcm_oss_period_size(substream, params, sparams); 《浅析alsa声卡驱动snd_pcm_oss_period_size函数》
    if (err < 0)
        goto failure;
// oss_frame_size为1次采样所有声道所需存储空间字节数[luther.gliethttp]
// 所以runtime->oss.period_bytes / oss_frame_size;就表示a[]一个数组项能够存储
// 多少个采样数据[luther.gliethttp]
    n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);
// 将一个period_bytes存储空间能够存储的采样数据个数n设置到SNDRV_PCM_HW_PARAM_PERIOD_SIZE参数中
    err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL);
    if (err < 0)
        goto failure;
// 设置runtime->oss.periods到SNDRV_PCM_HW_PARAM_PERIODS参数空间[luther.gliethttp]
    err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,
                     runtime->oss.periods, NULL);
    if (err < 0)
        goto failure;
// 在内核中发起系统调用,执行本应用户空间发起调用的fops函数集,完成参数设置任务[luther.gliethttp]
    snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); 《浅析alsa声卡驱动snd_pcm_kernel_ioctl函数》

    if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, sparams)) < 0) {
        snd_printd("HW_PARAMS failed: %i\n", err);
        goto failure;
    }

    memset(sw_params, 0, sizeof(*sw_params));
    if (runtime->oss.trigger) { // 在snd_pcm_oss_init_substream中默认使能该位
        sw_params->start_threshold = 1;
    } else {
        sw_params->start_threshold = runtime->boundary;
    }
    if (atomic_read(&substream->mmap_count) ||
        substream->stream == SNDRV_PCM_STREAM_CAPTURE) // 录音
        sw_params->stop_threshold = runtime->boundary;
    else
        sw_params->stop_threshold = runtime->buffer_size;
    sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
    sw_params->period_step = 1;
    sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
        1 : runtime->period_size;
    // 如果放音那么有1个数据就开始放,如果录音,那么只有上面填充满runtime->period_size字节后,才会启动start_threshold工作[luther.gliethttp]
    if (atomic_read(&substream->mmap_count) ||
        substream->oss.setup.nosilence) {
        sw_params->silence_threshold = 0;
        sw_params->silence_size = 0;
    } else {
        snd_pcm_uframes_t frames;
        frames = runtime->period_size + 16;
        if (frames > runtime->buffer_size)
            frames = runtime->buffer_size;
        sw_params->silence_threshold = frames;
        sw_params->silence_size = frames;
    }
// 在内核中发起系统调用,执行本应用户空间发起调用的fops函数集,完成参数设置任务[luther.gliethttp]
    if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
        snd_printd("SW_PARAMS failed: %i\n", err);
        goto failure;
    }

// periods的理解见上面
    runtime->oss.periods = params_periods(sparams);
// oss_period_size就是采样次数[luther.gliethttp]
    oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams));
    if (oss_period_size < 0) {
        err = -EINVAL;
        goto failure;
    }
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
    if (runtime->oss.plugin_first) {
// 如果runtime->oss存在plugin音频插件处理需求,
// 那么为每个插件调用snd_pcm_plugin_alloc(plugin, frames);申请oss_period_size次采样所需的存储空间.
        err = snd_pcm_plug_alloc(substream, oss_period_size); 《浅析alsa声卡驱动snd_pcm_plugin_alloc函数》
        if (err < 0)
            goto failure;
    }
#endif
    oss_period_size *= oss_frame_size; // oss_period_size乘完之后就是oss_frame_size次采样所需存储空间大小

// oss_period_size为a[]数组一项的存储空间大小,
// runtime->oss.periods为数组包含项数,相乘之后为该periods个项一共所需存储空间大小[luther.gliethttp]
    oss_buffer_size = oss_period_size * runtime->oss.periods;
    if (oss_buffer_size < 0) {
        err = -EINVAL;
        goto failure;
    }

    runtime->oss.period_bytes = oss_period_size; // 一个period数据存储空间占用字节数
    runtime->oss.buffer_bytes = oss_buffer_size; // periods个数据存储空间一共占用字节数

    pdprintf("oss: period bytes = %i, buffer bytes = %i\n",
         runtime->oss.period_bytes,
         runtime->oss.buffer_bytes);
    pdprintf("slave: period_size = %i, buffer_size = %i\n",
         params_period_size(sparams),
         params_buffer_size(sparams));

// 读取上面最终设置成的参数值[luther.gliethttp]
    runtime->oss.format = snd_pcm_oss_format_to(params_format(params));
    runtime->oss.channels = params_channels(params);
    runtime->oss.rate = params_rate(params);

    runtime->oss.params = 0;
    runtime->oss.prepare = 1;
    vfree(runtime->oss.buffer); // 不管runtime->oss.buffer申请了没,都将其释放
    runtime->oss.buffer = vmalloc(runtime->oss.period_bytes); // 重新申请一个period所需的内存空间
    runtime->oss.buffer_used = 0;
    if (runtime->dma_area)
        snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));

// oss_frame_size次采样所需存储空间大小为oss_period_size
// 这里用来计算oss_period_size这么大的存储空间,可以被分割成多少个period_frames数组项a[period_frames]
    runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);

    err = 0;
failure:
    kfree(sw_params);
    kfree(params);
    kfree(sparams);
    mutex_unlock(&runtime->oss.params_lock);
    return err;
}
阅读(4623) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~