11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Digital Audio (PCM) abstract layer 3c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/init.h> 231da177e4SLinus Torvalds #include <linux/slab.h> 241da177e4SLinus Torvalds #include <linux/time.h> 251a60d4c5SIngo Molnar #include <linux/mutex.h> 261da177e4SLinus Torvalds #include <sound/core.h> 271da177e4SLinus Torvalds #include <sound/minors.h> 281da177e4SLinus Torvalds #include <sound/pcm.h> 291da177e4SLinus Torvalds #include <sound/control.h> 301da177e4SLinus Torvalds #include <sound/info.h> 311da177e4SLinus Torvalds 32c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); 331da177e4SLinus Torvalds MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 341da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 351da177e4SLinus Torvalds 36f87135f5SClemens Ladisch static LIST_HEAD(snd_pcm_devices); 371da177e4SLinus Torvalds static LIST_HEAD(snd_pcm_notify_list); 381a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 391da177e4SLinus Torvalds 40877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm); 41877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device); 42877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device); 43877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device); 441da177e4SLinus Torvalds 45f90c06a2SPawel MOLL static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) 46f87135f5SClemens Ladisch { 47f87135f5SClemens Ladisch struct snd_pcm *pcm; 48f87135f5SClemens Ladisch 499244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 50f87135f5SClemens Ladisch if (pcm->card == card && pcm->device == device) 51f87135f5SClemens Ladisch return pcm; 52f87135f5SClemens Ladisch } 53f87135f5SClemens Ladisch return NULL; 54f87135f5SClemens Ladisch } 55f87135f5SClemens Ladisch 56f90c06a2SPawel MOLL static int snd_pcm_next(struct snd_card *card, int device) 57f90c06a2SPawel MOLL { 58f90c06a2SPawel MOLL struct snd_pcm *pcm; 59f90c06a2SPawel MOLL 60f90c06a2SPawel MOLL list_for_each_entry(pcm, &snd_pcm_devices, list) { 61f90c06a2SPawel MOLL if (pcm->card == card && pcm->device > device) 62f90c06a2SPawel MOLL return pcm->device; 63f90c06a2SPawel MOLL else if (pcm->card->number > card->number) 64f90c06a2SPawel MOLL return -1; 65f90c06a2SPawel MOLL } 66f90c06a2SPawel MOLL return -1; 67f90c06a2SPawel MOLL } 68f90c06a2SPawel MOLL 69f90c06a2SPawel MOLL static int snd_pcm_add(struct snd_pcm *newpcm) 70f90c06a2SPawel MOLL { 71f90c06a2SPawel MOLL struct snd_pcm *pcm; 72f90c06a2SPawel MOLL 73f90c06a2SPawel MOLL list_for_each_entry(pcm, &snd_pcm_devices, list) { 74f90c06a2SPawel MOLL if (pcm->card == newpcm->card && pcm->device == newpcm->device) 75f90c06a2SPawel MOLL return -EBUSY; 76f90c06a2SPawel MOLL if (pcm->card->number > newpcm->card->number || 77f90c06a2SPawel MOLL (pcm->card == newpcm->card && 78f90c06a2SPawel MOLL pcm->device > newpcm->device)) { 79f90c06a2SPawel MOLL list_add(&newpcm->list, pcm->list.prev); 80f90c06a2SPawel MOLL return 0; 81f90c06a2SPawel MOLL } 82f90c06a2SPawel MOLL } 83f90c06a2SPawel MOLL list_add_tail(&newpcm->list, &snd_pcm_devices); 84f90c06a2SPawel MOLL return 0; 85f90c06a2SPawel MOLL } 86f90c06a2SPawel MOLL 87877211f5STakashi Iwai static int snd_pcm_control_ioctl(struct snd_card *card, 88877211f5STakashi Iwai struct snd_ctl_file *control, 891da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 901da177e4SLinus Torvalds { 911da177e4SLinus Torvalds switch (cmd) { 921da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 931da177e4SLinus Torvalds { 941da177e4SLinus Torvalds int device; 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds if (get_user(device, (int __user *)arg)) 971da177e4SLinus Torvalds return -EFAULT; 981a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 99f90c06a2SPawel MOLL device = snd_pcm_next(card, device); 1001a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1011da177e4SLinus Torvalds if (put_user(device, (int __user *)arg)) 1021da177e4SLinus Torvalds return -EFAULT; 1031da177e4SLinus Torvalds return 0; 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_INFO: 1061da177e4SLinus Torvalds { 107877211f5STakashi Iwai struct snd_pcm_info __user *info; 1081da177e4SLinus Torvalds unsigned int device, subdevice; 109877211f5STakashi Iwai int stream; 110877211f5STakashi Iwai struct snd_pcm *pcm; 111877211f5STakashi Iwai struct snd_pcm_str *pstr; 112877211f5STakashi Iwai struct snd_pcm_substream *substream; 113f87135f5SClemens Ladisch int err; 114f87135f5SClemens Ladisch 115877211f5STakashi Iwai info = (struct snd_pcm_info __user *)arg; 1161da177e4SLinus Torvalds if (get_user(device, &info->device)) 1171da177e4SLinus Torvalds return -EFAULT; 1181da177e4SLinus Torvalds if (get_user(stream, &info->stream)) 1191da177e4SLinus Torvalds return -EFAULT; 1201da177e4SLinus Torvalds if (stream < 0 || stream > 1) 1211da177e4SLinus Torvalds return -EINVAL; 1221da177e4SLinus Torvalds if (get_user(subdevice, &info->subdevice)) 1231da177e4SLinus Torvalds return -EFAULT; 1241a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 125f90c06a2SPawel MOLL pcm = snd_pcm_get(card, device); 126f87135f5SClemens Ladisch if (pcm == NULL) { 127f87135f5SClemens Ladisch err = -ENXIO; 128f87135f5SClemens Ladisch goto _error; 129f87135f5SClemens Ladisch } 130f87135f5SClemens Ladisch pstr = &pcm->streams[stream]; 131f87135f5SClemens Ladisch if (pstr->substream_count == 0) { 132f87135f5SClemens Ladisch err = -ENOENT; 133f87135f5SClemens Ladisch goto _error; 134f87135f5SClemens Ladisch } 135f87135f5SClemens Ladisch if (subdevice >= pstr->substream_count) { 136f87135f5SClemens Ladisch err = -ENXIO; 137f87135f5SClemens Ladisch goto _error; 138f87135f5SClemens Ladisch } 139f87135f5SClemens Ladisch for (substream = pstr->substream; substream; 140f87135f5SClemens Ladisch substream = substream->next) 1411da177e4SLinus Torvalds if (substream->number == (int)subdevice) 1421da177e4SLinus Torvalds break; 143f87135f5SClemens Ladisch if (substream == NULL) { 144f87135f5SClemens Ladisch err = -ENXIO; 145f87135f5SClemens Ladisch goto _error; 146f87135f5SClemens Ladisch } 147f87135f5SClemens Ladisch err = snd_pcm_info_user(substream, info); 148f87135f5SClemens Ladisch _error: 1491a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 150f87135f5SClemens Ladisch return err; 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 1531da177e4SLinus Torvalds { 1541da177e4SLinus Torvalds int val; 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds if (get_user(val, (int __user *)arg)) 1571da177e4SLinus Torvalds return -EFAULT; 1581da177e4SLinus Torvalds control->prefer_pcm_subdevice = val; 1591da177e4SLinus Torvalds return 0; 1601da177e4SLinus Torvalds } 1611da177e4SLinus Torvalds } 1621da177e4SLinus Torvalds return -ENOIOCTLCMD; 1631da177e4SLinus Torvalds } 16421a3479aSJaroslav Kysela 165b7d90a35STakashi Iwai #ifdef CONFIG_SND_VERBOSE_PROCFS 16621a3479aSJaroslav Kysela 1671da177e4SLinus Torvalds #define STATE(v) [SNDRV_PCM_STATE_##v] = #v 1681da177e4SLinus Torvalds #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v 1691da177e4SLinus Torvalds #define READY(v) [SNDRV_PCM_READY_##v] = #v 1701da177e4SLinus Torvalds #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v 1711da177e4SLinus Torvalds #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v 1721da177e4SLinus Torvalds #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v 1731da177e4SLinus Torvalds #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v 1741da177e4SLinus Torvalds #define START(v) [SNDRV_PCM_START_##v] = #v 1751da177e4SLinus Torvalds #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v 1761da177e4SLinus Torvalds #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds static char *snd_pcm_format_names[] = { 1791da177e4SLinus Torvalds FORMAT(S8), 1801da177e4SLinus Torvalds FORMAT(U8), 1811da177e4SLinus Torvalds FORMAT(S16_LE), 1821da177e4SLinus Torvalds FORMAT(S16_BE), 1831da177e4SLinus Torvalds FORMAT(U16_LE), 1841da177e4SLinus Torvalds FORMAT(U16_BE), 1851da177e4SLinus Torvalds FORMAT(S24_LE), 1861da177e4SLinus Torvalds FORMAT(S24_BE), 1871da177e4SLinus Torvalds FORMAT(U24_LE), 1881da177e4SLinus Torvalds FORMAT(U24_BE), 1891da177e4SLinus Torvalds FORMAT(S32_LE), 1901da177e4SLinus Torvalds FORMAT(S32_BE), 1911da177e4SLinus Torvalds FORMAT(U32_LE), 1921da177e4SLinus Torvalds FORMAT(U32_BE), 1931da177e4SLinus Torvalds FORMAT(FLOAT_LE), 1941da177e4SLinus Torvalds FORMAT(FLOAT_BE), 1951da177e4SLinus Torvalds FORMAT(FLOAT64_LE), 1961da177e4SLinus Torvalds FORMAT(FLOAT64_BE), 1971da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_LE), 1981da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_BE), 1991da177e4SLinus Torvalds FORMAT(MU_LAW), 2001da177e4SLinus Torvalds FORMAT(A_LAW), 2011da177e4SLinus Torvalds FORMAT(IMA_ADPCM), 2021da177e4SLinus Torvalds FORMAT(MPEG), 2031da177e4SLinus Torvalds FORMAT(GSM), 2041da177e4SLinus Torvalds FORMAT(SPECIAL), 2051da177e4SLinus Torvalds FORMAT(S24_3LE), 2061da177e4SLinus Torvalds FORMAT(S24_3BE), 2071da177e4SLinus Torvalds FORMAT(U24_3LE), 2081da177e4SLinus Torvalds FORMAT(U24_3BE), 2091da177e4SLinus Torvalds FORMAT(S20_3LE), 2101da177e4SLinus Torvalds FORMAT(S20_3BE), 2111da177e4SLinus Torvalds FORMAT(U20_3LE), 2121da177e4SLinus Torvalds FORMAT(U20_3BE), 2131da177e4SLinus Torvalds FORMAT(S18_3LE), 2141da177e4SLinus Torvalds FORMAT(S18_3BE), 2151da177e4SLinus Torvalds FORMAT(U18_3LE), 2161da177e4SLinus Torvalds FORMAT(U18_3BE), 2171da177e4SLinus Torvalds }; 2181da177e4SLinus Torvalds 21912831c15SAdrian Bunk static const char *snd_pcm_format_name(snd_pcm_format_t format) 220e28563ccSTakashi Iwai { 221e28563ccSTakashi Iwai return snd_pcm_format_names[format]; 222e28563ccSTakashi Iwai } 223e28563ccSTakashi Iwai 224e28563ccSTakashi Iwai static char *snd_pcm_stream_names[] = { 225e28563ccSTakashi Iwai STREAM(PLAYBACK), 226e28563ccSTakashi Iwai STREAM(CAPTURE), 227e28563ccSTakashi Iwai }; 228e28563ccSTakashi Iwai 229e28563ccSTakashi Iwai static char *snd_pcm_state_names[] = { 230e28563ccSTakashi Iwai STATE(OPEN), 231e28563ccSTakashi Iwai STATE(SETUP), 232e28563ccSTakashi Iwai STATE(PREPARED), 233e28563ccSTakashi Iwai STATE(RUNNING), 234e28563ccSTakashi Iwai STATE(XRUN), 235e28563ccSTakashi Iwai STATE(DRAINING), 236e28563ccSTakashi Iwai STATE(PAUSED), 237e28563ccSTakashi Iwai STATE(SUSPENDED), 238e28563ccSTakashi Iwai }; 239e28563ccSTakashi Iwai 240e28563ccSTakashi Iwai static char *snd_pcm_access_names[] = { 241e28563ccSTakashi Iwai ACCESS(MMAP_INTERLEAVED), 242e28563ccSTakashi Iwai ACCESS(MMAP_NONINTERLEAVED), 243e28563ccSTakashi Iwai ACCESS(MMAP_COMPLEX), 244e28563ccSTakashi Iwai ACCESS(RW_INTERLEAVED), 245e28563ccSTakashi Iwai ACCESS(RW_NONINTERLEAVED), 246e28563ccSTakashi Iwai }; 247e28563ccSTakashi Iwai 2481da177e4SLinus Torvalds static char *snd_pcm_subformat_names[] = { 2491da177e4SLinus Torvalds SUBFORMAT(STD), 2501da177e4SLinus Torvalds }; 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds static char *snd_pcm_tstamp_mode_names[] = { 2531da177e4SLinus Torvalds TSTAMP(NONE), 2548c121586SJaroslav Kysela TSTAMP(ENABLE), 2551da177e4SLinus Torvalds }; 2561da177e4SLinus Torvalds 257877211f5STakashi Iwai static const char *snd_pcm_stream_name(int stream) 2581da177e4SLinus Torvalds { 2591da177e4SLinus Torvalds return snd_pcm_stream_names[stream]; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds static const char *snd_pcm_access_name(snd_pcm_access_t access) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds return snd_pcm_access_names[access]; 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds return snd_pcm_subformat_names[subformat]; 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 272877211f5STakashi Iwai static const char *snd_pcm_tstamp_mode_name(int mode) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds return snd_pcm_tstamp_mode_names[mode]; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds static const char *snd_pcm_state_name(snd_pcm_state_t state) 2781da177e4SLinus Torvalds { 2791da177e4SLinus Torvalds return snd_pcm_state_names[state]; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 2831da177e4SLinus Torvalds #include <linux/soundcard.h> 2841a60d4c5SIngo Molnar 2851da177e4SLinus Torvalds static const char *snd_pcm_oss_format_name(int format) 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds switch (format) { 2881da177e4SLinus Torvalds case AFMT_MU_LAW: 2891da177e4SLinus Torvalds return "MU_LAW"; 2901da177e4SLinus Torvalds case AFMT_A_LAW: 2911da177e4SLinus Torvalds return "A_LAW"; 2921da177e4SLinus Torvalds case AFMT_IMA_ADPCM: 2931da177e4SLinus Torvalds return "IMA_ADPCM"; 2941da177e4SLinus Torvalds case AFMT_U8: 2951da177e4SLinus Torvalds return "U8"; 2961da177e4SLinus Torvalds case AFMT_S16_LE: 2971da177e4SLinus Torvalds return "S16_LE"; 2981da177e4SLinus Torvalds case AFMT_S16_BE: 2991da177e4SLinus Torvalds return "S16_BE"; 3001da177e4SLinus Torvalds case AFMT_S8: 3011da177e4SLinus Torvalds return "S8"; 3021da177e4SLinus Torvalds case AFMT_U16_LE: 3031da177e4SLinus Torvalds return "U16_LE"; 3041da177e4SLinus Torvalds case AFMT_U16_BE: 3051da177e4SLinus Torvalds return "U16_BE"; 3061da177e4SLinus Torvalds case AFMT_MPEG: 3071da177e4SLinus Torvalds return "MPEG"; 3081da177e4SLinus Torvalds default: 3091da177e4SLinus Torvalds return "unknown"; 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds #endif 3131da177e4SLinus Torvalds 314877211f5STakashi Iwai static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, 315877211f5STakashi Iwai struct snd_info_buffer *buffer) 3161da177e4SLinus Torvalds { 317877211f5STakashi Iwai struct snd_pcm_info *info; 3181da177e4SLinus Torvalds int err; 3191da177e4SLinus Torvalds 3207c22f1aaSTakashi Iwai if (! substream) 3217c22f1aaSTakashi Iwai return; 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds info = kmalloc(sizeof(*info), GFP_KERNEL); 3241da177e4SLinus Torvalds if (! info) { 3251da177e4SLinus Torvalds printk(KERN_DEBUG "snd_pcm_proc_info_read: cannot malloc\n"); 3261da177e4SLinus Torvalds return; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds err = snd_pcm_info(substream, info); 3301da177e4SLinus Torvalds if (err < 0) { 3311da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 3321da177e4SLinus Torvalds kfree(info); 3331da177e4SLinus Torvalds return; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds snd_iprintf(buffer, "card: %d\n", info->card); 3361da177e4SLinus Torvalds snd_iprintf(buffer, "device: %d\n", info->device); 3371da177e4SLinus Torvalds snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); 3381da177e4SLinus Torvalds snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); 3391da177e4SLinus Torvalds snd_iprintf(buffer, "id: %s\n", info->id); 3401da177e4SLinus Torvalds snd_iprintf(buffer, "name: %s\n", info->name); 3411da177e4SLinus Torvalds snd_iprintf(buffer, "subname: %s\n", info->subname); 3421da177e4SLinus Torvalds snd_iprintf(buffer, "class: %d\n", info->dev_class); 3431da177e4SLinus Torvalds snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); 3441da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); 3451da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); 3461da177e4SLinus Torvalds kfree(info); 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 349877211f5STakashi Iwai static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, 350877211f5STakashi Iwai struct snd_info_buffer *buffer) 3511da177e4SLinus Torvalds { 352877211f5STakashi Iwai snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, 353877211f5STakashi Iwai buffer); 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356877211f5STakashi Iwai static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, 357877211f5STakashi Iwai struct snd_info_buffer *buffer) 3581da177e4SLinus Torvalds { 359877211f5STakashi Iwai snd_pcm_proc_info_read((struct snd_pcm_substream *)entry->private_data, 360877211f5STakashi Iwai buffer); 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds 363877211f5STakashi Iwai static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, 364877211f5STakashi Iwai struct snd_info_buffer *buffer) 3651da177e4SLinus Torvalds { 366877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 367877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 3681da177e4SLinus Torvalds if (!runtime) { 3691da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 3701da177e4SLinus Torvalds return; 3711da177e4SLinus Torvalds } 3721da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 3731da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 3741da177e4SLinus Torvalds return; 3751da177e4SLinus Torvalds } 3761da177e4SLinus Torvalds snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); 3771da177e4SLinus Torvalds snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); 3781da177e4SLinus Torvalds snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); 3791da177e4SLinus Torvalds snd_iprintf(buffer, "channels: %u\n", runtime->channels); 3801da177e4SLinus Torvalds snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 3811da177e4SLinus Torvalds snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); 3821da177e4SLinus Torvalds snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); 3831da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 3841da177e4SLinus Torvalds if (substream->oss.oss) { 3851da177e4SLinus Torvalds snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); 3861da177e4SLinus Torvalds snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); 3871da177e4SLinus Torvalds snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); 3881da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); 3891da177e4SLinus Torvalds snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); 3901da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds #endif 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 395877211f5STakashi Iwai static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, 396877211f5STakashi Iwai struct snd_info_buffer *buffer) 3971da177e4SLinus Torvalds { 398877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 399877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 4001da177e4SLinus Torvalds if (!runtime) { 4011da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 4021da177e4SLinus Torvalds return; 4031da177e4SLinus Torvalds } 4041da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 4051da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 4061da177e4SLinus Torvalds return; 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); 4091da177e4SLinus Torvalds snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); 4101da177e4SLinus Torvalds snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); 4111da177e4SLinus Torvalds snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); 4121da177e4SLinus Torvalds snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); 4131da177e4SLinus Torvalds snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); 4141da177e4SLinus Torvalds snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); 4151da177e4SLinus Torvalds snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds 418877211f5STakashi Iwai static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, 419877211f5STakashi Iwai struct snd_info_buffer *buffer) 4201da177e4SLinus Torvalds { 421877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 422877211f5STakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 423877211f5STakashi Iwai struct snd_pcm_status status; 4241da177e4SLinus Torvalds int err; 4251da177e4SLinus Torvalds if (!runtime) { 4261da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 4271da177e4SLinus Torvalds return; 4281da177e4SLinus Torvalds } 4291da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 4301da177e4SLinus Torvalds err = snd_pcm_status(substream, &status); 4311da177e4SLinus Torvalds if (err < 0) { 4321da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 4331da177e4SLinus Torvalds return; 4341da177e4SLinus Torvalds } 4351da177e4SLinus Torvalds snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 4361da177e4SLinus Torvalds snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", 4371da177e4SLinus Torvalds status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); 4381da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp : %ld.%09ld\n", 4391da177e4SLinus Torvalds status.tstamp.tv_sec, status.tstamp.tv_nsec); 4401da177e4SLinus Torvalds snd_iprintf(buffer, "delay : %ld\n", status.delay); 4411da177e4SLinus Torvalds snd_iprintf(buffer, "avail : %ld\n", status.avail); 4421da177e4SLinus Torvalds snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); 4431da177e4SLinus Torvalds snd_iprintf(buffer, "-----\n"); 4441da177e4SLinus Torvalds snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); 4451da177e4SLinus Torvalds snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds 44861fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 449877211f5STakashi Iwai static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 450877211f5STakashi Iwai struct snd_info_buffer *buffer) 4511da177e4SLinus Torvalds { 452877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 4531da177e4SLinus Torvalds snd_iprintf(buffer, "%d\n", pstr->xrun_debug); 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 456877211f5STakashi Iwai static void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, 457877211f5STakashi Iwai struct snd_info_buffer *buffer) 4581da177e4SLinus Torvalds { 459877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 4601da177e4SLinus Torvalds char line[64]; 4611da177e4SLinus Torvalds if (!snd_info_get_line(buffer, line, sizeof(line))) 4621da177e4SLinus Torvalds pstr->xrun_debug = simple_strtoul(line, NULL, 10); 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds #endif 4651da177e4SLinus Torvalds 466877211f5STakashi Iwai static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) 4671da177e4SLinus Torvalds { 468877211f5STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 469877211f5STakashi Iwai struct snd_info_entry *entry; 4701da177e4SLinus Torvalds char name[16]; 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds sprintf(name, "pcm%i%c", pcm->device, 4731da177e4SLinus Torvalds pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 4741da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) 4751da177e4SLinus Torvalds return -ENOMEM; 4761da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 4771da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4781da177e4SLinus Torvalds snd_info_free_entry(entry); 4791da177e4SLinus Torvalds return -ENOMEM; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds pstr->proc_root = entry; 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { 484bf850204STakashi Iwai snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 4851da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 4861da177e4SLinus Torvalds snd_info_free_entry(entry); 4871da177e4SLinus Torvalds entry = NULL; 4881da177e4SLinus Torvalds } 4891da177e4SLinus Torvalds } 4901da177e4SLinus Torvalds pstr->proc_info_entry = entry; 4911da177e4SLinus Torvalds 49261fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 493877211f5STakashi Iwai if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 494877211f5STakashi Iwai pstr->proc_root)) != NULL) { 4951da177e4SLinus Torvalds entry->c.text.read = snd_pcm_xrun_debug_read; 4961da177e4SLinus Torvalds entry->c.text.write = snd_pcm_xrun_debug_write; 497bd7bf042STakashi Iwai entry->mode |= S_IWUSR; 4981da177e4SLinus Torvalds entry->private_data = pstr; 4991da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5001da177e4SLinus Torvalds snd_info_free_entry(entry); 5011da177e4SLinus Torvalds entry = NULL; 5021da177e4SLinus Torvalds } 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = entry; 5051da177e4SLinus Torvalds #endif 5061da177e4SLinus Torvalds return 0; 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds 509877211f5STakashi Iwai static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 5101da177e4SLinus Torvalds { 51161fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 512746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_xrun_debug_entry); 5131da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = NULL; 5141da177e4SLinus Torvalds #endif 515746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_info_entry); 5161da177e4SLinus Torvalds pstr->proc_info_entry = NULL; 517746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_root); 5181da177e4SLinus Torvalds pstr->proc_root = NULL; 5191da177e4SLinus Torvalds return 0; 5201da177e4SLinus Torvalds } 5211da177e4SLinus Torvalds 522877211f5STakashi Iwai static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5231da177e4SLinus Torvalds { 524877211f5STakashi Iwai struct snd_info_entry *entry; 525877211f5STakashi Iwai struct snd_card *card; 5261da177e4SLinus Torvalds char name[16]; 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds card = substream->pcm->card; 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds sprintf(name, "sub%i", substream->number); 5311da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) 5321da177e4SLinus Torvalds return -ENOMEM; 5331da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5341da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5351da177e4SLinus Torvalds snd_info_free_entry(entry); 5361da177e4SLinus Torvalds return -ENOMEM; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds substream->proc_root = entry; 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { 541bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 542bf850204STakashi Iwai snd_pcm_substream_proc_info_read); 5431da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5441da177e4SLinus Torvalds snd_info_free_entry(entry); 5451da177e4SLinus Torvalds entry = NULL; 5461da177e4SLinus Torvalds } 5471da177e4SLinus Torvalds } 5481da177e4SLinus Torvalds substream->proc_info_entry = entry; 5491da177e4SLinus Torvalds 5501da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { 551bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 552bf850204STakashi Iwai snd_pcm_substream_proc_hw_params_read); 5531da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5541da177e4SLinus Torvalds snd_info_free_entry(entry); 5551da177e4SLinus Torvalds entry = NULL; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds substream->proc_hw_params_entry = entry; 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { 561bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 562bf850204STakashi Iwai snd_pcm_substream_proc_sw_params_read); 5631da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5641da177e4SLinus Torvalds snd_info_free_entry(entry); 5651da177e4SLinus Torvalds entry = NULL; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds substream->proc_sw_params_entry = entry; 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { 571bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 572bf850204STakashi Iwai snd_pcm_substream_proc_status_read); 5731da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5741da177e4SLinus Torvalds snd_info_free_entry(entry); 5751da177e4SLinus Torvalds entry = NULL; 5761da177e4SLinus Torvalds } 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds substream->proc_status_entry = entry; 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds return 0; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds 583877211f5STakashi Iwai static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) 5841da177e4SLinus Torvalds { 585746d4a02STakashi Iwai snd_info_free_entry(substream->proc_info_entry); 5861da177e4SLinus Torvalds substream->proc_info_entry = NULL; 587746d4a02STakashi Iwai snd_info_free_entry(substream->proc_hw_params_entry); 5881da177e4SLinus Torvalds substream->proc_hw_params_entry = NULL; 589746d4a02STakashi Iwai snd_info_free_entry(substream->proc_sw_params_entry); 5901da177e4SLinus Torvalds substream->proc_sw_params_entry = NULL; 591746d4a02STakashi Iwai snd_info_free_entry(substream->proc_status_entry); 5921da177e4SLinus Torvalds substream->proc_status_entry = NULL; 593746d4a02STakashi Iwai snd_info_free_entry(substream->proc_root); 5941da177e4SLinus Torvalds substream->proc_root = NULL; 5951da177e4SLinus Torvalds return 0; 5961da177e4SLinus Torvalds } 597b7d90a35STakashi Iwai #else /* !CONFIG_SND_VERBOSE_PROCFS */ 598e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 599e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 600e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 601e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; } 602b7d90a35STakashi Iwai #endif /* CONFIG_SND_VERBOSE_PROCFS */ 6031da177e4SLinus Torvalds 6041da177e4SLinus Torvalds /** 6051da177e4SLinus Torvalds * snd_pcm_new_stream - create a new PCM stream 6061da177e4SLinus Torvalds * @pcm: the pcm instance 6071da177e4SLinus Torvalds * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 6081da177e4SLinus Torvalds * @substream_count: the number of substreams 6091da177e4SLinus Torvalds * 6101da177e4SLinus Torvalds * Creates a new stream for the pcm. 6111da177e4SLinus Torvalds * The corresponding stream on the pcm must have been empty before 6121da177e4SLinus Torvalds * calling this, i.e. zero must be given to the argument of 6131da177e4SLinus Torvalds * snd_pcm_new(). 6141da177e4SLinus Torvalds * 6151da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 6161da177e4SLinus Torvalds */ 617877211f5STakashi Iwai int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 6181da177e4SLinus Torvalds { 6191da177e4SLinus Torvalds int idx, err; 620877211f5STakashi Iwai struct snd_pcm_str *pstr = &pcm->streams[stream]; 621877211f5STakashi Iwai struct snd_pcm_substream *substream, *prev; 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 6241a60d4c5SIngo Molnar mutex_init(&pstr->oss.setup_mutex); 6251da177e4SLinus Torvalds #endif 6261da177e4SLinus Torvalds pstr->stream = stream; 6271da177e4SLinus Torvalds pstr->pcm = pcm; 6281da177e4SLinus Torvalds pstr->substream_count = substream_count; 6291da177e4SLinus Torvalds if (substream_count > 0) { 6301da177e4SLinus Torvalds err = snd_pcm_stream_proc_init(pstr); 63173e77ba0STakashi Iwai if (err < 0) { 63273e77ba0STakashi Iwai snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); 6331da177e4SLinus Torvalds return err; 6341da177e4SLinus Torvalds } 63573e77ba0STakashi Iwai } 6361da177e4SLinus Torvalds prev = NULL; 6371da177e4SLinus Torvalds for (idx = 0, prev = NULL; idx < substream_count; idx++) { 638ca2c0966STakashi Iwai substream = kzalloc(sizeof(*substream), GFP_KERNEL); 63973e77ba0STakashi Iwai if (substream == NULL) { 64073e77ba0STakashi Iwai snd_printk(KERN_ERR "Cannot allocate PCM substream\n"); 6411da177e4SLinus Torvalds return -ENOMEM; 64273e77ba0STakashi Iwai } 6431da177e4SLinus Torvalds substream->pcm = pcm; 6441da177e4SLinus Torvalds substream->pstr = pstr; 6451da177e4SLinus Torvalds substream->number = idx; 6461da177e4SLinus Torvalds substream->stream = stream; 6471da177e4SLinus Torvalds sprintf(substream->name, "subdevice #%i", idx); 6489442e691STakashi Iwai snprintf(substream->latency_id, sizeof(substream->latency_id), 6499442e691STakashi Iwai "ALSA-PCM%d-%d%c%d", pcm->card->number, pcm->device, 6509442e691STakashi Iwai (stream ? 'c' : 'p'), idx); 6511da177e4SLinus Torvalds substream->buffer_bytes_max = UINT_MAX; 6521da177e4SLinus Torvalds if (prev == NULL) 6531da177e4SLinus Torvalds pstr->substream = substream; 6541da177e4SLinus Torvalds else 6551da177e4SLinus Torvalds prev->next = substream; 6561da177e4SLinus Torvalds err = snd_pcm_substream_proc_init(substream); 6571da177e4SLinus Torvalds if (err < 0) { 65873e77ba0STakashi Iwai snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); 6594d361285SAkinobu Mita if (prev == NULL) 6604d361285SAkinobu Mita pstr->substream = NULL; 6614d361285SAkinobu Mita else 6624d361285SAkinobu Mita prev->next = NULL; 6631da177e4SLinus Torvalds kfree(substream); 6641da177e4SLinus Torvalds return err; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds substream->group = &substream->self_group; 6671da177e4SLinus Torvalds spin_lock_init(&substream->self_group.lock); 6681da177e4SLinus Torvalds INIT_LIST_HEAD(&substream->self_group.substreams); 6691da177e4SLinus Torvalds list_add_tail(&substream->link_list, &substream->self_group.substreams); 6701da177e4SLinus Torvalds spin_lock_init(&substream->timer_lock); 6719c323fcbSTakashi Iwai atomic_set(&substream->mmap_count, 0); 6721da177e4SLinus Torvalds prev = substream; 6731da177e4SLinus Torvalds } 6741da177e4SLinus Torvalds return 0; 6751da177e4SLinus Torvalds } 6761da177e4SLinus Torvalds 677e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new_stream); 678e88e8ae6STakashi Iwai 6791da177e4SLinus Torvalds /** 6801da177e4SLinus Torvalds * snd_pcm_new - create a new PCM instance 6811da177e4SLinus Torvalds * @card: the card instance 6821da177e4SLinus Torvalds * @id: the id string 6831da177e4SLinus Torvalds * @device: the device index (zero based) 6841da177e4SLinus Torvalds * @playback_count: the number of substreams for playback 6851da177e4SLinus Torvalds * @capture_count: the number of substreams for capture 6861da177e4SLinus Torvalds * @rpcm: the pointer to store the new pcm instance 6871da177e4SLinus Torvalds * 6881da177e4SLinus Torvalds * Creates a new PCM instance. 6891da177e4SLinus Torvalds * 6901da177e4SLinus Torvalds * The pcm operators have to be set afterwards to the new instance 6911da177e4SLinus Torvalds * via snd_pcm_set_ops(). 6921da177e4SLinus Torvalds * 6931da177e4SLinus Torvalds * Returns zero if successful, or a negative error code on failure. 6941da177e4SLinus Torvalds */ 695877211f5STakashi Iwai int snd_pcm_new(struct snd_card *card, char *id, int device, 6961da177e4SLinus Torvalds int playback_count, int capture_count, 697877211f5STakashi Iwai struct snd_pcm ** rpcm) 6981da177e4SLinus Torvalds { 699877211f5STakashi Iwai struct snd_pcm *pcm; 7001da177e4SLinus Torvalds int err; 701877211f5STakashi Iwai static struct snd_device_ops ops = { 7021da177e4SLinus Torvalds .dev_free = snd_pcm_dev_free, 7031da177e4SLinus Torvalds .dev_register = snd_pcm_dev_register, 7041da177e4SLinus Torvalds .dev_disconnect = snd_pcm_dev_disconnect, 7051da177e4SLinus Torvalds }; 7061da177e4SLinus Torvalds 7077eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7087eaa943cSTakashi Iwai return -ENXIO; 7097eaa943cSTakashi Iwai if (rpcm) 7101da177e4SLinus Torvalds *rpcm = NULL; 711ca2c0966STakashi Iwai pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 71273e77ba0STakashi Iwai if (pcm == NULL) { 71373e77ba0STakashi Iwai snd_printk(KERN_ERR "Cannot allocate PCM\n"); 7141da177e4SLinus Torvalds return -ENOMEM; 71573e77ba0STakashi Iwai } 7161da177e4SLinus Torvalds pcm->card = card; 7171da177e4SLinus Torvalds pcm->device = device; 71873e77ba0STakashi Iwai if (id) 7191da177e4SLinus Torvalds strlcpy(pcm->id, id, sizeof(pcm->id)); 7201da177e4SLinus Torvalds if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { 7211da177e4SLinus Torvalds snd_pcm_free(pcm); 7221da177e4SLinus Torvalds return err; 7231da177e4SLinus Torvalds } 7241da177e4SLinus Torvalds if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { 7251da177e4SLinus Torvalds snd_pcm_free(pcm); 7261da177e4SLinus Torvalds return err; 7271da177e4SLinus Torvalds } 7281a60d4c5SIngo Molnar mutex_init(&pcm->open_mutex); 7291da177e4SLinus Torvalds init_waitqueue_head(&pcm->open_wait); 7301da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { 7311da177e4SLinus Torvalds snd_pcm_free(pcm); 7321da177e4SLinus Torvalds return err; 7331da177e4SLinus Torvalds } 7347eaa943cSTakashi Iwai if (rpcm) 7351da177e4SLinus Torvalds *rpcm = pcm; 7361da177e4SLinus Torvalds return 0; 7371da177e4SLinus Torvalds } 7381da177e4SLinus Torvalds 739e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new); 740e88e8ae6STakashi Iwai 741877211f5STakashi Iwai static void snd_pcm_free_stream(struct snd_pcm_str * pstr) 7421da177e4SLinus Torvalds { 743877211f5STakashi Iwai struct snd_pcm_substream *substream, *substream_next; 7441da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 745877211f5STakashi Iwai struct snd_pcm_oss_setup *setup, *setupn; 7461da177e4SLinus Torvalds #endif 7471da177e4SLinus Torvalds substream = pstr->substream; 7481da177e4SLinus Torvalds while (substream) { 7491da177e4SLinus Torvalds substream_next = substream->next; 750c461482cSTakashi Iwai snd_pcm_timer_done(substream); 7511da177e4SLinus Torvalds snd_pcm_substream_proc_done(substream); 7521da177e4SLinus Torvalds kfree(substream); 7531da177e4SLinus Torvalds substream = substream_next; 7541da177e4SLinus Torvalds } 7551da177e4SLinus Torvalds snd_pcm_stream_proc_done(pstr); 7561da177e4SLinus Torvalds #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) 7571da177e4SLinus Torvalds for (setup = pstr->oss.setup_list; setup; setup = setupn) { 7581da177e4SLinus Torvalds setupn = setup->next; 7591da177e4SLinus Torvalds kfree(setup->task_name); 7601da177e4SLinus Torvalds kfree(setup); 7611da177e4SLinus Torvalds } 7621da177e4SLinus Torvalds #endif 7631da177e4SLinus Torvalds } 7641da177e4SLinus Torvalds 765877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm) 7661da177e4SLinus Torvalds { 767c461482cSTakashi Iwai struct snd_pcm_notify *notify; 768c461482cSTakashi Iwai 7697eaa943cSTakashi Iwai if (!pcm) 7707eaa943cSTakashi Iwai return 0; 771c461482cSTakashi Iwai list_for_each_entry(notify, &snd_pcm_notify_list, list) { 772c461482cSTakashi Iwai notify->n_unregister(pcm); 773c461482cSTakashi Iwai } 7741da177e4SLinus Torvalds if (pcm->private_free) 7751da177e4SLinus Torvalds pcm->private_free(pcm); 7761da177e4SLinus Torvalds snd_pcm_lib_preallocate_free_for_all(pcm); 7771da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 7781da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 7791da177e4SLinus Torvalds kfree(pcm); 7801da177e4SLinus Torvalds return 0; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds 783877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device) 7841da177e4SLinus Torvalds { 785877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 7861da177e4SLinus Torvalds return snd_pcm_free(pcm); 7871da177e4SLinus Torvalds } 7881da177e4SLinus Torvalds 7893bf75f9bSTakashi Iwai int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 7903bf75f9bSTakashi Iwai struct file *file, 791877211f5STakashi Iwai struct snd_pcm_substream **rsubstream) 7921da177e4SLinus Torvalds { 793877211f5STakashi Iwai struct snd_pcm_str * pstr; 794877211f5STakashi Iwai struct snd_pcm_substream *substream; 795877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 796877211f5STakashi Iwai struct snd_ctl_file *kctl; 797877211f5STakashi Iwai struct snd_card *card; 7981da177e4SLinus Torvalds int prefer_subdevice = -1; 7991da177e4SLinus Torvalds size_t size; 8001da177e4SLinus Torvalds 8017eaa943cSTakashi Iwai if (snd_BUG_ON(!pcm || !rsubstream)) 8027eaa943cSTakashi Iwai return -ENXIO; 8031da177e4SLinus Torvalds *rsubstream = NULL; 8041da177e4SLinus Torvalds pstr = &pcm->streams[stream]; 8053bf75f9bSTakashi Iwai if (pstr->substream == NULL || pstr->substream_count == 0) 8061da177e4SLinus Torvalds return -ENODEV; 8071da177e4SLinus Torvalds 8081da177e4SLinus Torvalds card = pcm->card; 809399ccdc1STakashi Iwai read_lock(&card->ctl_files_rwlock); 8109244b2c3SJohannes Berg list_for_each_entry(kctl, &card->ctl_files, list) { 8111da177e4SLinus Torvalds if (kctl->pid == current->pid) { 8121da177e4SLinus Torvalds prefer_subdevice = kctl->prefer_pcm_subdevice; 8132529bba7STakashi Iwai if (prefer_subdevice != -1) 8141da177e4SLinus Torvalds break; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds } 817399ccdc1STakashi Iwai read_unlock(&card->ctl_files_rwlock); 8181da177e4SLinus Torvalds 8191da177e4SLinus Torvalds switch (stream) { 8201da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 8211da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 8221da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { 8231da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 8241da177e4SLinus Torvalds return -EAGAIN; 8251da177e4SLinus Torvalds } 8261da177e4SLinus Torvalds } 8271da177e4SLinus Torvalds break; 8281da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 8291da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 8301da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { 8311da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 8321da177e4SLinus Torvalds return -EAGAIN; 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds break; 8361da177e4SLinus Torvalds default: 8371da177e4SLinus Torvalds return -EINVAL; 8381da177e4SLinus Torvalds } 8391da177e4SLinus Torvalds 8400df63e44STakashi Iwai if (file->f_flags & O_APPEND) { 8410df63e44STakashi Iwai if (prefer_subdevice < 0) { 8420df63e44STakashi Iwai if (pstr->substream_count > 1) 8430df63e44STakashi Iwai return -EINVAL; /* must be unique */ 8440df63e44STakashi Iwai substream = pstr->substream; 8450df63e44STakashi Iwai } else { 8460df63e44STakashi Iwai for (substream = pstr->substream; substream; 8470df63e44STakashi Iwai substream = substream->next) 8480df63e44STakashi Iwai if (substream->number == prefer_subdevice) 8490df63e44STakashi Iwai break; 8500df63e44STakashi Iwai } 8510df63e44STakashi Iwai if (! substream) 8520df63e44STakashi Iwai return -ENODEV; 8530df63e44STakashi Iwai if (! SUBSTREAM_BUSY(substream)) 8540df63e44STakashi Iwai return -EBADFD; 8550df63e44STakashi Iwai substream->ref_count++; 8560df63e44STakashi Iwai *rsubstream = substream; 8570df63e44STakashi Iwai return 0; 8580df63e44STakashi Iwai } 8590df63e44STakashi Iwai 8601da177e4SLinus Torvalds if (prefer_subdevice >= 0) { 8611da177e4SLinus Torvalds for (substream = pstr->substream; substream; substream = substream->next) 8621da177e4SLinus Torvalds if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) 8631da177e4SLinus Torvalds goto __ok; 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds for (substream = pstr->substream; substream; substream = substream->next) 8661da177e4SLinus Torvalds if (!SUBSTREAM_BUSY(substream)) 8671da177e4SLinus Torvalds break; 8681da177e4SLinus Torvalds __ok: 8691da177e4SLinus Torvalds if (substream == NULL) 8701da177e4SLinus Torvalds return -EAGAIN; 8711da177e4SLinus Torvalds 872ca2c0966STakashi Iwai runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 8731da177e4SLinus Torvalds if (runtime == NULL) 8741da177e4SLinus Torvalds return -ENOMEM; 8751da177e4SLinus Torvalds 876877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 8771da177e4SLinus Torvalds runtime->status = snd_malloc_pages(size, GFP_KERNEL); 8781da177e4SLinus Torvalds if (runtime->status == NULL) { 8791da177e4SLinus Torvalds kfree(runtime); 8801da177e4SLinus Torvalds return -ENOMEM; 8811da177e4SLinus Torvalds } 8821da177e4SLinus Torvalds memset((void*)runtime->status, 0, size); 8831da177e4SLinus Torvalds 884877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 8851da177e4SLinus Torvalds runtime->control = snd_malloc_pages(size, GFP_KERNEL); 8861da177e4SLinus Torvalds if (runtime->control == NULL) { 887877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 888877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 8891da177e4SLinus Torvalds kfree(runtime); 8901da177e4SLinus Torvalds return -ENOMEM; 8911da177e4SLinus Torvalds } 8921da177e4SLinus Torvalds memset((void*)runtime->control, 0, size); 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds init_waitqueue_head(&runtime->sleep); 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds runtime->status->state = SNDRV_PCM_STATE_OPEN; 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds substream->runtime = runtime; 8991da177e4SLinus Torvalds substream->private_data = pcm->private_data; 9000df63e44STakashi Iwai substream->ref_count = 1; 9010df63e44STakashi Iwai substream->f_flags = file->f_flags; 9021da177e4SLinus Torvalds pstr->substream_opened++; 9031da177e4SLinus Torvalds *rsubstream = substream; 9041da177e4SLinus Torvalds return 0; 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds 9073bf75f9bSTakashi Iwai void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 9081da177e4SLinus Torvalds { 909877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 9100df63e44STakashi Iwai 9117eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 9127eaa943cSTakashi Iwai return; 9131da177e4SLinus Torvalds runtime = substream->runtime; 9141da177e4SLinus Torvalds if (runtime->private_free != NULL) 9151da177e4SLinus Torvalds runtime->private_free(runtime); 916877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 917877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 918877211f5STakashi Iwai snd_free_pages((void*)runtime->control, 919877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 9201da177e4SLinus Torvalds kfree(runtime->hw_constraints.rules); 9211da177e4SLinus Torvalds kfree(runtime); 9221da177e4SLinus Torvalds substream->runtime = NULL; 9231da177e4SLinus Torvalds substream->pstr->substream_opened--; 9241da177e4SLinus Torvalds } 9251da177e4SLinus Torvalds 926d80f19faSGreg Kroah-Hartman static ssize_t show_pcm_class(struct device *dev, 927d80f19faSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 9289d19f48cSTakashi Iwai { 9299d19f48cSTakashi Iwai struct snd_pcm *pcm; 9309d19f48cSTakashi Iwai const char *str; 9319d19f48cSTakashi Iwai static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 9329d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_GENERIC] = "generic", 9339d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MULTI] = "multi", 9349d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MODEM] = "modem", 9359d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 9369d19f48cSTakashi Iwai }; 9379d19f48cSTakashi Iwai 938d80f19faSGreg Kroah-Hartman if (! (pcm = dev_get_drvdata(dev)) || 9399d19f48cSTakashi Iwai pcm->dev_class > SNDRV_PCM_CLASS_LAST) 9409d19f48cSTakashi Iwai str = "none"; 9419d19f48cSTakashi Iwai else 9429d19f48cSTakashi Iwai str = strs[pcm->dev_class]; 9439d19f48cSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", str); 9449d19f48cSTakashi Iwai } 9459d19f48cSTakashi Iwai 946d80f19faSGreg Kroah-Hartman static struct device_attribute pcm_attrs = 9479d19f48cSTakashi Iwai __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); 9489d19f48cSTakashi Iwai 949877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device) 9501da177e4SLinus Torvalds { 951f87135f5SClemens Ladisch int cidx, err; 952877211f5STakashi Iwai struct snd_pcm_substream *substream; 9539244b2c3SJohannes Berg struct snd_pcm_notify *notify; 9541da177e4SLinus Torvalds char str[16]; 955877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 956c78085fcSJohannes Berg struct device *dev; 9571da177e4SLinus Torvalds 9587eaa943cSTakashi Iwai if (snd_BUG_ON(!pcm || !device)) 9597eaa943cSTakashi Iwai return -ENXIO; 9601a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 961f90c06a2SPawel MOLL err = snd_pcm_add(pcm); 962f90c06a2SPawel MOLL if (err) { 9631a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 964f90c06a2SPawel MOLL return err; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 9671da177e4SLinus Torvalds int devtype = -1; 9681da177e4SLinus Torvalds if (pcm->streams[cidx].substream == NULL) 9691da177e4SLinus Torvalds continue; 9701da177e4SLinus Torvalds switch (cidx) { 9711da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 9721da177e4SLinus Torvalds sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); 9731da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 9741da177e4SLinus Torvalds break; 9751da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 9761da177e4SLinus Torvalds sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); 9771da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 9781da177e4SLinus Torvalds break; 9791da177e4SLinus Torvalds } 980c78085fcSJohannes Berg /* device pointer to use, pcm->dev takes precedence if 981c78085fcSJohannes Berg * it is assigned, otherwise fall back to card's device 982c78085fcSJohannes Berg * if possible */ 983c78085fcSJohannes Berg dev = pcm->dev; 984c78085fcSJohannes Berg if (!dev) 985c2902c8aSTakashi Iwai dev = snd_card_get_device_link(pcm->card); 986c78085fcSJohannes Berg /* register pcm */ 987c78085fcSJohannes Berg err = snd_register_device_for_dev(devtype, pcm->card, 9882af677fcSClemens Ladisch pcm->device, 989f87135f5SClemens Ladisch &snd_pcm_f_ops[cidx], 990c78085fcSJohannes Berg pcm, str, dev); 991c78085fcSJohannes Berg if (err < 0) { 992f87135f5SClemens Ladisch list_del(&pcm->list); 9931a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 9941da177e4SLinus Torvalds return err; 9951da177e4SLinus Torvalds } 9969d19f48cSTakashi Iwai snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, 9979d19f48cSTakashi Iwai &pcm_attrs); 9981da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 9991da177e4SLinus Torvalds snd_pcm_timer_init(substream); 10001da177e4SLinus Torvalds } 10019244b2c3SJohannes Berg 10029244b2c3SJohannes Berg list_for_each_entry(notify, &snd_pcm_notify_list, list) 10031da177e4SLinus Torvalds notify->n_register(pcm); 10049244b2c3SJohannes Berg 10051a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10061da177e4SLinus Torvalds return 0; 10071da177e4SLinus Torvalds } 10081da177e4SLinus Torvalds 1009877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device) 10101da177e4SLinus Torvalds { 1011877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 1012c461482cSTakashi Iwai struct snd_pcm_notify *notify; 1013877211f5STakashi Iwai struct snd_pcm_substream *substream; 1014c461482cSTakashi Iwai int cidx, devtype; 10151da177e4SLinus Torvalds 10161a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1017c461482cSTakashi Iwai if (list_empty(&pcm->list)) 1018c461482cSTakashi Iwai goto unlock; 1019c461482cSTakashi Iwai 1020f87135f5SClemens Ladisch list_del_init(&pcm->list); 10211da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) 10221da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 10231da177e4SLinus Torvalds if (substream->runtime) 10241da177e4SLinus Torvalds substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 1025c461482cSTakashi Iwai list_for_each_entry(notify, &snd_pcm_notify_list, list) { 10261da177e4SLinus Torvalds notify->n_disconnect(pcm); 10271da177e4SLinus Torvalds } 10281da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 10291da177e4SLinus Torvalds devtype = -1; 10301da177e4SLinus Torvalds switch (cidx) { 10311da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 10321da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 10331da177e4SLinus Torvalds break; 10341da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 10351da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 10361da177e4SLinus Torvalds break; 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds snd_unregister_device(devtype, pcm->card, pcm->device); 10391da177e4SLinus Torvalds } 1040c461482cSTakashi Iwai unlock: 10411a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1042c461482cSTakashi Iwai return 0; 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds 1045877211f5STakashi Iwai int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 10461da177e4SLinus Torvalds { 10479244b2c3SJohannes Berg struct snd_pcm *pcm; 10481da177e4SLinus Torvalds 10497eaa943cSTakashi Iwai if (snd_BUG_ON(!notify || 10507eaa943cSTakashi Iwai !notify->n_register || 10517eaa943cSTakashi Iwai !notify->n_unregister || 10527eaa943cSTakashi Iwai !notify->n_disconnect)) 10537eaa943cSTakashi Iwai return -EINVAL; 10541a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 10551da177e4SLinus Torvalds if (nfree) { 10561da177e4SLinus Torvalds list_del(¬ify->list); 10579244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 10589244b2c3SJohannes Berg notify->n_unregister(pcm); 10591da177e4SLinus Torvalds } else { 10601da177e4SLinus Torvalds list_add_tail(¬ify->list, &snd_pcm_notify_list); 10619244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 10629244b2c3SJohannes Berg notify->n_register(pcm); 10631da177e4SLinus Torvalds } 10641a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10651da177e4SLinus Torvalds return 0; 10661da177e4SLinus Torvalds } 10671da177e4SLinus Torvalds 1068e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_notify); 1069e88e8ae6STakashi Iwai 1070e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 10711da177e4SLinus Torvalds /* 10721da177e4SLinus Torvalds * Info interface 10731da177e4SLinus Torvalds */ 10741da177e4SLinus Torvalds 1075877211f5STakashi Iwai static void snd_pcm_proc_read(struct snd_info_entry *entry, 1076877211f5STakashi Iwai struct snd_info_buffer *buffer) 10771da177e4SLinus Torvalds { 1078877211f5STakashi Iwai struct snd_pcm *pcm; 10791da177e4SLinus Torvalds 10801a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 10819244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 1082f87135f5SClemens Ladisch snd_iprintf(buffer, "%02i-%02i: %s : %s", 1083f87135f5SClemens Ladisch pcm->card->number, pcm->device, pcm->id, pcm->name); 10841da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1085877211f5STakashi Iwai snd_iprintf(buffer, " : playback %i", 1086877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 10871da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 1088877211f5STakashi Iwai snd_iprintf(buffer, " : capture %i", 1089877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 10901da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 10911da177e4SLinus Torvalds } 10921a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds 10956581f4e7STakashi Iwai static struct snd_info_entry *snd_pcm_proc_entry; 10961da177e4SLinus Torvalds 1097e28563ccSTakashi Iwai static void snd_pcm_proc_init(void) 10981da177e4SLinus Torvalds { 1099877211f5STakashi Iwai struct snd_info_entry *entry; 11001da177e4SLinus Torvalds 11011da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { 1102bf850204STakashi Iwai snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 11031da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 11041da177e4SLinus Torvalds snd_info_free_entry(entry); 11051da177e4SLinus Torvalds entry = NULL; 11061da177e4SLinus Torvalds } 11071da177e4SLinus Torvalds } 11081da177e4SLinus Torvalds snd_pcm_proc_entry = entry; 1109e28563ccSTakashi Iwai } 1110e28563ccSTakashi Iwai 1111e28563ccSTakashi Iwai static void snd_pcm_proc_done(void) 1112e28563ccSTakashi Iwai { 1113746d4a02STakashi Iwai snd_info_free_entry(snd_pcm_proc_entry); 1114e28563ccSTakashi Iwai } 1115e28563ccSTakashi Iwai 1116e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 1117e28563ccSTakashi Iwai #define snd_pcm_proc_init() 1118e28563ccSTakashi Iwai #define snd_pcm_proc_done() 1119e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 1120e28563ccSTakashi Iwai 1121e28563ccSTakashi Iwai 1122e28563ccSTakashi Iwai /* 1123e28563ccSTakashi Iwai * ENTRY functions 1124e28563ccSTakashi Iwai */ 1125e28563ccSTakashi Iwai 1126e28563ccSTakashi Iwai static int __init alsa_pcm_init(void) 1127e28563ccSTakashi Iwai { 1128e28563ccSTakashi Iwai snd_ctl_register_ioctl(snd_pcm_control_ioctl); 1129e28563ccSTakashi Iwai snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 1130e28563ccSTakashi Iwai snd_pcm_proc_init(); 11311da177e4SLinus Torvalds return 0; 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds 11341da177e4SLinus Torvalds static void __exit alsa_pcm_exit(void) 11351da177e4SLinus Torvalds { 11361da177e4SLinus Torvalds snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 11371da177e4SLinus Torvalds snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 1138e28563ccSTakashi Iwai snd_pcm_proc_done(); 11391da177e4SLinus Torvalds } 11401da177e4SLinus Torvalds 11411da177e4SLinus Torvalds module_init(alsa_pcm_init) 11421da177e4SLinus Torvalds module_exit(alsa_pcm_exit) 1143