📄 pcm.c
字号:
/* * Digital Audio (PCM) abstract layer * 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 * */#include <sound/driver.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/time.h>#include <sound/core.h>#include <sound/minors.h>#include <sound/pcm.h>#include <sound/control.h>#include <sound/info.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Abramo Bagnara <abramo@alsa-project.org>");MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");MODULE_LICENSE("GPL");snd_pcm_t *snd_pcm_devices[SNDRV_CARDS * SNDRV_PCM_DEVICES];static LIST_HEAD(snd_pcm_notify_list);static DECLARE_MUTEX(register_mutex);static int snd_pcm_free(snd_pcm_t *pcm);static int snd_pcm_dev_free(snd_device_t *device);static int snd_pcm_dev_register(snd_device_t *device);static int snd_pcm_dev_disconnect(snd_device_t *device);static int snd_pcm_dev_unregister(snd_device_t *device);static int snd_pcm_control_ioctl(snd_card_t * card, snd_ctl_file_t * control, unsigned int cmd, unsigned long arg){ unsigned int tmp; tmp = card->number * SNDRV_PCM_DEVICES; switch (cmd) { case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: { int device; if (get_user(device, (int __user *)arg)) return -EFAULT; device = device < 0 ? 0 : device + 1; while (device < SNDRV_PCM_DEVICES) { if (snd_pcm_devices[tmp + device]) break; device++; } if (device == SNDRV_PCM_DEVICES) device = -1; if (put_user(device, (int __user *)arg)) return -EFAULT; return 0; } case SNDRV_CTL_IOCTL_PCM_INFO: { snd_pcm_info_t __user *info; unsigned int device, subdevice; snd_pcm_stream_t stream; snd_pcm_t *pcm; snd_pcm_str_t *pstr; snd_pcm_substream_t *substream; info = (snd_pcm_info_t __user *)arg; if (get_user(device, &info->device)) return -EFAULT; if (device >= SNDRV_PCM_DEVICES) return -ENXIO; pcm = snd_pcm_devices[tmp + device]; if (pcm == NULL) return -ENXIO; if (get_user(stream, &info->stream)) return -EFAULT; if (stream < 0 || stream > 1) return -EINVAL; pstr = &pcm->streams[stream]; if (pstr->substream_count == 0) return -ENOENT; if (get_user(subdevice, &info->subdevice)) return -EFAULT; if (subdevice >= pstr->substream_count) return -ENXIO; for (substream = pstr->substream; substream; substream = substream->next) if (substream->number == (int)subdevice) break; if (substream == NULL) return -ENXIO; return snd_pcm_info_user(substream, info); } case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: { int val; if (get_user(val, (int __user *)arg)) return -EFAULT; control->prefer_pcm_subdevice = val; return 0; } } return -ENOIOCTLCMD;}#define STATE(v) [SNDRV_PCM_STATE_##v] = #v#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v#define READY(v) [SNDRV_PCM_READY_##v] = #v#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v#define START(v) [SNDRV_PCM_START_##v] = #v#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v char *snd_pcm_stream_names[] = { STREAM(PLAYBACK), STREAM(CAPTURE),};char *snd_pcm_state_names[] = { STATE(OPEN), STATE(SETUP), STATE(PREPARED), STATE(RUNNING), STATE(XRUN), STATE(DRAINING), STATE(PAUSED), STATE(SUSPENDED),};char *snd_pcm_access_names[] = { ACCESS(MMAP_INTERLEAVED), ACCESS(MMAP_NONINTERLEAVED), ACCESS(MMAP_COMPLEX), ACCESS(RW_INTERLEAVED), ACCESS(RW_NONINTERLEAVED),};char *snd_pcm_format_names[] = { FORMAT(S8), FORMAT(U8), FORMAT(S16_LE), FORMAT(S16_BE), FORMAT(U16_LE), FORMAT(U16_BE), FORMAT(S24_LE), FORMAT(S24_BE), FORMAT(U24_LE), FORMAT(U24_BE), FORMAT(S32_LE), FORMAT(S32_BE), FORMAT(U32_LE), FORMAT(U32_BE), FORMAT(FLOAT_LE), FORMAT(FLOAT_BE), FORMAT(FLOAT64_LE), FORMAT(FLOAT64_BE), FORMAT(IEC958_SUBFRAME_LE), FORMAT(IEC958_SUBFRAME_BE), FORMAT(MU_LAW), FORMAT(A_LAW), FORMAT(IMA_ADPCM), FORMAT(MPEG), FORMAT(GSM), FORMAT(SPECIAL), FORMAT(S24_3LE), FORMAT(S24_3BE), FORMAT(U24_3LE), FORMAT(U24_3BE), FORMAT(S20_3LE), FORMAT(S20_3BE), FORMAT(U20_3LE), FORMAT(U20_3BE), FORMAT(S18_3LE), FORMAT(S18_3BE), FORMAT(U18_3LE), FORMAT(U18_3BE),};char *snd_pcm_subformat_names[] = { SUBFORMAT(STD), };char *snd_pcm_tstamp_mode_names[] = { TSTAMP(NONE), TSTAMP(MMAP),};const char *snd_pcm_stream_name(snd_pcm_stream_t stream){ snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL); return snd_pcm_stream_names[stream];}const char *snd_pcm_access_name(snd_pcm_access_t access){ snd_assert(access <= SNDRV_PCM_ACCESS_LAST, return NULL); return snd_pcm_access_names[access];}const char *snd_pcm_format_name(snd_pcm_format_t format){ snd_assert(format <= SNDRV_PCM_FORMAT_LAST, return NULL); return snd_pcm_format_names[format];}const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat){ snd_assert(subformat <= SNDRV_PCM_SUBFORMAT_LAST, return NULL); return snd_pcm_subformat_names[subformat];}const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode){ snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL); return snd_pcm_tstamp_mode_names[mode];}const char *snd_pcm_state_name(snd_pcm_state_t state){ snd_assert(state <= SNDRV_PCM_STATE_LAST, return NULL); return snd_pcm_state_names[state];}#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)#include <linux/soundcard.h>const char *snd_pcm_oss_format_name(int format){ switch (format) { case AFMT_MU_LAW: return "MU_LAW"; case AFMT_A_LAW: return "A_LAW"; case AFMT_IMA_ADPCM: return "IMA_ADPCM"; case AFMT_U8: return "U8"; case AFMT_S16_LE: return "S16_LE"; case AFMT_S16_BE: return "S16_BE"; case AFMT_S8: return "S8"; case AFMT_U16_LE: return "U16_LE"; case AFMT_U16_BE: return "U16_BE"; case AFMT_MPEG: return "MPEG"; default: return "unknown"; }}#endifstatic void snd_pcm_proc_info_read(snd_pcm_substream_t *substream, snd_info_buffer_t *buffer){ snd_pcm_info_t info; int err; snd_runtime_check(substream, return); err = snd_pcm_info(substream, &info); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); return; } snd_iprintf(buffer, "card: %d\n", info.card); snd_iprintf(buffer, "device: %d\n", info.device); snd_iprintf(buffer, "subdevice: %d\n", info.subdevice); snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info.stream)); snd_iprintf(buffer, "id: %s\n", info.id); snd_iprintf(buffer, "name: %s\n", info.name); snd_iprintf(buffer, "subname: %s\n", info.subname); snd_iprintf(buffer, "class: %d\n", info.dev_class); snd_iprintf(buffer, "subclass: %d\n", info.dev_subclass); snd_iprintf(buffer, "subdevices_count: %d\n", info.subdevices_count); snd_iprintf(buffer, "subdevices_avail: %d\n", info.subdevices_avail);}static void snd_pcm_stream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer){ snd_pcm_proc_info_read(((snd_pcm_str_t *)entry->private_data)->substream, buffer);}static void snd_pcm_substream_proc_info_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer){ snd_pcm_proc_info_read((snd_pcm_substream_t *)entry->private_data, buffer);}static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer){ snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; snd_pcm_runtime_t *runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); return; } snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); snd_pcm_stream_unlock_irq(substream); return; } snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); snd_iprintf(buffer, "channels: %u\n", runtime->channels); snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); snd_iprintf(buffer, "tick_time: %u\n", runtime->tick_time);#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) if (substream->oss.oss) { snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); }#endif snd_pcm_stream_unlock_irq(substream);}static void snd_pcm_substream_proc_sw_params_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer){ snd_pcm_substream_t *substream = (snd_pcm_substream_t *)entry->private_data; snd_pcm_runtime_t *runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); return; } snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); snd_pcm_stream_unlock_irq(substream); return; } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); snd_iprintf(buffer, "sleep_min: %u\n", runtime->sleep_min); snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); snd_iprintf(buffer, "xfer_align: %lu\n", runtime->xfer_align);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -