snd_pcm_oss_f_reg==> snd_pcm_oss_write==> snd_pcm_oss_write1==> snd_pcm_oss_make_ready==> snd_pcm_oss_change_paramsstatic 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;
}