📄 pcm_oss.c
字号:
/* * Digital Audio (PCM) abstract layer / OSS compatible * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#if 0#define PLUGIN_DEBUG#endif#include <sound/driver.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/vmalloc.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/pcm.h>#include <sound/pcm_params.h>#include "pcm_plugin.h"#include <sound/info.h>#include <linux/soundcard.h>#include <sound/initval.h>static int snd_dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};static int snd_adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};static int snd_nonblock_open;MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");MODULE_DESCRIPTION("PCM OSS emulation for ALSA.");MODULE_LICENSE("GPL");MODULE_PARM(snd_dsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");MODULE_PARM_DESC(snd_dsp_map, "PCM device number assigned to 1st OSS device.");MODULE_PARM_SYNTAX(snd_dsp_map, "default:0,skill:advanced");MODULE_PARM(snd_adsp_map, "1-" __MODULE_STRING(SNDRV_CARDS) "i");MODULE_PARM_DESC(snd_adsp_map, "PCM device number assigned to 2nd OSS device.");MODULE_PARM_SYNTAX(snd_adsp_map, "default:1,skill:advanced");MODULE_PARM(snd_nonblock_open, "i");MODULE_PARM_DESC(snd_nonblock_open, "Don't block opening busy PCM devices.");MODULE_PARM_SYNTAX(snd_nonblock_open, "default:0,skill:advanced");extern int snd_mixer_oss_ioctl_card(snd_card_t *card, unsigned int cmd, unsigned long arg);static int snd_pcm_oss_get_rate(snd_pcm_oss_file_t *pcm_oss_file);static int snd_pcm_oss_get_channels(snd_pcm_oss_file_t *pcm_oss_file);static int snd_pcm_oss_get_format(snd_pcm_oss_file_t *pcm_oss_file);EXPORT_NO_SYMBOLS;static inline mm_segment_t snd_enter_user(void){ mm_segment_t fs = get_fs(); set_fs(get_ds()); return fs;}static inline void snd_leave_user(mm_segment_t fs){ set_fs(fs);}static inline void dec_mod_count(struct module *module){ if (module) __MOD_DEC_USE_COUNT(module);}int snd_pcm_oss_plugin_clear(snd_pcm_substream_t *substream){ snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_plugin_t *plugin, *next; plugin = runtime->oss.plugin_first; while (plugin) { next = plugin->next; snd_pcm_plugin_free(plugin); plugin = next; } runtime->oss.plugin_first = runtime->oss.plugin_last = NULL; return 0;}int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin){ snd_pcm_runtime_t *runtime = plugin->plug->runtime; plugin->next = runtime->oss.plugin_first; plugin->prev = NULL; if (runtime->oss.plugin_first) { runtime->oss.plugin_first->prev = plugin; runtime->oss.plugin_first = plugin; } else { runtime->oss.plugin_last = runtime->oss.plugin_first = plugin; } return 0;}int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin){ snd_pcm_runtime_t *runtime = plugin->plug->runtime; plugin->next = NULL; plugin->prev = runtime->oss.plugin_last; if (runtime->oss.plugin_last) { runtime->oss.plugin_last->next = plugin; runtime->oss.plugin_last = plugin; } else { runtime->oss.plugin_last = runtime->oss.plugin_first = plugin; } return 0;}static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames){ snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->period_size == runtime->oss.period_bytes) return frames; if (runtime->period_size < runtime->oss.period_bytes) return frames * (runtime->oss.period_bytes / runtime->period_size); return frames / (runtime->period_size / runtime->oss.period_bytes);}static int snd_pcm_oss_format_from(int format){ switch (format) { case AFMT_MU_LAW: return SNDRV_PCM_FORMAT_MU_LAW; case AFMT_A_LAW: return SNDRV_PCM_FORMAT_A_LAW; case AFMT_IMA_ADPCM: return SNDRV_PCM_FORMAT_IMA_ADPCM; case AFMT_U8: return SNDRV_PCM_FORMAT_U8; case AFMT_S16_LE: return SNDRV_PCM_FORMAT_S16_LE; case AFMT_S16_BE: return SNDRV_PCM_FORMAT_S16_BE; case AFMT_S8: return SNDRV_PCM_FORMAT_S8; case AFMT_U16_LE: return SNDRV_PCM_FORMAT_U16_LE; case AFMT_U16_BE: return SNDRV_PCM_FORMAT_U16_BE; case AFMT_MPEG: return SNDRV_PCM_FORMAT_MPEG; default: return SNDRV_PCM_FORMAT_U8; }}static int snd_pcm_oss_format_to(int format){ switch (format) { case SNDRV_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; case SNDRV_PCM_FORMAT_A_LAW: return AFMT_A_LAW; case SNDRV_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; case SNDRV_PCM_FORMAT_U8: return AFMT_U8; case SNDRV_PCM_FORMAT_S16_LE: return AFMT_S16_LE; case SNDRV_PCM_FORMAT_S16_BE: return AFMT_S16_BE; case SNDRV_PCM_FORMAT_S8: return AFMT_S8; case SNDRV_PCM_FORMAT_U16_LE: return AFMT_U16_LE; case SNDRV_PCM_FORMAT_U16_BE: return AFMT_U16_BE; case SNDRV_PCM_FORMAT_MPEG: return AFMT_MPEG; default: return -EINVAL; }}static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *oss_params, snd_pcm_hw_params_t *slave_params){ size_t s; size_t oss_buffer_size, oss_period_size, oss_periods; size_t min_period_size, max_period_size; snd_pcm_runtime_t *runtime = substream->runtime; size_t oss_frame_size; oss_frame_size = snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8; oss_buffer_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0)) * oss_frame_size; oss_buffer_size = 1 << ld2(oss_buffer_size); if (atomic_read(&runtime->mmap_count)) { if (oss_buffer_size > runtime->oss.mmap_bytes) oss_buffer_size = runtime->oss.mmap_bytes; } if (substream->oss.setup && substream->oss.setup->period_size > 16) oss_period_size = substream->oss.setup->period_size; else if (runtime->oss.fragshift) { oss_period_size = 1 << runtime->oss.fragshift; if (oss_period_size > oss_buffer_size / 2) oss_period_size = oss_buffer_size / 2; } else { int sd; size_t bytes_per_sec = params_rate(oss_params) * snd_pcm_format_physical_width(params_format(oss_params)) * params_channels(oss_params) / 8; oss_period_size = oss_buffer_size; do { oss_period_size /= 2; } while (oss_period_size > bytes_per_sec); if (runtime->oss.subdivision == 0) { sd = 4; if (oss_period_size / sd > 4096) sd *= 2; if (oss_period_size / sd < 4096) sd = 1; } else sd = runtime->oss.subdivision; oss_period_size /= sd; if (oss_period_size < 16) oss_period_size = 16; } min_period_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); min_period_size *= oss_frame_size; min_period_size = 1 << (ld2(min_period_size - 1) + 1); if (oss_period_size < min_period_size) oss_period_size = min_period_size; max_period_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 0)); max_period_size *= oss_frame_size; max_period_size = 1 << ld2(max_period_size); if (oss_period_size > max_period_size) oss_period_size = max_period_size; oss_periods = oss_buffer_size / oss_period_size; if (substream->oss.setup) { if (substream->oss.setup->periods > 1) oss_periods = substream->oss.setup->periods; } s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); if (runtime->oss.maxfrags && s > runtime->oss.maxfrags) s = runtime->oss.maxfrags; if (oss_periods > s) oss_periods = s; s = snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, 0); if (s < 2) s = 2; if (oss_periods < s) oss_periods = s; while (oss_period_size * oss_periods > oss_buffer_size) oss_period_size /= 2; snd_assert(oss_period_size >= 16, return -EINVAL); runtime->oss.period_bytes = oss_period_size; runtime->oss.periods = oss_periods; return 0;}static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream){ snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_hw_params_t params, sparams; snd_pcm_sw_params_t sw_params; ssize_t oss_buffer_size, oss_period_size; size_t oss_frame_size; int err; int direct; int format, sformat, n; unsigned int sformat_mask; unsigned int mask; if (atomic_read(&runtime->mmap_count)) { direct = 1; } else { snd_pcm_oss_setup_t *setup = substream->oss.setup; direct = (setup != NULL && setup->direct); } _snd_pcm_hw_params_any(&sparams); _snd_pcm_hw_param_setinteger(&sparams, SNDRV_PCM_HW_PARAM_PERIODS); _snd_pcm_hw_param_min(&sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); if (atomic_read(&runtime->mmap_count)) mask = 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; else { mask = 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; if (!direct) mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; } err = snd_pcm_hw_param_mask(substream, &sparams, SNDRV_PCM_HW_PARAM_ACCESS, &mask); if (err < 0) { snd_printd("No usable accesses\n"); return -EINVAL; } snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_RATE, runtime->oss.rate, 0); snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); format = snd_pcm_oss_format_from(runtime->oss.format); sformat_mask = *hw_param_mask(&sparams, SNDRV_PCM_HW_PARAM_FORMAT); if (direct) sformat = format; else sformat = snd_pcm_plug_slave_format(format, sformat_mask); if (sformat < 0 || !(sformat_mask & (1 << sformat))) { for (sformat = 0; sformat <= SNDRV_PCM_FORMAT_LAST; sformat++) { if ((sformat_mask & (1 << sformat)) && snd_pcm_oss_format_to(sformat) >= 0) break; } if (sformat > SNDRV_PCM_FORMAT_LAST) { snd_printd("Cannot find a format!!!\n"); return -EINVAL; } } err = _snd_pcm_hw_param_set(&sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); snd_assert(err >= 0, return err); if (direct) { params = sparams; } else { _snd_pcm_hw_params_any(¶ms); _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_ACCESS, SNDRV_PCM_ACCESS_RW_INTERLEAVED, 0); _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_FORMAT, snd_pcm_oss_format_from(runtime->oss.format), 0); _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_CHANNELS, runtime->oss.channels, 0); _snd_pcm_hw_param_set(¶ms, SNDRV_PCM_HW_PARAM_RATE, runtime->oss.rate, 0); pdprintf("client: access = %i, format = %i, channels = %i, rate = %i\n", params_access(¶ms), params_format(¶ms), params_channels(¶ms), params_rate(¶ms)); } 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(¶ms)) * params_channels(¶ms) / 8; snd_pcm_oss_plugin_clear(substream); if (!direct) { /* add necessary plugins */ snd_pcm_oss_plugin_clear(substream); if ((err = snd_pcm_plug_format_plugins(substream, ¶ms, &sparams)) < 0) { snd_printd("snd_pcm_plug_format_plugins failed: %i\n", err); snd_pcm_oss_plugin_clear(substream); return err; } if (runtime->oss.plugin_first) { snd_pcm_plugin_t *plugin; 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); return err; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { err = snd_pcm_plugin_append(plugin); } else { err = snd_pcm_plugin_insert(plugin); } if (err < 0) { snd_pcm_oss_plugin_clear(substream); return err; } } } err = snd_pcm_oss_period_size(substream, ¶ms, &sparams); if (err < 0) return err; n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size); err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, 0); snd_assert(err >= 0, return err); err = snd_pcm_hw_param_near(substream, &sparams, SNDRV_PCM_HW_PARAM_PERIODS, runtime->oss.periods, 0); snd_assert(err >= 0, return err); snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, 0); if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, &sparams)) < 0) { snd_printd("HW_PARAMS failed: %i\n", err); return err; } memset(&sw_params, 0, sizeof(sw_params)); if (runtime->oss.trigger) { sw_params.start_threshold = 1; } else { sw_params.start_threshold = runtime->boundary; } if (atomic_read(&runtime->mmap_count)) 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.sleep_min = 0; sw_params.avail_min = runtime->period_size; sw_params.xfer_align = 1; sw_params.silence_threshold = 0; sw_params.silence_size = 0; if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params)) < 0) { snd_printd("SW_PARAMS failed: %i\n", err); return err; } runtime->control->avail_min = runtime->period_size; runtime->oss.periods = params_periods(&sparams); oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(&sparams)); snd_assert(oss_period_size >= 0, return -EINVAL); if (runtime->oss.plugin_first) { err = snd_pcm_plug_alloc(substream, oss_period_size); if (err < 0) return err; } oss_period_size *= oss_frame_size; oss_buffer_size = oss_period_size * runtime->oss.periods; snd_assert(oss_buffer_size >= 0, return -EINVAL); runtime->oss.period_bytes = oss_period_size; runtime->oss.buffer_bytes = oss_buffer_size; 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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -