📄 pcm.c
字号:
/* * Digital Audio (PCM) abstract layer * Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/mutex.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@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>");MODULE_DESCRIPTION("Midlevel PCM code for ALSA.");MODULE_LICENSE("GPL");static LIST_HEAD(snd_pcm_devices);static LIST_HEAD(snd_pcm_notify_list);static DEFINE_MUTEX(register_mutex);static int snd_pcm_free(struct snd_pcm *pcm);static int snd_pcm_dev_free(struct snd_device *device);static int snd_pcm_dev_register(struct snd_device *device);static int snd_pcm_dev_disconnect(struct snd_device *device);static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device){ struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { if (pcm->card == card && pcm->device == device) return pcm; } return NULL;}static int snd_pcm_control_ioctl(struct snd_card *card, struct snd_ctl_file *control, unsigned int cmd, unsigned long arg){ switch (cmd) { case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: { int device; if (get_user(device, (int __user *)arg)) return -EFAULT; mutex_lock(®ister_mutex); device = device < 0 ? 0 : device + 1; while (device < SNDRV_PCM_DEVICES) { if (snd_pcm_search(card, device)) break; device++; } if (device == SNDRV_PCM_DEVICES) device = -1; mutex_unlock(®ister_mutex); if (put_user(device, (int __user *)arg)) return -EFAULT; return 0; } case SNDRV_CTL_IOCTL_PCM_INFO: { struct snd_pcm_info __user *info; unsigned int device, subdevice; int stream; struct snd_pcm *pcm; struct snd_pcm_str *pstr; struct snd_pcm_substream *substream; int err; info = (struct snd_pcm_info __user *)arg; if (get_user(device, &info->device)) return -EFAULT; if (get_user(stream, &info->stream)) return -EFAULT; if (stream < 0 || stream > 1) return -EINVAL; if (get_user(subdevice, &info->subdevice)) return -EFAULT; mutex_lock(®ister_mutex); pcm = snd_pcm_search(card, device); if (pcm == NULL) { err = -ENXIO; goto _error; } pstr = &pcm->streams[stream]; if (pstr->substream_count == 0) { err = -ENOENT; goto _error; } if (subdevice >= pstr->substream_count) { err = -ENXIO; goto _error; } for (substream = pstr->substream; substream; substream = substream->next) if (substream->number == (int)subdevice) break; if (substream == NULL) { err = -ENXIO; goto _error; } err = snd_pcm_info_user(substream, info); _error: mutex_unlock(®ister_mutex); return err; } 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;}#ifdef CONFIG_SND_VERBOSE_PROCFS#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 static 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),};static const char *snd_pcm_format_name(snd_pcm_format_t format){ return snd_pcm_format_names[format];}static char *snd_pcm_stream_names[] = { STREAM(PLAYBACK), STREAM(CAPTURE),};static char *snd_pcm_state_names[] = { STATE(OPEN), STATE(SETUP), STATE(PREPARED), STATE(RUNNING), STATE(XRUN), STATE(DRAINING), STATE(PAUSED), STATE(SUSPENDED),};static char *snd_pcm_access_names[] = { ACCESS(MMAP_INTERLEAVED), ACCESS(MMAP_NONINTERLEAVED), ACCESS(MMAP_COMPLEX), ACCESS(RW_INTERLEAVED), ACCESS(RW_NONINTERLEAVED),};static char *snd_pcm_subformat_names[] = { SUBFORMAT(STD), };static char *snd_pcm_tstamp_mode_names[] = { TSTAMP(NONE), TSTAMP(MMAP),};static const char *snd_pcm_stream_name(int stream){ snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL); return snd_pcm_stream_names[stream];}static const char *snd_pcm_access_name(snd_pcm_access_t access){ return snd_pcm_access_names[access];}static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat){ return snd_pcm_subformat_names[subformat];}static const char *snd_pcm_tstamp_mode_name(int mode){ snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL); return snd_pcm_tstamp_mode_names[mode];}static const char *snd_pcm_state_name(snd_pcm_state_t state){ return snd_pcm_state_names[state];}#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)#include <linux/soundcard.h>static 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(struct snd_pcm_substream *substream, struct snd_info_buffer *buffer){ struct snd_pcm_info *info; int err; if (! substream) return; info = kmalloc(sizeof(*info), GFP_KERNEL); if (! info) { printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n"); return; } err = snd_pcm_info(substream, info); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); kfree(info); 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); kfree(info);}static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, buffer);}static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data, buffer);}static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime = substream->runtime; if (!runtime) { snd_iprintf(buffer, "closed\n"); return; } if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); 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}static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -