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> 24da155d5bSPaul Gortmaker #include <linux/module.h> 251da177e4SLinus Torvalds #include <linux/time.h> 261a60d4c5SIngo Molnar #include <linux/mutex.h> 2751990e82SPaul Gortmaker #include <linux/device.h> 281da177e4SLinus Torvalds #include <sound/core.h> 291da177e4SLinus Torvalds #include <sound/minors.h> 301da177e4SLinus Torvalds #include <sound/pcm.h> 311da177e4SLinus Torvalds #include <sound/control.h> 321da177e4SLinus Torvalds #include <sound/info.h> 331da177e4SLinus Torvalds 34c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); 351da177e4SLinus Torvalds MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 361da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 371da177e4SLinus Torvalds 38f87135f5SClemens Ladisch static LIST_HEAD(snd_pcm_devices); 391da177e4SLinus Torvalds static LIST_HEAD(snd_pcm_notify_list); 401a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 411da177e4SLinus Torvalds 42877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm); 43877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device); 44877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device); 45877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device); 461da177e4SLinus Torvalds 47f90c06a2SPawel MOLL static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) 48f87135f5SClemens Ladisch { 49f87135f5SClemens Ladisch struct snd_pcm *pcm; 50f87135f5SClemens Ladisch 519244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 52a4461f41SRussell King if (pcm->internal) 53a4461f41SRussell King continue; 54f87135f5SClemens Ladisch if (pcm->card == card && pcm->device == device) 55f87135f5SClemens Ladisch return pcm; 56f87135f5SClemens Ladisch } 57f87135f5SClemens Ladisch return NULL; 58f87135f5SClemens Ladisch } 59f87135f5SClemens Ladisch 60f90c06a2SPawel MOLL static int snd_pcm_next(struct snd_card *card, int device) 61f90c06a2SPawel MOLL { 62f90c06a2SPawel MOLL struct snd_pcm *pcm; 63f90c06a2SPawel MOLL 64f90c06a2SPawel MOLL list_for_each_entry(pcm, &snd_pcm_devices, list) { 65a4461f41SRussell King if (pcm->internal) 66a4461f41SRussell King continue; 67f90c06a2SPawel MOLL if (pcm->card == card && pcm->device > device) 68f90c06a2SPawel MOLL return pcm->device; 69f90c06a2SPawel MOLL else if (pcm->card->number > card->number) 70f90c06a2SPawel MOLL return -1; 71f90c06a2SPawel MOLL } 72f90c06a2SPawel MOLL return -1; 73f90c06a2SPawel MOLL } 74f90c06a2SPawel MOLL 75f90c06a2SPawel MOLL static int snd_pcm_add(struct snd_pcm *newpcm) 76f90c06a2SPawel MOLL { 77f90c06a2SPawel MOLL struct snd_pcm *pcm; 78f90c06a2SPawel MOLL 79f90c06a2SPawel MOLL list_for_each_entry(pcm, &snd_pcm_devices, list) { 80f90c06a2SPawel MOLL if (pcm->card == newpcm->card && pcm->device == newpcm->device) 81f90c06a2SPawel MOLL return -EBUSY; 82f90c06a2SPawel MOLL if (pcm->card->number > newpcm->card->number || 83f90c06a2SPawel MOLL (pcm->card == newpcm->card && 84f90c06a2SPawel MOLL pcm->device > newpcm->device)) { 85f90c06a2SPawel MOLL list_add(&newpcm->list, pcm->list.prev); 86f90c06a2SPawel MOLL return 0; 87f90c06a2SPawel MOLL } 88f90c06a2SPawel MOLL } 89f90c06a2SPawel MOLL list_add_tail(&newpcm->list, &snd_pcm_devices); 90f90c06a2SPawel MOLL return 0; 91f90c06a2SPawel MOLL } 92f90c06a2SPawel MOLL 93877211f5STakashi Iwai static int snd_pcm_control_ioctl(struct snd_card *card, 94877211f5STakashi Iwai struct snd_ctl_file *control, 951da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 961da177e4SLinus Torvalds { 971da177e4SLinus Torvalds switch (cmd) { 981da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 991da177e4SLinus Torvalds { 1001da177e4SLinus Torvalds int device; 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds if (get_user(device, (int __user *)arg)) 1031da177e4SLinus Torvalds return -EFAULT; 1041a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 105f90c06a2SPawel MOLL device = snd_pcm_next(card, device); 1061a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1071da177e4SLinus Torvalds if (put_user(device, (int __user *)arg)) 1081da177e4SLinus Torvalds return -EFAULT; 1091da177e4SLinus Torvalds return 0; 1101da177e4SLinus Torvalds } 1111da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_INFO: 1121da177e4SLinus Torvalds { 113877211f5STakashi Iwai struct snd_pcm_info __user *info; 1141da177e4SLinus Torvalds unsigned int device, subdevice; 115877211f5STakashi Iwai int stream; 116877211f5STakashi Iwai struct snd_pcm *pcm; 117877211f5STakashi Iwai struct snd_pcm_str *pstr; 118877211f5STakashi Iwai struct snd_pcm_substream *substream; 119f87135f5SClemens Ladisch int err; 120f87135f5SClemens Ladisch 121877211f5STakashi Iwai info = (struct snd_pcm_info __user *)arg; 1221da177e4SLinus Torvalds if (get_user(device, &info->device)) 1231da177e4SLinus Torvalds return -EFAULT; 1241da177e4SLinus Torvalds if (get_user(stream, &info->stream)) 1251da177e4SLinus Torvalds return -EFAULT; 1261da177e4SLinus Torvalds if (stream < 0 || stream > 1) 1271da177e4SLinus Torvalds return -EINVAL; 1281da177e4SLinus Torvalds if (get_user(subdevice, &info->subdevice)) 1291da177e4SLinus Torvalds return -EFAULT; 1301a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 131f90c06a2SPawel MOLL pcm = snd_pcm_get(card, device); 132f87135f5SClemens Ladisch if (pcm == NULL) { 133f87135f5SClemens Ladisch err = -ENXIO; 134f87135f5SClemens Ladisch goto _error; 135f87135f5SClemens Ladisch } 136f87135f5SClemens Ladisch pstr = &pcm->streams[stream]; 137f87135f5SClemens Ladisch if (pstr->substream_count == 0) { 138f87135f5SClemens Ladisch err = -ENOENT; 139f87135f5SClemens Ladisch goto _error; 140f87135f5SClemens Ladisch } 141f87135f5SClemens Ladisch if (subdevice >= pstr->substream_count) { 142f87135f5SClemens Ladisch err = -ENXIO; 143f87135f5SClemens Ladisch goto _error; 144f87135f5SClemens Ladisch } 145f87135f5SClemens Ladisch for (substream = pstr->substream; substream; 146f87135f5SClemens Ladisch substream = substream->next) 1471da177e4SLinus Torvalds if (substream->number == (int)subdevice) 1481da177e4SLinus Torvalds break; 149f87135f5SClemens Ladisch if (substream == NULL) { 150f87135f5SClemens Ladisch err = -ENXIO; 151f87135f5SClemens Ladisch goto _error; 152f87135f5SClemens Ladisch } 153f87135f5SClemens Ladisch err = snd_pcm_info_user(substream, info); 154f87135f5SClemens Ladisch _error: 1551a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 156f87135f5SClemens Ladisch return err; 1571da177e4SLinus Torvalds } 1581da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 1591da177e4SLinus Torvalds { 1601da177e4SLinus Torvalds int val; 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds if (get_user(val, (int __user *)arg)) 1631da177e4SLinus Torvalds return -EFAULT; 1641da177e4SLinus Torvalds control->prefer_pcm_subdevice = val; 1651da177e4SLinus Torvalds return 0; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds } 1681da177e4SLinus Torvalds return -ENOIOCTLCMD; 1691da177e4SLinus Torvalds } 17021a3479aSJaroslav Kysela 1711da177e4SLinus Torvalds #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds static char *snd_pcm_format_names[] = { 1741da177e4SLinus Torvalds FORMAT(S8), 1751da177e4SLinus Torvalds FORMAT(U8), 1761da177e4SLinus Torvalds FORMAT(S16_LE), 1771da177e4SLinus Torvalds FORMAT(S16_BE), 1781da177e4SLinus Torvalds FORMAT(U16_LE), 1791da177e4SLinus Torvalds FORMAT(U16_BE), 1801da177e4SLinus Torvalds FORMAT(S24_LE), 1811da177e4SLinus Torvalds FORMAT(S24_BE), 1821da177e4SLinus Torvalds FORMAT(U24_LE), 1831da177e4SLinus Torvalds FORMAT(U24_BE), 1841da177e4SLinus Torvalds FORMAT(S32_LE), 1851da177e4SLinus Torvalds FORMAT(S32_BE), 1861da177e4SLinus Torvalds FORMAT(U32_LE), 1871da177e4SLinus Torvalds FORMAT(U32_BE), 1881da177e4SLinus Torvalds FORMAT(FLOAT_LE), 1891da177e4SLinus Torvalds FORMAT(FLOAT_BE), 1901da177e4SLinus Torvalds FORMAT(FLOAT64_LE), 1911da177e4SLinus Torvalds FORMAT(FLOAT64_BE), 1921da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_LE), 1931da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_BE), 1941da177e4SLinus Torvalds FORMAT(MU_LAW), 1951da177e4SLinus Torvalds FORMAT(A_LAW), 1961da177e4SLinus Torvalds FORMAT(IMA_ADPCM), 1971da177e4SLinus Torvalds FORMAT(MPEG), 1981da177e4SLinus Torvalds FORMAT(GSM), 1991da177e4SLinus Torvalds FORMAT(SPECIAL), 2001da177e4SLinus Torvalds FORMAT(S24_3LE), 2011da177e4SLinus Torvalds FORMAT(S24_3BE), 2021da177e4SLinus Torvalds FORMAT(U24_3LE), 2031da177e4SLinus Torvalds FORMAT(U24_3BE), 2041da177e4SLinus Torvalds FORMAT(S20_3LE), 2051da177e4SLinus Torvalds FORMAT(S20_3BE), 2061da177e4SLinus Torvalds FORMAT(U20_3LE), 2071da177e4SLinus Torvalds FORMAT(U20_3BE), 2081da177e4SLinus Torvalds FORMAT(S18_3LE), 2091da177e4SLinus Torvalds FORMAT(S18_3BE), 2101da177e4SLinus Torvalds FORMAT(U18_3LE), 2111da177e4SLinus Torvalds FORMAT(U18_3BE), 2127a28826aSDan Carpenter FORMAT(G723_24), 2137a28826aSDan Carpenter FORMAT(G723_24_1B), 2147a28826aSDan Carpenter FORMAT(G723_40), 2157a28826aSDan Carpenter FORMAT(G723_40_1B), 216ef7a4f97SDaniel Mack FORMAT(DSD_U8), 217ef7a4f97SDaniel Mack FORMAT(DSD_U16_LE), 2181da177e4SLinus Torvalds }; 2191da177e4SLinus Torvalds 2206e5265ecSTakashi Iwai const char *snd_pcm_format_name(snd_pcm_format_t format) 221e28563ccSTakashi Iwai { 222fea952e5SClemens Ladisch if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) 2237a28826aSDan Carpenter return "Unknown"; 224fea952e5SClemens Ladisch return snd_pcm_format_names[(__force unsigned int)format]; 225e28563ccSTakashi Iwai } 2266e5265ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_format_name); 2276e5265ecSTakashi Iwai 2286e5265ecSTakashi Iwai #ifdef CONFIG_SND_VERBOSE_PROCFS 2296e5265ecSTakashi Iwai 2306e5265ecSTakashi Iwai #define STATE(v) [SNDRV_PCM_STATE_##v] = #v 2316e5265ecSTakashi Iwai #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v 2326e5265ecSTakashi Iwai #define READY(v) [SNDRV_PCM_READY_##v] = #v 2336e5265ecSTakashi Iwai #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v 2346e5265ecSTakashi Iwai #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v 2356e5265ecSTakashi Iwai #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v 2366e5265ecSTakashi Iwai #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v 2376e5265ecSTakashi Iwai #define START(v) [SNDRV_PCM_START_##v] = #v 2386e5265ecSTakashi Iwai #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 239e28563ccSTakashi Iwai 240e28563ccSTakashi Iwai static char *snd_pcm_stream_names[] = { 241e28563ccSTakashi Iwai STREAM(PLAYBACK), 242e28563ccSTakashi Iwai STREAM(CAPTURE), 243e28563ccSTakashi Iwai }; 244e28563ccSTakashi Iwai 245e28563ccSTakashi Iwai static char *snd_pcm_state_names[] = { 246e28563ccSTakashi Iwai STATE(OPEN), 247e28563ccSTakashi Iwai STATE(SETUP), 248e28563ccSTakashi Iwai STATE(PREPARED), 249e28563ccSTakashi Iwai STATE(RUNNING), 250e28563ccSTakashi Iwai STATE(XRUN), 251e28563ccSTakashi Iwai STATE(DRAINING), 252e28563ccSTakashi Iwai STATE(PAUSED), 253e28563ccSTakashi Iwai STATE(SUSPENDED), 254e28563ccSTakashi Iwai }; 255e28563ccSTakashi Iwai 256e28563ccSTakashi Iwai static char *snd_pcm_access_names[] = { 257e28563ccSTakashi Iwai ACCESS(MMAP_INTERLEAVED), 258e28563ccSTakashi Iwai ACCESS(MMAP_NONINTERLEAVED), 259e28563ccSTakashi Iwai ACCESS(MMAP_COMPLEX), 260e28563ccSTakashi Iwai ACCESS(RW_INTERLEAVED), 261e28563ccSTakashi Iwai ACCESS(RW_NONINTERLEAVED), 262e28563ccSTakashi Iwai }; 263e28563ccSTakashi Iwai 2641da177e4SLinus Torvalds static char *snd_pcm_subformat_names[] = { 2651da177e4SLinus Torvalds SUBFORMAT(STD), 2661da177e4SLinus Torvalds }; 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds static char *snd_pcm_tstamp_mode_names[] = { 2691da177e4SLinus Torvalds TSTAMP(NONE), 2708c121586SJaroslav Kysela TSTAMP(ENABLE), 2711da177e4SLinus Torvalds }; 2721da177e4SLinus Torvalds 273877211f5STakashi Iwai static const char *snd_pcm_stream_name(int stream) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds return snd_pcm_stream_names[stream]; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds static const char *snd_pcm_access_name(snd_pcm_access_t access) 2791da177e4SLinus Torvalds { 280fea952e5SClemens Ladisch return snd_pcm_access_names[(__force int)access]; 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) 2841da177e4SLinus Torvalds { 285fea952e5SClemens Ladisch return snd_pcm_subformat_names[(__force int)subformat]; 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds 288877211f5STakashi Iwai static const char *snd_pcm_tstamp_mode_name(int mode) 2891da177e4SLinus Torvalds { 2901da177e4SLinus Torvalds return snd_pcm_tstamp_mode_names[mode]; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds static const char *snd_pcm_state_name(snd_pcm_state_t state) 2941da177e4SLinus Torvalds { 295fea952e5SClemens Ladisch return snd_pcm_state_names[(__force int)state]; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2988eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 2991da177e4SLinus Torvalds #include <linux/soundcard.h> 3001a60d4c5SIngo Molnar 3011da177e4SLinus Torvalds static const char *snd_pcm_oss_format_name(int format) 3021da177e4SLinus Torvalds { 3031da177e4SLinus Torvalds switch (format) { 3041da177e4SLinus Torvalds case AFMT_MU_LAW: 3051da177e4SLinus Torvalds return "MU_LAW"; 3061da177e4SLinus Torvalds case AFMT_A_LAW: 3071da177e4SLinus Torvalds return "A_LAW"; 3081da177e4SLinus Torvalds case AFMT_IMA_ADPCM: 3091da177e4SLinus Torvalds return "IMA_ADPCM"; 3101da177e4SLinus Torvalds case AFMT_U8: 3111da177e4SLinus Torvalds return "U8"; 3121da177e4SLinus Torvalds case AFMT_S16_LE: 3131da177e4SLinus Torvalds return "S16_LE"; 3141da177e4SLinus Torvalds case AFMT_S16_BE: 3151da177e4SLinus Torvalds return "S16_BE"; 3161da177e4SLinus Torvalds case AFMT_S8: 3171da177e4SLinus Torvalds return "S8"; 3181da177e4SLinus Torvalds case AFMT_U16_LE: 3191da177e4SLinus Torvalds return "U16_LE"; 3201da177e4SLinus Torvalds case AFMT_U16_BE: 3211da177e4SLinus Torvalds return "U16_BE"; 3221da177e4SLinus Torvalds case AFMT_MPEG: 3231da177e4SLinus Torvalds return "MPEG"; 3241da177e4SLinus Torvalds default: 3251da177e4SLinus Torvalds return "unknown"; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds #endif 3291da177e4SLinus Torvalds 330877211f5STakashi Iwai static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, 331877211f5STakashi Iwai struct snd_info_buffer *buffer) 3321da177e4SLinus Torvalds { 333877211f5STakashi Iwai struct snd_pcm_info *info; 3341da177e4SLinus Torvalds int err; 3351da177e4SLinus Torvalds 3367c22f1aaSTakashi Iwai if (! substream) 3377c22f1aaSTakashi Iwai return; 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds info = kmalloc(sizeof(*info), GFP_KERNEL); 3401da177e4SLinus Torvalds if (! info) { 34109e56df8STakashi Iwai pcm_dbg(substream->pcm, 34209e56df8STakashi Iwai "snd_pcm_proc_info_read: cannot malloc\n"); 3431da177e4SLinus Torvalds return; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds err = snd_pcm_info(substream, info); 3471da177e4SLinus Torvalds if (err < 0) { 3481da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 3491da177e4SLinus Torvalds kfree(info); 3501da177e4SLinus Torvalds return; 3511da177e4SLinus Torvalds } 3521da177e4SLinus Torvalds snd_iprintf(buffer, "card: %d\n", info->card); 3531da177e4SLinus Torvalds snd_iprintf(buffer, "device: %d\n", info->device); 3541da177e4SLinus Torvalds snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); 3551da177e4SLinus Torvalds snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); 3561da177e4SLinus Torvalds snd_iprintf(buffer, "id: %s\n", info->id); 3571da177e4SLinus Torvalds snd_iprintf(buffer, "name: %s\n", info->name); 3581da177e4SLinus Torvalds snd_iprintf(buffer, "subname: %s\n", info->subname); 3591da177e4SLinus Torvalds snd_iprintf(buffer, "class: %d\n", info->dev_class); 3601da177e4SLinus Torvalds snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); 3611da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); 3621da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); 3631da177e4SLinus Torvalds kfree(info); 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 366877211f5STakashi Iwai static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, 367877211f5STakashi Iwai struct snd_info_buffer *buffer) 3681da177e4SLinus Torvalds { 369877211f5STakashi Iwai snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, 370877211f5STakashi Iwai buffer); 3711da177e4SLinus Torvalds } 3721da177e4SLinus Torvalds 373877211f5STakashi Iwai static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, 374877211f5STakashi Iwai struct snd_info_buffer *buffer) 3751da177e4SLinus Torvalds { 3769fe856e4SJoe Perches snd_pcm_proc_info_read(entry->private_data, buffer); 3771da177e4SLinus Torvalds } 3781da177e4SLinus Torvalds 379877211f5STakashi Iwai static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, 380877211f5STakashi Iwai struct snd_info_buffer *buffer) 3811da177e4SLinus Torvalds { 382877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 383901d46d5STakashi Iwai struct snd_pcm_runtime *runtime; 384901d46d5STakashi Iwai 385901d46d5STakashi Iwai mutex_lock(&substream->pcm->open_mutex); 386901d46d5STakashi Iwai runtime = substream->runtime; 3871da177e4SLinus Torvalds if (!runtime) { 3881da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 389901d46d5STakashi Iwai goto unlock; 3901da177e4SLinus Torvalds } 3911da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 3921da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 393901d46d5STakashi Iwai goto unlock; 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); 3961da177e4SLinus Torvalds snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); 3971da177e4SLinus Torvalds snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); 3981da177e4SLinus Torvalds snd_iprintf(buffer, "channels: %u\n", runtime->channels); 3991da177e4SLinus Torvalds snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 4001da177e4SLinus Torvalds snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); 4011da177e4SLinus Torvalds snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); 4028eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 4031da177e4SLinus Torvalds if (substream->oss.oss) { 4041da177e4SLinus Torvalds snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); 4051da177e4SLinus Torvalds snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); 4061da177e4SLinus Torvalds snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); 4071da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); 4081da177e4SLinus Torvalds snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); 4091da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); 4101da177e4SLinus Torvalds } 4111da177e4SLinus Torvalds #endif 412901d46d5STakashi Iwai unlock: 413901d46d5STakashi Iwai mutex_unlock(&substream->pcm->open_mutex); 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds 416877211f5STakashi Iwai static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, 417877211f5STakashi Iwai struct snd_info_buffer *buffer) 4181da177e4SLinus Torvalds { 419877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 420901d46d5STakashi Iwai struct snd_pcm_runtime *runtime; 421901d46d5STakashi Iwai 422901d46d5STakashi Iwai mutex_lock(&substream->pcm->open_mutex); 423901d46d5STakashi Iwai runtime = substream->runtime; 4241da177e4SLinus Torvalds if (!runtime) { 4251da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 426901d46d5STakashi Iwai goto unlock; 4271da177e4SLinus Torvalds } 4281da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 4291da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 430901d46d5STakashi Iwai goto unlock; 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); 4331da177e4SLinus Torvalds snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); 4341da177e4SLinus Torvalds snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); 4351da177e4SLinus Torvalds snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); 4361da177e4SLinus Torvalds snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); 4371da177e4SLinus Torvalds snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); 4381da177e4SLinus Torvalds snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); 4391da177e4SLinus Torvalds snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); 440901d46d5STakashi Iwai unlock: 441901d46d5STakashi Iwai mutex_unlock(&substream->pcm->open_mutex); 4421da177e4SLinus Torvalds } 4431da177e4SLinus Torvalds 444877211f5STakashi Iwai static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, 445877211f5STakashi Iwai struct snd_info_buffer *buffer) 4461da177e4SLinus Torvalds { 447877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 448901d46d5STakashi Iwai struct snd_pcm_runtime *runtime; 449877211f5STakashi Iwai struct snd_pcm_status status; 4501da177e4SLinus Torvalds int err; 451901d46d5STakashi Iwai 452901d46d5STakashi Iwai mutex_lock(&substream->pcm->open_mutex); 453901d46d5STakashi Iwai runtime = substream->runtime; 4541da177e4SLinus Torvalds if (!runtime) { 4551da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 456901d46d5STakashi Iwai goto unlock; 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 4591da177e4SLinus Torvalds err = snd_pcm_status(substream, &status); 4601da177e4SLinus Torvalds if (err < 0) { 4611da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 462901d46d5STakashi Iwai goto unlock; 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 465e7373b70SClemens Ladisch snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); 4661da177e4SLinus Torvalds snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", 4671da177e4SLinus Torvalds status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); 4681da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp : %ld.%09ld\n", 4691da177e4SLinus Torvalds status.tstamp.tv_sec, status.tstamp.tv_nsec); 4701da177e4SLinus Torvalds snd_iprintf(buffer, "delay : %ld\n", status.delay); 4711da177e4SLinus Torvalds snd_iprintf(buffer, "avail : %ld\n", status.avail); 4721da177e4SLinus Torvalds snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); 4731da177e4SLinus Torvalds snd_iprintf(buffer, "-----\n"); 4741da177e4SLinus Torvalds snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); 4751da177e4SLinus Torvalds snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); 476901d46d5STakashi Iwai unlock: 477901d46d5STakashi Iwai mutex_unlock(&substream->pcm->open_mutex); 4781da177e4SLinus Torvalds } 4791da177e4SLinus Torvalds 48061fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 481877211f5STakashi Iwai static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 482877211f5STakashi Iwai struct snd_info_buffer *buffer) 4831da177e4SLinus Torvalds { 484877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 4851da177e4SLinus Torvalds snd_iprintf(buffer, "%d\n", pstr->xrun_debug); 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 488877211f5STakashi Iwai static void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, 489877211f5STakashi Iwai struct snd_info_buffer *buffer) 4901da177e4SLinus Torvalds { 491877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 4921da177e4SLinus Torvalds char line[64]; 4931da177e4SLinus Torvalds if (!snd_info_get_line(buffer, line, sizeof(line))) 4941da177e4SLinus Torvalds pstr->xrun_debug = simple_strtoul(line, NULL, 10); 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds #endif 4971da177e4SLinus Torvalds 498877211f5STakashi Iwai static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) 4991da177e4SLinus Torvalds { 500877211f5STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 501877211f5STakashi Iwai struct snd_info_entry *entry; 5021da177e4SLinus Torvalds char name[16]; 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds sprintf(name, "pcm%i%c", pcm->device, 5051da177e4SLinus Torvalds pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 5061da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(pcm->card, name, pcm->card->proc_root)) == NULL) 5071da177e4SLinus Torvalds return -ENOMEM; 5081da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5091da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5101da177e4SLinus Torvalds snd_info_free_entry(entry); 5111da177e4SLinus Torvalds return -ENOMEM; 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds pstr->proc_root = entry; 5141da177e4SLinus Torvalds 5151da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { 516bf850204STakashi Iwai snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 5171da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5181da177e4SLinus Torvalds snd_info_free_entry(entry); 5191da177e4SLinus Torvalds entry = NULL; 5201da177e4SLinus Torvalds } 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds pstr->proc_info_entry = entry; 5231da177e4SLinus Torvalds 52461fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 525877211f5STakashi Iwai if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 526877211f5STakashi Iwai pstr->proc_root)) != NULL) { 5271da177e4SLinus Torvalds entry->c.text.read = snd_pcm_xrun_debug_read; 5281da177e4SLinus Torvalds entry->c.text.write = snd_pcm_xrun_debug_write; 529bd7bf042STakashi Iwai entry->mode |= S_IWUSR; 5301da177e4SLinus Torvalds entry->private_data = pstr; 5311da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5321da177e4SLinus Torvalds snd_info_free_entry(entry); 5331da177e4SLinus Torvalds entry = NULL; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds } 5361da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = entry; 5371da177e4SLinus Torvalds #endif 5381da177e4SLinus Torvalds return 0; 5391da177e4SLinus Torvalds } 5401da177e4SLinus Torvalds 541877211f5STakashi Iwai static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 5421da177e4SLinus Torvalds { 54361fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 544746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_xrun_debug_entry); 5451da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = NULL; 5461da177e4SLinus Torvalds #endif 547746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_info_entry); 5481da177e4SLinus Torvalds pstr->proc_info_entry = NULL; 549746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_root); 5501da177e4SLinus Torvalds pstr->proc_root = NULL; 5511da177e4SLinus Torvalds return 0; 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds 554877211f5STakashi Iwai static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5551da177e4SLinus Torvalds { 556877211f5STakashi Iwai struct snd_info_entry *entry; 557877211f5STakashi Iwai struct snd_card *card; 5581da177e4SLinus Torvalds char name[16]; 5591da177e4SLinus Torvalds 5601da177e4SLinus Torvalds card = substream->pcm->card; 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds sprintf(name, "sub%i", substream->number); 5631da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, name, substream->pstr->proc_root)) == NULL) 5641da177e4SLinus Torvalds return -ENOMEM; 5651da177e4SLinus Torvalds entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 5661da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5671da177e4SLinus Torvalds snd_info_free_entry(entry); 5681da177e4SLinus Torvalds return -ENOMEM; 5691da177e4SLinus Torvalds } 5701da177e4SLinus Torvalds substream->proc_root = entry; 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { 573bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 574bf850204STakashi Iwai snd_pcm_substream_proc_info_read); 5751da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5761da177e4SLinus Torvalds snd_info_free_entry(entry); 5771da177e4SLinus Torvalds entry = NULL; 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds substream->proc_info_entry = entry; 5811da177e4SLinus Torvalds 5821da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { 583bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 584bf850204STakashi Iwai snd_pcm_substream_proc_hw_params_read); 5851da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5861da177e4SLinus Torvalds snd_info_free_entry(entry); 5871da177e4SLinus Torvalds entry = NULL; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds substream->proc_hw_params_entry = entry; 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { 593bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 594bf850204STakashi Iwai snd_pcm_substream_proc_sw_params_read); 5951da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5961da177e4SLinus Torvalds snd_info_free_entry(entry); 5971da177e4SLinus Torvalds entry = NULL; 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds substream->proc_sw_params_entry = entry; 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { 603bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 604bf850204STakashi Iwai snd_pcm_substream_proc_status_read); 6051da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6061da177e4SLinus Torvalds snd_info_free_entry(entry); 6071da177e4SLinus Torvalds entry = NULL; 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds } 6101da177e4SLinus Torvalds substream->proc_status_entry = entry; 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds return 0; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds 615877211f5STakashi Iwai static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) 6161da177e4SLinus Torvalds { 617746d4a02STakashi Iwai snd_info_free_entry(substream->proc_info_entry); 6181da177e4SLinus Torvalds substream->proc_info_entry = NULL; 619746d4a02STakashi Iwai snd_info_free_entry(substream->proc_hw_params_entry); 6201da177e4SLinus Torvalds substream->proc_hw_params_entry = NULL; 621746d4a02STakashi Iwai snd_info_free_entry(substream->proc_sw_params_entry); 6221da177e4SLinus Torvalds substream->proc_sw_params_entry = NULL; 623746d4a02STakashi Iwai snd_info_free_entry(substream->proc_status_entry); 6241da177e4SLinus Torvalds substream->proc_status_entry = NULL; 625746d4a02STakashi Iwai snd_info_free_entry(substream->proc_root); 6261da177e4SLinus Torvalds substream->proc_root = NULL; 6271da177e4SLinus Torvalds return 0; 6281da177e4SLinus Torvalds } 629b7d90a35STakashi Iwai #else /* !CONFIG_SND_VERBOSE_PROCFS */ 630e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 631e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 632e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 633e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; } 634b7d90a35STakashi Iwai #endif /* CONFIG_SND_VERBOSE_PROCFS */ 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds /** 6371da177e4SLinus Torvalds * snd_pcm_new_stream - create a new PCM stream 6381da177e4SLinus Torvalds * @pcm: the pcm instance 6391da177e4SLinus Torvalds * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 6401da177e4SLinus Torvalds * @substream_count: the number of substreams 6411da177e4SLinus Torvalds * 6421da177e4SLinus Torvalds * Creates a new stream for the pcm. 6431da177e4SLinus Torvalds * The corresponding stream on the pcm must have been empty before 6441da177e4SLinus Torvalds * calling this, i.e. zero must be given to the argument of 6451da177e4SLinus Torvalds * snd_pcm_new(). 6461da177e4SLinus Torvalds * 647eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 6481da177e4SLinus Torvalds */ 649877211f5STakashi Iwai int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 6501da177e4SLinus Torvalds { 6511da177e4SLinus Torvalds int idx, err; 652877211f5STakashi Iwai struct snd_pcm_str *pstr = &pcm->streams[stream]; 653877211f5STakashi Iwai struct snd_pcm_substream *substream, *prev; 6541da177e4SLinus Torvalds 6558eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 6561a60d4c5SIngo Molnar mutex_init(&pstr->oss.setup_mutex); 6571da177e4SLinus Torvalds #endif 6581da177e4SLinus Torvalds pstr->stream = stream; 6591da177e4SLinus Torvalds pstr->pcm = pcm; 6601da177e4SLinus Torvalds pstr->substream_count = substream_count; 661945e5038SLiam Girdwood if (substream_count > 0 && !pcm->internal) { 6621da177e4SLinus Torvalds err = snd_pcm_stream_proc_init(pstr); 66373e77ba0STakashi Iwai if (err < 0) { 66409e56df8STakashi Iwai pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); 6651da177e4SLinus Torvalds return err; 6661da177e4SLinus Torvalds } 66773e77ba0STakashi Iwai } 6681da177e4SLinus Torvalds prev = NULL; 6691da177e4SLinus Torvalds for (idx = 0, prev = NULL; idx < substream_count; idx++) { 670ca2c0966STakashi Iwai substream = kzalloc(sizeof(*substream), GFP_KERNEL); 67173e77ba0STakashi Iwai if (substream == NULL) { 67209e56df8STakashi Iwai pcm_err(pcm, "Cannot allocate PCM substream\n"); 6731da177e4SLinus Torvalds return -ENOMEM; 67473e77ba0STakashi Iwai } 6751da177e4SLinus Torvalds substream->pcm = pcm; 6761da177e4SLinus Torvalds substream->pstr = pstr; 6771da177e4SLinus Torvalds substream->number = idx; 6781da177e4SLinus Torvalds substream->stream = stream; 6791da177e4SLinus Torvalds sprintf(substream->name, "subdevice #%i", idx); 6801da177e4SLinus Torvalds substream->buffer_bytes_max = UINT_MAX; 6811da177e4SLinus Torvalds if (prev == NULL) 6821da177e4SLinus Torvalds pstr->substream = substream; 6831da177e4SLinus Torvalds else 6841da177e4SLinus Torvalds prev->next = substream; 685945e5038SLiam Girdwood 686945e5038SLiam Girdwood if (!pcm->internal) { 6871da177e4SLinus Torvalds err = snd_pcm_substream_proc_init(substream); 6881da177e4SLinus Torvalds if (err < 0) { 68909e56df8STakashi Iwai pcm_err(pcm, 69009e56df8STakashi Iwai "Error in snd_pcm_stream_proc_init\n"); 6914d361285SAkinobu Mita if (prev == NULL) 6924d361285SAkinobu Mita pstr->substream = NULL; 6934d361285SAkinobu Mita else 6944d361285SAkinobu Mita prev->next = NULL; 6951da177e4SLinus Torvalds kfree(substream); 6961da177e4SLinus Torvalds return err; 6971da177e4SLinus Torvalds } 698945e5038SLiam Girdwood } 6991da177e4SLinus Torvalds substream->group = &substream->self_group; 7001da177e4SLinus Torvalds spin_lock_init(&substream->self_group.lock); 7011da177e4SLinus Torvalds INIT_LIST_HEAD(&substream->self_group.substreams); 7021da177e4SLinus Torvalds list_add_tail(&substream->link_list, &substream->self_group.substreams); 7039c323fcbSTakashi Iwai atomic_set(&substream->mmap_count, 0); 7041da177e4SLinus Torvalds prev = substream; 7051da177e4SLinus Torvalds } 7061da177e4SLinus Torvalds return 0; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 709e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new_stream); 710e88e8ae6STakashi Iwai 711945e5038SLiam Girdwood static int _snd_pcm_new(struct snd_card *card, const char *id, int device, 712945e5038SLiam Girdwood int playback_count, int capture_count, bool internal, 713877211f5STakashi Iwai struct snd_pcm **rpcm) 7141da177e4SLinus Torvalds { 715877211f5STakashi Iwai struct snd_pcm *pcm; 7161da177e4SLinus Torvalds int err; 717877211f5STakashi Iwai static struct snd_device_ops ops = { 7181da177e4SLinus Torvalds .dev_free = snd_pcm_dev_free, 7191da177e4SLinus Torvalds .dev_register = snd_pcm_dev_register, 7201da177e4SLinus Torvalds .dev_disconnect = snd_pcm_dev_disconnect, 7211da177e4SLinus Torvalds }; 7221da177e4SLinus Torvalds 7237eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7247eaa943cSTakashi Iwai return -ENXIO; 7257eaa943cSTakashi Iwai if (rpcm) 7261da177e4SLinus Torvalds *rpcm = NULL; 727ca2c0966STakashi Iwai pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 72873e77ba0STakashi Iwai if (pcm == NULL) { 72909e56df8STakashi Iwai dev_err(card->dev, "Cannot allocate PCM\n"); 7301da177e4SLinus Torvalds return -ENOMEM; 73173e77ba0STakashi Iwai } 7321da177e4SLinus Torvalds pcm->card = card; 7331da177e4SLinus Torvalds pcm->device = device; 734945e5038SLiam Girdwood pcm->internal = internal; 73573e77ba0STakashi Iwai if (id) 7361da177e4SLinus Torvalds strlcpy(pcm->id, id, sizeof(pcm->id)); 7371da177e4SLinus Torvalds if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { 7381da177e4SLinus Torvalds snd_pcm_free(pcm); 7391da177e4SLinus Torvalds return err; 7401da177e4SLinus Torvalds } 7411da177e4SLinus Torvalds if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { 7421da177e4SLinus Torvalds snd_pcm_free(pcm); 7431da177e4SLinus Torvalds return err; 7441da177e4SLinus Torvalds } 7451a60d4c5SIngo Molnar mutex_init(&pcm->open_mutex); 7461da177e4SLinus Torvalds init_waitqueue_head(&pcm->open_wait); 7471da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { 7481da177e4SLinus Torvalds snd_pcm_free(pcm); 7491da177e4SLinus Torvalds return err; 7501da177e4SLinus Torvalds } 7517eaa943cSTakashi Iwai if (rpcm) 7521da177e4SLinus Torvalds *rpcm = pcm; 7531da177e4SLinus Torvalds return 0; 7541da177e4SLinus Torvalds } 7551da177e4SLinus Torvalds 756945e5038SLiam Girdwood /** 757945e5038SLiam Girdwood * snd_pcm_new - create a new PCM instance 758945e5038SLiam Girdwood * @card: the card instance 759945e5038SLiam Girdwood * @id: the id string 760945e5038SLiam Girdwood * @device: the device index (zero based) 761945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 762945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 763945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 764945e5038SLiam Girdwood * 765945e5038SLiam Girdwood * Creates a new PCM instance. 766945e5038SLiam Girdwood * 767945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 768945e5038SLiam Girdwood * via snd_pcm_set_ops(). 769945e5038SLiam Girdwood * 770eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 771945e5038SLiam Girdwood */ 772945e5038SLiam Girdwood int snd_pcm_new(struct snd_card *card, const char *id, int device, 773945e5038SLiam Girdwood int playback_count, int capture_count, struct snd_pcm **rpcm) 774945e5038SLiam Girdwood { 775945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 776945e5038SLiam Girdwood false, rpcm); 777945e5038SLiam Girdwood } 778e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new); 779e88e8ae6STakashi Iwai 780945e5038SLiam Girdwood /** 781945e5038SLiam Girdwood * snd_pcm_new_internal - create a new internal PCM instance 782945e5038SLiam Girdwood * @card: the card instance 783945e5038SLiam Girdwood * @id: the id string 784945e5038SLiam Girdwood * @device: the device index (zero based - shared with normal PCMs) 785945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 786945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 787945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 788945e5038SLiam Girdwood * 789945e5038SLiam Girdwood * Creates a new internal PCM instance with no userspace device or procfs 790945e5038SLiam Girdwood * entries. This is used by ASoC Back End PCMs in order to create a PCM that 791945e5038SLiam Girdwood * will only be used internally by kernel drivers. i.e. it cannot be opened 792945e5038SLiam Girdwood * by userspace. It provides existing ASoC components drivers with a substream 793945e5038SLiam Girdwood * and access to any private data. 794945e5038SLiam Girdwood * 795945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 796945e5038SLiam Girdwood * via snd_pcm_set_ops(). 797945e5038SLiam Girdwood * 798eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 799945e5038SLiam Girdwood */ 800945e5038SLiam Girdwood int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, 801945e5038SLiam Girdwood int playback_count, int capture_count, 802945e5038SLiam Girdwood struct snd_pcm **rpcm) 803945e5038SLiam Girdwood { 804945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 805945e5038SLiam Girdwood true, rpcm); 806945e5038SLiam Girdwood } 807945e5038SLiam Girdwood EXPORT_SYMBOL(snd_pcm_new_internal); 808945e5038SLiam Girdwood 809877211f5STakashi Iwai static void snd_pcm_free_stream(struct snd_pcm_str * pstr) 8101da177e4SLinus Torvalds { 811877211f5STakashi Iwai struct snd_pcm_substream *substream, *substream_next; 8128eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 813877211f5STakashi Iwai struct snd_pcm_oss_setup *setup, *setupn; 8141da177e4SLinus Torvalds #endif 8151da177e4SLinus Torvalds substream = pstr->substream; 8161da177e4SLinus Torvalds while (substream) { 8171da177e4SLinus Torvalds substream_next = substream->next; 818c461482cSTakashi Iwai snd_pcm_timer_done(substream); 8191da177e4SLinus Torvalds snd_pcm_substream_proc_done(substream); 8201da177e4SLinus Torvalds kfree(substream); 8211da177e4SLinus Torvalds substream = substream_next; 8221da177e4SLinus Torvalds } 8231da177e4SLinus Torvalds snd_pcm_stream_proc_done(pstr); 8248eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 8251da177e4SLinus Torvalds for (setup = pstr->oss.setup_list; setup; setup = setupn) { 8261da177e4SLinus Torvalds setupn = setup->next; 8271da177e4SLinus Torvalds kfree(setup->task_name); 8281da177e4SLinus Torvalds kfree(setup); 8291da177e4SLinus Torvalds } 8301da177e4SLinus Torvalds #endif 8311da177e4SLinus Torvalds } 8321da177e4SLinus Torvalds 833877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm) 8341da177e4SLinus Torvalds { 835c461482cSTakashi Iwai struct snd_pcm_notify *notify; 836c461482cSTakashi Iwai 8377eaa943cSTakashi Iwai if (!pcm) 8387eaa943cSTakashi Iwai return 0; 839c461482cSTakashi Iwai list_for_each_entry(notify, &snd_pcm_notify_list, list) { 840c461482cSTakashi Iwai notify->n_unregister(pcm); 841c461482cSTakashi Iwai } 8421da177e4SLinus Torvalds if (pcm->private_free) 8431da177e4SLinus Torvalds pcm->private_free(pcm); 8441da177e4SLinus Torvalds snd_pcm_lib_preallocate_free_for_all(pcm); 8451da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 8461da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 8471da177e4SLinus Torvalds kfree(pcm); 8481da177e4SLinus Torvalds return 0; 8491da177e4SLinus Torvalds } 8501da177e4SLinus Torvalds 851877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device) 8521da177e4SLinus Torvalds { 853877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 8541da177e4SLinus Torvalds return snd_pcm_free(pcm); 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds 8573bf75f9bSTakashi Iwai int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 8583bf75f9bSTakashi Iwai struct file *file, 859877211f5STakashi Iwai struct snd_pcm_substream **rsubstream) 8601da177e4SLinus Torvalds { 861877211f5STakashi Iwai struct snd_pcm_str * pstr; 862877211f5STakashi Iwai struct snd_pcm_substream *substream; 863877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 864877211f5STakashi Iwai struct snd_ctl_file *kctl; 865877211f5STakashi Iwai struct snd_card *card; 8661da177e4SLinus Torvalds int prefer_subdevice = -1; 8671da177e4SLinus Torvalds size_t size; 8681da177e4SLinus Torvalds 8697eaa943cSTakashi Iwai if (snd_BUG_ON(!pcm || !rsubstream)) 8707eaa943cSTakashi Iwai return -ENXIO; 8711da177e4SLinus Torvalds *rsubstream = NULL; 8721da177e4SLinus Torvalds pstr = &pcm->streams[stream]; 8733bf75f9bSTakashi Iwai if (pstr->substream == NULL || pstr->substream_count == 0) 8741da177e4SLinus Torvalds return -ENODEV; 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds card = pcm->card; 877399ccdc1STakashi Iwai read_lock(&card->ctl_files_rwlock); 8789244b2c3SJohannes Berg list_for_each_entry(kctl, &card->ctl_files, list) { 87925d27edeSClemens Ladisch if (kctl->pid == task_pid(current)) { 8801da177e4SLinus Torvalds prefer_subdevice = kctl->prefer_pcm_subdevice; 8812529bba7STakashi Iwai if (prefer_subdevice != -1) 8821da177e4SLinus Torvalds break; 8831da177e4SLinus Torvalds } 8841da177e4SLinus Torvalds } 885399ccdc1STakashi Iwai read_unlock(&card->ctl_files_rwlock); 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds switch (stream) { 8881da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 8891da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 8901da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { 8911da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 8921da177e4SLinus Torvalds return -EAGAIN; 8931da177e4SLinus Torvalds } 8941da177e4SLinus Torvalds } 8951da177e4SLinus Torvalds break; 8961da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 8971da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 8981da177e4SLinus Torvalds for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { 8991da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 9001da177e4SLinus Torvalds return -EAGAIN; 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds } 9031da177e4SLinus Torvalds break; 9041da177e4SLinus Torvalds default: 9051da177e4SLinus Torvalds return -EINVAL; 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds 9080df63e44STakashi Iwai if (file->f_flags & O_APPEND) { 9090df63e44STakashi Iwai if (prefer_subdevice < 0) { 9100df63e44STakashi Iwai if (pstr->substream_count > 1) 9110df63e44STakashi Iwai return -EINVAL; /* must be unique */ 9120df63e44STakashi Iwai substream = pstr->substream; 9130df63e44STakashi Iwai } else { 9140df63e44STakashi Iwai for (substream = pstr->substream; substream; 9150df63e44STakashi Iwai substream = substream->next) 9160df63e44STakashi Iwai if (substream->number == prefer_subdevice) 9170df63e44STakashi Iwai break; 9180df63e44STakashi Iwai } 9190df63e44STakashi Iwai if (! substream) 9200df63e44STakashi Iwai return -ENODEV; 9210df63e44STakashi Iwai if (! SUBSTREAM_BUSY(substream)) 9220df63e44STakashi Iwai return -EBADFD; 9230df63e44STakashi Iwai substream->ref_count++; 9240df63e44STakashi Iwai *rsubstream = substream; 9250df63e44STakashi Iwai return 0; 9260df63e44STakashi Iwai } 9270df63e44STakashi Iwai 9281da177e4SLinus Torvalds if (prefer_subdevice >= 0) { 9291da177e4SLinus Torvalds for (substream = pstr->substream; substream; substream = substream->next) 9301da177e4SLinus Torvalds if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) 9311da177e4SLinus Torvalds goto __ok; 9321da177e4SLinus Torvalds } 9331da177e4SLinus Torvalds for (substream = pstr->substream; substream; substream = substream->next) 9341da177e4SLinus Torvalds if (!SUBSTREAM_BUSY(substream)) 9351da177e4SLinus Torvalds break; 9361da177e4SLinus Torvalds __ok: 9371da177e4SLinus Torvalds if (substream == NULL) 9381da177e4SLinus Torvalds return -EAGAIN; 9391da177e4SLinus Torvalds 940ca2c0966STakashi Iwai runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 9411da177e4SLinus Torvalds if (runtime == NULL) 9421da177e4SLinus Torvalds return -ENOMEM; 9431da177e4SLinus Torvalds 944877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 9451da177e4SLinus Torvalds runtime->status = snd_malloc_pages(size, GFP_KERNEL); 9461da177e4SLinus Torvalds if (runtime->status == NULL) { 9471da177e4SLinus Torvalds kfree(runtime); 9481da177e4SLinus Torvalds return -ENOMEM; 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds memset((void*)runtime->status, 0, size); 9511da177e4SLinus Torvalds 952877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 9531da177e4SLinus Torvalds runtime->control = snd_malloc_pages(size, GFP_KERNEL); 9541da177e4SLinus Torvalds if (runtime->control == NULL) { 955877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 956877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 9571da177e4SLinus Torvalds kfree(runtime); 9581da177e4SLinus Torvalds return -ENOMEM; 9591da177e4SLinus Torvalds } 9601da177e4SLinus Torvalds memset((void*)runtime->control, 0, size); 9611da177e4SLinus Torvalds 9621da177e4SLinus Torvalds init_waitqueue_head(&runtime->sleep); 963c91a988dSJaroslav Kysela init_waitqueue_head(&runtime->tsleep); 9641da177e4SLinus Torvalds 9651da177e4SLinus Torvalds runtime->status->state = SNDRV_PCM_STATE_OPEN; 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds substream->runtime = runtime; 9681da177e4SLinus Torvalds substream->private_data = pcm->private_data; 9690df63e44STakashi Iwai substream->ref_count = 1; 9700df63e44STakashi Iwai substream->f_flags = file->f_flags; 971e7373b70SClemens Ladisch substream->pid = get_pid(task_pid(current)); 9721da177e4SLinus Torvalds pstr->substream_opened++; 9731da177e4SLinus Torvalds *rsubstream = substream; 9741da177e4SLinus Torvalds return 0; 9751da177e4SLinus Torvalds } 9761da177e4SLinus Torvalds 9773bf75f9bSTakashi Iwai void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 9781da177e4SLinus Torvalds { 979877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 9800df63e44STakashi Iwai 9817eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 9827eaa943cSTakashi Iwai return; 9831da177e4SLinus Torvalds runtime = substream->runtime; 9841da177e4SLinus Torvalds if (runtime->private_free != NULL) 9851da177e4SLinus Torvalds runtime->private_free(runtime); 986877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 987877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 988877211f5STakashi Iwai snd_free_pages((void*)runtime->control, 989877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 9901da177e4SLinus Torvalds kfree(runtime->hw_constraints.rules); 9914d96eb25SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 9924d96eb25SJaroslav Kysela kfree(runtime->hwptr_log); 9934d96eb25SJaroslav Kysela #endif 9941da177e4SLinus Torvalds kfree(runtime); 9951da177e4SLinus Torvalds substream->runtime = NULL; 996e7373b70SClemens Ladisch put_pid(substream->pid); 997e7373b70SClemens Ladisch substream->pid = NULL; 9981da177e4SLinus Torvalds substream->pstr->substream_opened--; 9991da177e4SLinus Torvalds } 10001da177e4SLinus Torvalds 1001d80f19faSGreg Kroah-Hartman static ssize_t show_pcm_class(struct device *dev, 1002d80f19faSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 10039d19f48cSTakashi Iwai { 10049d19f48cSTakashi Iwai struct snd_pcm *pcm; 10059d19f48cSTakashi Iwai const char *str; 10069d19f48cSTakashi Iwai static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 10079d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_GENERIC] = "generic", 10089d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MULTI] = "multi", 10099d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MODEM] = "modem", 10109d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 10119d19f48cSTakashi Iwai }; 10129d19f48cSTakashi Iwai 1013d80f19faSGreg Kroah-Hartman if (! (pcm = dev_get_drvdata(dev)) || 10149d19f48cSTakashi Iwai pcm->dev_class > SNDRV_PCM_CLASS_LAST) 10159d19f48cSTakashi Iwai str = "none"; 10169d19f48cSTakashi Iwai else 10179d19f48cSTakashi Iwai str = strs[pcm->dev_class]; 10189d19f48cSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", str); 10199d19f48cSTakashi Iwai } 10209d19f48cSTakashi Iwai 1021d80f19faSGreg Kroah-Hartman static struct device_attribute pcm_attrs = 10229d19f48cSTakashi Iwai __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL); 10239d19f48cSTakashi Iwai 1024877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device) 10251da177e4SLinus Torvalds { 1026f87135f5SClemens Ladisch int cidx, err; 1027877211f5STakashi Iwai struct snd_pcm_substream *substream; 10289244b2c3SJohannes Berg struct snd_pcm_notify *notify; 10291da177e4SLinus Torvalds char str[16]; 10304b3be6afSJulia Lawall struct snd_pcm *pcm; 1031c78085fcSJohannes Berg struct device *dev; 10321da177e4SLinus Torvalds 10334b3be6afSJulia Lawall if (snd_BUG_ON(!device || !device->device_data)) 10347eaa943cSTakashi Iwai return -ENXIO; 10354b3be6afSJulia Lawall pcm = device->device_data; 10361a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1037f90c06a2SPawel MOLL err = snd_pcm_add(pcm); 1038f90c06a2SPawel MOLL if (err) { 10391a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1040f90c06a2SPawel MOLL return err; 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 10431da177e4SLinus Torvalds int devtype = -1; 1044945e5038SLiam Girdwood if (pcm->streams[cidx].substream == NULL || pcm->internal) 10451da177e4SLinus Torvalds continue; 10461da177e4SLinus Torvalds switch (cidx) { 10471da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 10481da177e4SLinus Torvalds sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); 10491da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 10501da177e4SLinus Torvalds break; 10511da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 10521da177e4SLinus Torvalds sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); 10531da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 10541da177e4SLinus Torvalds break; 10551da177e4SLinus Torvalds } 1056c78085fcSJohannes Berg /* device pointer to use, pcm->dev takes precedence if 1057c78085fcSJohannes Berg * it is assigned, otherwise fall back to card's device 1058c78085fcSJohannes Berg * if possible */ 1059c78085fcSJohannes Berg dev = pcm->dev; 1060c78085fcSJohannes Berg if (!dev) 1061c2902c8aSTakashi Iwai dev = snd_card_get_device_link(pcm->card); 1062c78085fcSJohannes Berg /* register pcm */ 1063c78085fcSJohannes Berg err = snd_register_device_for_dev(devtype, pcm->card, 10642af677fcSClemens Ladisch pcm->device, 1065f87135f5SClemens Ladisch &snd_pcm_f_ops[cidx], 1066c78085fcSJohannes Berg pcm, str, dev); 1067c78085fcSJohannes Berg if (err < 0) { 1068f87135f5SClemens Ladisch list_del(&pcm->list); 10691a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10701da177e4SLinus Torvalds return err; 10711da177e4SLinus Torvalds } 10729d19f48cSTakashi Iwai snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, 10739d19f48cSTakashi Iwai &pcm_attrs); 10741da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 10751da177e4SLinus Torvalds snd_pcm_timer_init(substream); 10761da177e4SLinus Torvalds } 10779244b2c3SJohannes Berg 10789244b2c3SJohannes Berg list_for_each_entry(notify, &snd_pcm_notify_list, list) 10791da177e4SLinus Torvalds notify->n_register(pcm); 10809244b2c3SJohannes Berg 10811a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10821da177e4SLinus Torvalds return 0; 10831da177e4SLinus Torvalds } 10841da177e4SLinus Torvalds 1085877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device) 10861da177e4SLinus Torvalds { 1087877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 1088c461482cSTakashi Iwai struct snd_pcm_notify *notify; 1089877211f5STakashi Iwai struct snd_pcm_substream *substream; 1090c461482cSTakashi Iwai int cidx, devtype; 10911da177e4SLinus Torvalds 10921a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1093c461482cSTakashi Iwai if (list_empty(&pcm->list)) 1094c461482cSTakashi Iwai goto unlock; 1095c461482cSTakashi Iwai 10969b0573c0STakashi Iwai mutex_lock(&pcm->open_mutex); 10970914f796STakashi Iwai wake_up(&pcm->open_wait); 1098f87135f5SClemens Ladisch list_del_init(&pcm->list); 10991da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) 11009b0573c0STakashi Iwai for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { 11019b0573c0STakashi Iwai snd_pcm_stream_lock_irq(substream); 11020914f796STakashi Iwai if (substream->runtime) { 11031da177e4SLinus Torvalds substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 11040914f796STakashi Iwai wake_up(&substream->runtime->sleep); 11050914f796STakashi Iwai wake_up(&substream->runtime->tsleep); 11060914f796STakashi Iwai } 11079b0573c0STakashi Iwai snd_pcm_stream_unlock_irq(substream); 11089b0573c0STakashi Iwai } 1109c461482cSTakashi Iwai list_for_each_entry(notify, &snd_pcm_notify_list, list) { 11101da177e4SLinus Torvalds notify->n_disconnect(pcm); 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 11131da177e4SLinus Torvalds devtype = -1; 11141da177e4SLinus Torvalds switch (cidx) { 11151da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 11161da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 11171da177e4SLinus Torvalds break; 11181da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 11191da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 11201da177e4SLinus Torvalds break; 11211da177e4SLinus Torvalds } 11221da177e4SLinus Torvalds snd_unregister_device(devtype, pcm->card, pcm->device); 11232d3391ecSTakashi Iwai if (pcm->streams[cidx].chmap_kctl) { 11242d3391ecSTakashi Iwai snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); 11252d3391ecSTakashi Iwai pcm->streams[cidx].chmap_kctl = NULL; 11262d3391ecSTakashi Iwai } 11271da177e4SLinus Torvalds } 11289b0573c0STakashi Iwai mutex_unlock(&pcm->open_mutex); 1129c461482cSTakashi Iwai unlock: 11301a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1131c461482cSTakashi Iwai return 0; 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds 1134877211f5STakashi Iwai int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 11351da177e4SLinus Torvalds { 11369244b2c3SJohannes Berg struct snd_pcm *pcm; 11371da177e4SLinus Torvalds 11387eaa943cSTakashi Iwai if (snd_BUG_ON(!notify || 11397eaa943cSTakashi Iwai !notify->n_register || 11407eaa943cSTakashi Iwai !notify->n_unregister || 11417eaa943cSTakashi Iwai !notify->n_disconnect)) 11427eaa943cSTakashi Iwai return -EINVAL; 11431a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11441da177e4SLinus Torvalds if (nfree) { 11451da177e4SLinus Torvalds list_del(¬ify->list); 11469244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 11479244b2c3SJohannes Berg notify->n_unregister(pcm); 11481da177e4SLinus Torvalds } else { 11491da177e4SLinus Torvalds list_add_tail(¬ify->list, &snd_pcm_notify_list); 11509244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 11519244b2c3SJohannes Berg notify->n_register(pcm); 11521da177e4SLinus Torvalds } 11531a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 11541da177e4SLinus Torvalds return 0; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 1157e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_notify); 1158e88e8ae6STakashi Iwai 1159e28563ccSTakashi Iwai #ifdef CONFIG_PROC_FS 11601da177e4SLinus Torvalds /* 11611da177e4SLinus Torvalds * Info interface 11621da177e4SLinus Torvalds */ 11631da177e4SLinus Torvalds 1164877211f5STakashi Iwai static void snd_pcm_proc_read(struct snd_info_entry *entry, 1165877211f5STakashi Iwai struct snd_info_buffer *buffer) 11661da177e4SLinus Torvalds { 1167877211f5STakashi Iwai struct snd_pcm *pcm; 11681da177e4SLinus Torvalds 11691a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11709244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 1171f87135f5SClemens Ladisch snd_iprintf(buffer, "%02i-%02i: %s : %s", 1172f87135f5SClemens Ladisch pcm->card->number, pcm->device, pcm->id, pcm->name); 11731da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1174877211f5STakashi Iwai snd_iprintf(buffer, " : playback %i", 1175877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 11761da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 1177877211f5STakashi Iwai snd_iprintf(buffer, " : capture %i", 1178877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 11791da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 11801da177e4SLinus Torvalds } 11811a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 11821da177e4SLinus Torvalds } 11831da177e4SLinus Torvalds 11846581f4e7STakashi Iwai static struct snd_info_entry *snd_pcm_proc_entry; 11851da177e4SLinus Torvalds 1186e28563ccSTakashi Iwai static void snd_pcm_proc_init(void) 11871da177e4SLinus Torvalds { 1188877211f5STakashi Iwai struct snd_info_entry *entry; 11891da177e4SLinus Torvalds 11901da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { 1191bf850204STakashi Iwai snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 11921da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 11931da177e4SLinus Torvalds snd_info_free_entry(entry); 11941da177e4SLinus Torvalds entry = NULL; 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds } 11971da177e4SLinus Torvalds snd_pcm_proc_entry = entry; 1198e28563ccSTakashi Iwai } 1199e28563ccSTakashi Iwai 1200e28563ccSTakashi Iwai static void snd_pcm_proc_done(void) 1201e28563ccSTakashi Iwai { 1202746d4a02STakashi Iwai snd_info_free_entry(snd_pcm_proc_entry); 1203e28563ccSTakashi Iwai } 1204e28563ccSTakashi Iwai 1205e28563ccSTakashi Iwai #else /* !CONFIG_PROC_FS */ 1206e28563ccSTakashi Iwai #define snd_pcm_proc_init() 1207e28563ccSTakashi Iwai #define snd_pcm_proc_done() 1208e28563ccSTakashi Iwai #endif /* CONFIG_PROC_FS */ 1209e28563ccSTakashi Iwai 1210e28563ccSTakashi Iwai 1211e28563ccSTakashi Iwai /* 1212e28563ccSTakashi Iwai * ENTRY functions 1213e28563ccSTakashi Iwai */ 1214e28563ccSTakashi Iwai 1215e28563ccSTakashi Iwai static int __init alsa_pcm_init(void) 1216e28563ccSTakashi Iwai { 1217e28563ccSTakashi Iwai snd_ctl_register_ioctl(snd_pcm_control_ioctl); 1218e28563ccSTakashi Iwai snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 1219e28563ccSTakashi Iwai snd_pcm_proc_init(); 12201da177e4SLinus Torvalds return 0; 12211da177e4SLinus Torvalds } 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds static void __exit alsa_pcm_exit(void) 12241da177e4SLinus Torvalds { 12251da177e4SLinus Torvalds snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 12261da177e4SLinus Torvalds snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 1227e28563ccSTakashi Iwai snd_pcm_proc_done(); 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds module_init(alsa_pcm_init) 12311da177e4SLinus Torvalds module_exit(alsa_pcm_exit) 1232