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> 2894ffb030SGustavo A. R. Silva #include <linux/nospec.h> 291da177e4SLinus Torvalds #include <sound/core.h> 301da177e4SLinus Torvalds #include <sound/minors.h> 311da177e4SLinus Torvalds #include <sound/pcm.h> 32a820ccbeSTakashi Iwai #include <sound/timer.h> 331da177e4SLinus Torvalds #include <sound/control.h> 341da177e4SLinus Torvalds #include <sound/info.h> 351da177e4SLinus Torvalds 362c4842d3STakashi Sakamoto #include "pcm_local.h" 372c4842d3STakashi Sakamoto 38c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); 391da177e4SLinus Torvalds MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 401da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 411da177e4SLinus Torvalds 42f87135f5SClemens Ladisch static LIST_HEAD(snd_pcm_devices); 431a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 4458f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 4558f30d65STakashi Iwai static LIST_HEAD(snd_pcm_notify_list); 4658f30d65STakashi Iwai #endif 471da177e4SLinus Torvalds 48877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm); 49877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device); 50877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device); 51877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device); 521da177e4SLinus Torvalds 53f90c06a2SPawel MOLL static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) 54f87135f5SClemens Ladisch { 55f87135f5SClemens Ladisch struct snd_pcm *pcm; 56f87135f5SClemens Ladisch 579244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 58f87135f5SClemens Ladisch if (pcm->card == card && pcm->device == device) 59f87135f5SClemens Ladisch return pcm; 60f87135f5SClemens Ladisch } 61f87135f5SClemens Ladisch return NULL; 62f87135f5SClemens Ladisch } 63f87135f5SClemens Ladisch 64f90c06a2SPawel MOLL static int snd_pcm_next(struct snd_card *card, int device) 65f90c06a2SPawel MOLL { 66f90c06a2SPawel MOLL struct snd_pcm *pcm; 67f90c06a2SPawel MOLL 68f90c06a2SPawel MOLL list_for_each_entry(pcm, &snd_pcm_devices, list) { 69f90c06a2SPawel MOLL if (pcm->card == card && pcm->device > device) 70f90c06a2SPawel MOLL return pcm->device; 71f90c06a2SPawel MOLL else if (pcm->card->number > card->number) 72f90c06a2SPawel MOLL return -1; 73f90c06a2SPawel MOLL } 74f90c06a2SPawel MOLL return -1; 75f90c06a2SPawel MOLL } 76f90c06a2SPawel MOLL 77f90c06a2SPawel MOLL static int snd_pcm_add(struct snd_pcm *newpcm) 78f90c06a2SPawel MOLL { 79f90c06a2SPawel MOLL struct snd_pcm *pcm; 80f90c06a2SPawel MOLL 81b95bd3a4STakashi Iwai if (newpcm->internal) 82b95bd3a4STakashi Iwai return 0; 83b95bd3a4STakashi Iwai 84f90c06a2SPawel MOLL list_for_each_entry(pcm, &snd_pcm_devices, list) { 85f90c06a2SPawel MOLL if (pcm->card == newpcm->card && pcm->device == newpcm->device) 86f90c06a2SPawel MOLL return -EBUSY; 87f90c06a2SPawel MOLL if (pcm->card->number > newpcm->card->number || 88f90c06a2SPawel MOLL (pcm->card == newpcm->card && 89f90c06a2SPawel MOLL pcm->device > newpcm->device)) { 90f90c06a2SPawel MOLL list_add(&newpcm->list, pcm->list.prev); 91f90c06a2SPawel MOLL return 0; 92f90c06a2SPawel MOLL } 93f90c06a2SPawel MOLL } 94f90c06a2SPawel MOLL list_add_tail(&newpcm->list, &snd_pcm_devices); 95f90c06a2SPawel MOLL return 0; 96f90c06a2SPawel MOLL } 97f90c06a2SPawel MOLL 98877211f5STakashi Iwai static int snd_pcm_control_ioctl(struct snd_card *card, 99877211f5STakashi Iwai struct snd_ctl_file *control, 1001da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 1011da177e4SLinus Torvalds { 1021da177e4SLinus Torvalds switch (cmd) { 1031da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 1041da177e4SLinus Torvalds { 1051da177e4SLinus Torvalds int device; 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds if (get_user(device, (int __user *)arg)) 1081da177e4SLinus Torvalds return -EFAULT; 1091a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 110f90c06a2SPawel MOLL device = snd_pcm_next(card, device); 1111a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1121da177e4SLinus Torvalds if (put_user(device, (int __user *)arg)) 1131da177e4SLinus Torvalds return -EFAULT; 1141da177e4SLinus Torvalds return 0; 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_INFO: 1171da177e4SLinus Torvalds { 118877211f5STakashi Iwai struct snd_pcm_info __user *info; 1191da177e4SLinus Torvalds unsigned int device, subdevice; 120877211f5STakashi Iwai int stream; 121877211f5STakashi Iwai struct snd_pcm *pcm; 122877211f5STakashi Iwai struct snd_pcm_str *pstr; 123877211f5STakashi Iwai struct snd_pcm_substream *substream; 124f87135f5SClemens Ladisch int err; 125f87135f5SClemens Ladisch 126877211f5STakashi Iwai info = (struct snd_pcm_info __user *)arg; 1271da177e4SLinus Torvalds if (get_user(device, &info->device)) 1281da177e4SLinus Torvalds return -EFAULT; 1291da177e4SLinus Torvalds if (get_user(stream, &info->stream)) 1301da177e4SLinus Torvalds return -EFAULT; 1311da177e4SLinus Torvalds if (stream < 0 || stream > 1) 1321da177e4SLinus Torvalds return -EINVAL; 13394ffb030SGustavo A. R. Silva stream = array_index_nospec(stream, 2); 1341da177e4SLinus Torvalds if (get_user(subdevice, &info->subdevice)) 1351da177e4SLinus Torvalds return -EFAULT; 1361a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 137f90c06a2SPawel MOLL pcm = snd_pcm_get(card, device); 138f87135f5SClemens Ladisch if (pcm == NULL) { 139f87135f5SClemens Ladisch err = -ENXIO; 140f87135f5SClemens Ladisch goto _error; 141f87135f5SClemens Ladisch } 142f87135f5SClemens Ladisch pstr = &pcm->streams[stream]; 143f87135f5SClemens Ladisch if (pstr->substream_count == 0) { 144f87135f5SClemens Ladisch err = -ENOENT; 145f87135f5SClemens Ladisch goto _error; 146f87135f5SClemens Ladisch } 147f87135f5SClemens Ladisch if (subdevice >= pstr->substream_count) { 148f87135f5SClemens Ladisch err = -ENXIO; 149f87135f5SClemens Ladisch goto _error; 150f87135f5SClemens Ladisch } 151f87135f5SClemens Ladisch for (substream = pstr->substream; substream; 152f87135f5SClemens Ladisch substream = substream->next) 1531da177e4SLinus Torvalds if (substream->number == (int)subdevice) 1541da177e4SLinus Torvalds break; 155f87135f5SClemens Ladisch if (substream == NULL) { 156f87135f5SClemens Ladisch err = -ENXIO; 157f87135f5SClemens Ladisch goto _error; 158f87135f5SClemens Ladisch } 159362bca57SRobb Glasser mutex_lock(&pcm->open_mutex); 160f87135f5SClemens Ladisch err = snd_pcm_info_user(substream, info); 161362bca57SRobb Glasser mutex_unlock(&pcm->open_mutex); 162f87135f5SClemens Ladisch _error: 1631a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 164f87135f5SClemens Ladisch return err; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 1671da177e4SLinus Torvalds { 1681da177e4SLinus Torvalds int val; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds if (get_user(val, (int __user *)arg)) 1711da177e4SLinus Torvalds return -EFAULT; 17223c18d4bSTakashi Iwai control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val; 1731da177e4SLinus Torvalds return 0; 1741da177e4SLinus Torvalds } 1751da177e4SLinus Torvalds } 1761da177e4SLinus Torvalds return -ENOIOCTLCMD; 1771da177e4SLinus Torvalds } 17821a3479aSJaroslav Kysela 1791da177e4SLinus Torvalds #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds static char *snd_pcm_format_names[] = { 1821da177e4SLinus Torvalds FORMAT(S8), 1831da177e4SLinus Torvalds FORMAT(U8), 1841da177e4SLinus Torvalds FORMAT(S16_LE), 1851da177e4SLinus Torvalds FORMAT(S16_BE), 1861da177e4SLinus Torvalds FORMAT(U16_LE), 1871da177e4SLinus Torvalds FORMAT(U16_BE), 1881da177e4SLinus Torvalds FORMAT(S24_LE), 1891da177e4SLinus Torvalds FORMAT(S24_BE), 1901da177e4SLinus Torvalds FORMAT(U24_LE), 1911da177e4SLinus Torvalds FORMAT(U24_BE), 1921da177e4SLinus Torvalds FORMAT(S32_LE), 1931da177e4SLinus Torvalds FORMAT(S32_BE), 1941da177e4SLinus Torvalds FORMAT(U32_LE), 1951da177e4SLinus Torvalds FORMAT(U32_BE), 1961da177e4SLinus Torvalds FORMAT(FLOAT_LE), 1971da177e4SLinus Torvalds FORMAT(FLOAT_BE), 1981da177e4SLinus Torvalds FORMAT(FLOAT64_LE), 1991da177e4SLinus Torvalds FORMAT(FLOAT64_BE), 2001da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_LE), 2011da177e4SLinus Torvalds FORMAT(IEC958_SUBFRAME_BE), 2021da177e4SLinus Torvalds FORMAT(MU_LAW), 2031da177e4SLinus Torvalds FORMAT(A_LAW), 2041da177e4SLinus Torvalds FORMAT(IMA_ADPCM), 2051da177e4SLinus Torvalds FORMAT(MPEG), 2061da177e4SLinus Torvalds FORMAT(GSM), 2071da177e4SLinus Torvalds FORMAT(SPECIAL), 2081da177e4SLinus Torvalds FORMAT(S24_3LE), 2091da177e4SLinus Torvalds FORMAT(S24_3BE), 2101da177e4SLinus Torvalds FORMAT(U24_3LE), 2111da177e4SLinus Torvalds FORMAT(U24_3BE), 2121da177e4SLinus Torvalds FORMAT(S20_3LE), 2131da177e4SLinus Torvalds FORMAT(S20_3BE), 2141da177e4SLinus Torvalds FORMAT(U20_3LE), 2151da177e4SLinus Torvalds FORMAT(U20_3BE), 2161da177e4SLinus Torvalds FORMAT(S18_3LE), 2171da177e4SLinus Torvalds FORMAT(S18_3BE), 2181da177e4SLinus Torvalds FORMAT(U18_3LE), 2191da177e4SLinus Torvalds FORMAT(U18_3BE), 2207a28826aSDan Carpenter FORMAT(G723_24), 2217a28826aSDan Carpenter FORMAT(G723_24_1B), 2227a28826aSDan Carpenter FORMAT(G723_40), 2237a28826aSDan Carpenter FORMAT(G723_40_1B), 224ef7a4f97SDaniel Mack FORMAT(DSD_U8), 225ef7a4f97SDaniel Mack FORMAT(DSD_U16_LE), 226d4288d3fSJurgen Kramer FORMAT(DSD_U32_LE), 227d42472ecSJussi Laako FORMAT(DSD_U16_BE), 228d42472ecSJussi Laako FORMAT(DSD_U32_BE), 2291da177e4SLinus Torvalds }; 2301da177e4SLinus Torvalds 23130b771cfSTakashi Iwai /** 23230b771cfSTakashi Iwai * snd_pcm_format_name - Return a name string for the given PCM format 23330b771cfSTakashi Iwai * @format: PCM format 23430b771cfSTakashi Iwai */ 2356e5265ecSTakashi Iwai const char *snd_pcm_format_name(snd_pcm_format_t format) 236e28563ccSTakashi Iwai { 237fea952e5SClemens Ladisch if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) 2387a28826aSDan Carpenter return "Unknown"; 239fea952e5SClemens Ladisch return snd_pcm_format_names[(__force unsigned int)format]; 240e28563ccSTakashi Iwai } 2416e5265ecSTakashi Iwai EXPORT_SYMBOL_GPL(snd_pcm_format_name); 2426e5265ecSTakashi Iwai 2436e5265ecSTakashi Iwai #ifdef CONFIG_SND_VERBOSE_PROCFS 2446e5265ecSTakashi Iwai 2456e5265ecSTakashi Iwai #define STATE(v) [SNDRV_PCM_STATE_##v] = #v 2466e5265ecSTakashi Iwai #define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v 2476e5265ecSTakashi Iwai #define READY(v) [SNDRV_PCM_READY_##v] = #v 2486e5265ecSTakashi Iwai #define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v 2496e5265ecSTakashi Iwai #define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v 2506e5265ecSTakashi Iwai #define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v 2516e5265ecSTakashi Iwai #define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v 2526e5265ecSTakashi Iwai #define START(v) [SNDRV_PCM_START_##v] = #v 2536e5265ecSTakashi Iwai #define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 254e28563ccSTakashi Iwai 255e28563ccSTakashi Iwai static char *snd_pcm_stream_names[] = { 256e28563ccSTakashi Iwai STREAM(PLAYBACK), 257e28563ccSTakashi Iwai STREAM(CAPTURE), 258e28563ccSTakashi Iwai }; 259e28563ccSTakashi Iwai 260e28563ccSTakashi Iwai static char *snd_pcm_state_names[] = { 261e28563ccSTakashi Iwai STATE(OPEN), 262e28563ccSTakashi Iwai STATE(SETUP), 263e28563ccSTakashi Iwai STATE(PREPARED), 264e28563ccSTakashi Iwai STATE(RUNNING), 265e28563ccSTakashi Iwai STATE(XRUN), 266e28563ccSTakashi Iwai STATE(DRAINING), 267e28563ccSTakashi Iwai STATE(PAUSED), 268e28563ccSTakashi Iwai STATE(SUSPENDED), 269e28563ccSTakashi Iwai }; 270e28563ccSTakashi Iwai 271e28563ccSTakashi Iwai static char *snd_pcm_access_names[] = { 272e28563ccSTakashi Iwai ACCESS(MMAP_INTERLEAVED), 273e28563ccSTakashi Iwai ACCESS(MMAP_NONINTERLEAVED), 274e28563ccSTakashi Iwai ACCESS(MMAP_COMPLEX), 275e28563ccSTakashi Iwai ACCESS(RW_INTERLEAVED), 276e28563ccSTakashi Iwai ACCESS(RW_NONINTERLEAVED), 277e28563ccSTakashi Iwai }; 278e28563ccSTakashi Iwai 2791da177e4SLinus Torvalds static char *snd_pcm_subformat_names[] = { 2801da177e4SLinus Torvalds SUBFORMAT(STD), 2811da177e4SLinus Torvalds }; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds static char *snd_pcm_tstamp_mode_names[] = { 2841da177e4SLinus Torvalds TSTAMP(NONE), 2858c121586SJaroslav Kysela TSTAMP(ENABLE), 2861da177e4SLinus Torvalds }; 2871da177e4SLinus Torvalds 288877211f5STakashi Iwai static const char *snd_pcm_stream_name(int stream) 2891da177e4SLinus Torvalds { 2901da177e4SLinus Torvalds return snd_pcm_stream_names[stream]; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds static const char *snd_pcm_access_name(snd_pcm_access_t access) 2941da177e4SLinus Torvalds { 295fea952e5SClemens Ladisch return snd_pcm_access_names[(__force int)access]; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) 2991da177e4SLinus Torvalds { 300fea952e5SClemens Ladisch return snd_pcm_subformat_names[(__force int)subformat]; 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 303877211f5STakashi Iwai static const char *snd_pcm_tstamp_mode_name(int mode) 3041da177e4SLinus Torvalds { 3051da177e4SLinus Torvalds return snd_pcm_tstamp_mode_names[mode]; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds static const char *snd_pcm_state_name(snd_pcm_state_t state) 3091da177e4SLinus Torvalds { 310fea952e5SClemens Ladisch return snd_pcm_state_names[(__force int)state]; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 3138eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 3141da177e4SLinus Torvalds #include <linux/soundcard.h> 3151a60d4c5SIngo Molnar 3161da177e4SLinus Torvalds static const char *snd_pcm_oss_format_name(int format) 3171da177e4SLinus Torvalds { 3181da177e4SLinus Torvalds switch (format) { 3191da177e4SLinus Torvalds case AFMT_MU_LAW: 3201da177e4SLinus Torvalds return "MU_LAW"; 3211da177e4SLinus Torvalds case AFMT_A_LAW: 3221da177e4SLinus Torvalds return "A_LAW"; 3231da177e4SLinus Torvalds case AFMT_IMA_ADPCM: 3241da177e4SLinus Torvalds return "IMA_ADPCM"; 3251da177e4SLinus Torvalds case AFMT_U8: 3261da177e4SLinus Torvalds return "U8"; 3271da177e4SLinus Torvalds case AFMT_S16_LE: 3281da177e4SLinus Torvalds return "S16_LE"; 3291da177e4SLinus Torvalds case AFMT_S16_BE: 3301da177e4SLinus Torvalds return "S16_BE"; 3311da177e4SLinus Torvalds case AFMT_S8: 3321da177e4SLinus Torvalds return "S8"; 3331da177e4SLinus Torvalds case AFMT_U16_LE: 3341da177e4SLinus Torvalds return "U16_LE"; 3351da177e4SLinus Torvalds case AFMT_U16_BE: 3361da177e4SLinus Torvalds return "U16_BE"; 3371da177e4SLinus Torvalds case AFMT_MPEG: 3381da177e4SLinus Torvalds return "MPEG"; 3391da177e4SLinus Torvalds default: 3401da177e4SLinus Torvalds return "unknown"; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds } 3431da177e4SLinus Torvalds #endif 3441da177e4SLinus Torvalds 345877211f5STakashi Iwai static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, 346877211f5STakashi Iwai struct snd_info_buffer *buffer) 3471da177e4SLinus Torvalds { 348877211f5STakashi Iwai struct snd_pcm_info *info; 3491da177e4SLinus Torvalds int err; 3501da177e4SLinus Torvalds 3517c22f1aaSTakashi Iwai if (! substream) 3527c22f1aaSTakashi Iwai return; 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds info = kmalloc(sizeof(*info), GFP_KERNEL); 355ec0e9937STakashi Iwai if (!info) 3561da177e4SLinus Torvalds return; 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds err = snd_pcm_info(substream, info); 3591da177e4SLinus Torvalds if (err < 0) { 3601da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 3611da177e4SLinus Torvalds kfree(info); 3621da177e4SLinus Torvalds return; 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds snd_iprintf(buffer, "card: %d\n", info->card); 3651da177e4SLinus Torvalds snd_iprintf(buffer, "device: %d\n", info->device); 3661da177e4SLinus Torvalds snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); 3671da177e4SLinus Torvalds snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); 3681da177e4SLinus Torvalds snd_iprintf(buffer, "id: %s\n", info->id); 3691da177e4SLinus Torvalds snd_iprintf(buffer, "name: %s\n", info->name); 3701da177e4SLinus Torvalds snd_iprintf(buffer, "subname: %s\n", info->subname); 3711da177e4SLinus Torvalds snd_iprintf(buffer, "class: %d\n", info->dev_class); 3721da177e4SLinus Torvalds snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); 3731da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); 3741da177e4SLinus Torvalds snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); 3751da177e4SLinus Torvalds kfree(info); 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 378877211f5STakashi Iwai static void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, 379877211f5STakashi Iwai struct snd_info_buffer *buffer) 3801da177e4SLinus Torvalds { 381877211f5STakashi Iwai snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, 382877211f5STakashi Iwai buffer); 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds 385877211f5STakashi Iwai static void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, 386877211f5STakashi Iwai struct snd_info_buffer *buffer) 3871da177e4SLinus Torvalds { 3889fe856e4SJoe Perches snd_pcm_proc_info_read(entry->private_data, buffer); 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds 391877211f5STakashi Iwai static void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, 392877211f5STakashi Iwai struct snd_info_buffer *buffer) 3931da177e4SLinus Torvalds { 394877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 395901d46d5STakashi Iwai struct snd_pcm_runtime *runtime; 396901d46d5STakashi Iwai 397901d46d5STakashi Iwai mutex_lock(&substream->pcm->open_mutex); 398901d46d5STakashi Iwai runtime = substream->runtime; 3991da177e4SLinus Torvalds if (!runtime) { 4001da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 401901d46d5STakashi Iwai goto unlock; 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 4041da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 405901d46d5STakashi Iwai goto unlock; 4061da177e4SLinus Torvalds } 4071da177e4SLinus Torvalds snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); 4081da177e4SLinus Torvalds snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); 4091da177e4SLinus Torvalds snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); 4101da177e4SLinus Torvalds snd_iprintf(buffer, "channels: %u\n", runtime->channels); 4111da177e4SLinus Torvalds snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 4121da177e4SLinus Torvalds snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); 4131da177e4SLinus Torvalds snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); 4148eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 4151da177e4SLinus Torvalds if (substream->oss.oss) { 4161da177e4SLinus Torvalds snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); 4171da177e4SLinus Torvalds snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); 4181da177e4SLinus Torvalds snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); 4191da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); 4201da177e4SLinus Torvalds snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); 4211da177e4SLinus Torvalds snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds #endif 424901d46d5STakashi Iwai unlock: 425901d46d5STakashi Iwai mutex_unlock(&substream->pcm->open_mutex); 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 428877211f5STakashi Iwai static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, 429877211f5STakashi Iwai struct snd_info_buffer *buffer) 4301da177e4SLinus Torvalds { 431877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 432901d46d5STakashi Iwai struct snd_pcm_runtime *runtime; 433901d46d5STakashi Iwai 434901d46d5STakashi Iwai mutex_lock(&substream->pcm->open_mutex); 435901d46d5STakashi Iwai runtime = substream->runtime; 4361da177e4SLinus Torvalds if (!runtime) { 4371da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 438901d46d5STakashi Iwai goto unlock; 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 4411da177e4SLinus Torvalds snd_iprintf(buffer, "no setup\n"); 442901d46d5STakashi Iwai goto unlock; 4431da177e4SLinus Torvalds } 4441da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); 4451da177e4SLinus Torvalds snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); 4461da177e4SLinus Torvalds snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); 4471da177e4SLinus Torvalds snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); 4481da177e4SLinus Torvalds snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); 4491da177e4SLinus Torvalds snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); 4501da177e4SLinus Torvalds snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); 4511da177e4SLinus Torvalds snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); 452901d46d5STakashi Iwai unlock: 453901d46d5STakashi Iwai mutex_unlock(&substream->pcm->open_mutex); 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 456877211f5STakashi Iwai static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, 457877211f5STakashi Iwai struct snd_info_buffer *buffer) 4581da177e4SLinus Torvalds { 459877211f5STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 460901d46d5STakashi Iwai struct snd_pcm_runtime *runtime; 461877211f5STakashi Iwai struct snd_pcm_status status; 4621da177e4SLinus Torvalds int err; 463901d46d5STakashi Iwai 464901d46d5STakashi Iwai mutex_lock(&substream->pcm->open_mutex); 465901d46d5STakashi Iwai runtime = substream->runtime; 4661da177e4SLinus Torvalds if (!runtime) { 4671da177e4SLinus Torvalds snd_iprintf(buffer, "closed\n"); 468901d46d5STakashi Iwai goto unlock; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 4711da177e4SLinus Torvalds err = snd_pcm_status(substream, &status); 4721da177e4SLinus Torvalds if (err < 0) { 4731da177e4SLinus Torvalds snd_iprintf(buffer, "error %d\n", err); 474901d46d5STakashi Iwai goto unlock; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 477e7373b70SClemens Ladisch snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); 4781da177e4SLinus Torvalds snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", 4791da177e4SLinus Torvalds status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); 4801da177e4SLinus Torvalds snd_iprintf(buffer, "tstamp : %ld.%09ld\n", 4811da177e4SLinus Torvalds status.tstamp.tv_sec, status.tstamp.tv_nsec); 4821da177e4SLinus Torvalds snd_iprintf(buffer, "delay : %ld\n", status.delay); 4831da177e4SLinus Torvalds snd_iprintf(buffer, "avail : %ld\n", status.avail); 4841da177e4SLinus Torvalds snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); 4851da177e4SLinus Torvalds snd_iprintf(buffer, "-----\n"); 4861da177e4SLinus Torvalds snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); 4871da177e4SLinus Torvalds snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); 488901d46d5STakashi Iwai unlock: 489901d46d5STakashi Iwai mutex_unlock(&substream->pcm->open_mutex); 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 49261fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 4932b30d411STakashi Iwai static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, 4942b30d411STakashi Iwai struct snd_info_buffer *buffer) 4952b30d411STakashi Iwai { 4962b30d411STakashi Iwai struct snd_pcm_substream *substream = entry->private_data; 4972b30d411STakashi Iwai 498e647f5a5STakashi Iwai snd_pcm_stop_xrun(substream); 4992b30d411STakashi Iwai } 5002b30d411STakashi Iwai 501877211f5STakashi Iwai static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 502877211f5STakashi Iwai struct snd_info_buffer *buffer) 5031da177e4SLinus Torvalds { 504877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 5051da177e4SLinus Torvalds snd_iprintf(buffer, "%d\n", pstr->xrun_debug); 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 508877211f5STakashi Iwai static void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, 509877211f5STakashi Iwai struct snd_info_buffer *buffer) 5101da177e4SLinus Torvalds { 511877211f5STakashi Iwai struct snd_pcm_str *pstr = entry->private_data; 5121da177e4SLinus Torvalds char line[64]; 5131da177e4SLinus Torvalds if (!snd_info_get_line(buffer, line, sizeof(line))) 5141da177e4SLinus Torvalds pstr->xrun_debug = simple_strtoul(line, NULL, 10); 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds #endif 5171da177e4SLinus Torvalds 518877211f5STakashi Iwai static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) 5191da177e4SLinus Torvalds { 520877211f5STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 521877211f5STakashi Iwai struct snd_info_entry *entry; 5221da177e4SLinus Torvalds char name[16]; 5231da177e4SLinus Torvalds 5241da177e4SLinus Torvalds sprintf(name, "pcm%i%c", pcm->device, 5251da177e4SLinus Torvalds pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 526c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(pcm->card, name, 527c8da9be4SMarkus Elfring pcm->card->proc_root); 528c8da9be4SMarkus Elfring if (!entry) 5291da177e4SLinus Torvalds return -ENOMEM; 5306a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 5311da177e4SLinus Torvalds pstr->proc_root = entry; 532c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root); 533a8d14981STakashi Iwai if (entry) 534bf850204STakashi Iwai snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 53561fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 536c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 537c8da9be4SMarkus Elfring pstr->proc_root); 538c8da9be4SMarkus Elfring if (entry) { 539a8d14981STakashi Iwai snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read); 5401da177e4SLinus Torvalds entry->c.text.write = snd_pcm_xrun_debug_write; 5416a73cf46SJoe Perches entry->mode |= 0200; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds #endif 5441da177e4SLinus Torvalds return 0; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds 547877211f5STakashi Iwai static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 5481da177e4SLinus Torvalds { 549746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_root); 5501da177e4SLinus Torvalds pstr->proc_root = NULL; 5511da177e4SLinus Torvalds return 0; 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds 554a8d14981STakashi Iwai static struct snd_info_entry * 555a8d14981STakashi Iwai create_substream_info_entry(struct snd_pcm_substream *substream, 556a8d14981STakashi Iwai const char *name, 557a8d14981STakashi Iwai void (*read)(struct snd_info_entry *, 558a8d14981STakashi Iwai struct snd_info_buffer *)) 559a8d14981STakashi Iwai { 560a8d14981STakashi Iwai struct snd_info_entry *entry; 561a8d14981STakashi Iwai 562a8d14981STakashi Iwai entry = snd_info_create_card_entry(substream->pcm->card, name, 563a8d14981STakashi Iwai substream->proc_root); 564a8d14981STakashi Iwai if (entry) 565a8d14981STakashi Iwai snd_info_set_text_ops(entry, substream, read); 566a8d14981STakashi Iwai return entry; 567a8d14981STakashi Iwai } 568a8d14981STakashi Iwai 569877211f5STakashi Iwai static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5701da177e4SLinus Torvalds { 571877211f5STakashi Iwai struct snd_info_entry *entry; 572877211f5STakashi Iwai struct snd_card *card; 5731da177e4SLinus Torvalds char name[16]; 5741da177e4SLinus Torvalds 5751da177e4SLinus Torvalds card = substream->pcm->card; 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds sprintf(name, "sub%i", substream->number); 578c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, name, 579c8da9be4SMarkus Elfring substream->pstr->proc_root); 580c8da9be4SMarkus Elfring if (!entry) 5811da177e4SLinus Torvalds return -ENOMEM; 5826a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 5831da177e4SLinus Torvalds substream->proc_root = entry; 584a8d14981STakashi Iwai 585a8d14981STakashi Iwai create_substream_info_entry(substream, "info", 586bf850204STakashi Iwai snd_pcm_substream_proc_info_read); 587a8d14981STakashi Iwai create_substream_info_entry(substream, "hw_params", 588bf850204STakashi Iwai snd_pcm_substream_proc_hw_params_read); 589a8d14981STakashi Iwai create_substream_info_entry(substream, "sw_params", 590bf850204STakashi Iwai snd_pcm_substream_proc_sw_params_read); 591a8d14981STakashi Iwai create_substream_info_entry(substream, "status", 592bf850204STakashi Iwai snd_pcm_substream_proc_status_read); 5931da177e4SLinus Torvalds 5942b30d411STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 595a8d14981STakashi Iwai entry = create_substream_info_entry(substream, "xrun_injection", NULL); 5962b30d411STakashi Iwai if (entry) { 5972b30d411STakashi Iwai entry->c.text.write = snd_pcm_xrun_injection_write; 5986a73cf46SJoe Perches entry->mode = S_IFREG | 0200; 5992b30d411STakashi Iwai } 6002b30d411STakashi Iwai #endif /* CONFIG_SND_PCM_XRUN_DEBUG */ 6012b30d411STakashi Iwai 6021da177e4SLinus Torvalds return 0; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds 605b7d90a35STakashi Iwai #else /* !CONFIG_SND_VERBOSE_PROCFS */ 606e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 607e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 608e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 609b7d90a35STakashi Iwai #endif /* CONFIG_SND_VERBOSE_PROCFS */ 6101da177e4SLinus Torvalds 611ef46c7afSTakashi Iwai static const struct attribute_group *pcm_dev_attr_groups[]; 612ef46c7afSTakashi Iwai 6133d21ef0bSTakashi Iwai /* 6143d21ef0bSTakashi Iwai * PM callbacks: we need to deal only with suspend here, as the resume is 6153d21ef0bSTakashi Iwai * triggered either from user-space or the driver's resume callback 6163d21ef0bSTakashi Iwai */ 6173d21ef0bSTakashi Iwai #ifdef CONFIG_PM_SLEEP 6183d21ef0bSTakashi Iwai static int do_pcm_suspend(struct device *dev) 6193d21ef0bSTakashi Iwai { 6203d21ef0bSTakashi Iwai struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 6213d21ef0bSTakashi Iwai 6223d21ef0bSTakashi Iwai if (!pstr->pcm->no_device_suspend) 6233d21ef0bSTakashi Iwai snd_pcm_suspend_all(pstr->pcm); 6243d21ef0bSTakashi Iwai return 0; 6253d21ef0bSTakashi Iwai } 6263d21ef0bSTakashi Iwai #endif 6273d21ef0bSTakashi Iwai 6283d21ef0bSTakashi Iwai static const struct dev_pm_ops pcm_dev_pm_ops = { 6293d21ef0bSTakashi Iwai SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) 6303d21ef0bSTakashi Iwai }; 6313d21ef0bSTakashi Iwai 6323d21ef0bSTakashi Iwai /* device type for PCM -- basically only for passing PM callbacks */ 6333d21ef0bSTakashi Iwai static const struct device_type pcm_dev_type = { 6343d21ef0bSTakashi Iwai .name = "pcm", 6353d21ef0bSTakashi Iwai .pm = &pcm_dev_pm_ops, 6363d21ef0bSTakashi Iwai }; 6373d21ef0bSTakashi Iwai 6381da177e4SLinus Torvalds /** 6391da177e4SLinus Torvalds * snd_pcm_new_stream - create a new PCM stream 6401da177e4SLinus Torvalds * @pcm: the pcm instance 6411da177e4SLinus Torvalds * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 6421da177e4SLinus Torvalds * @substream_count: the number of substreams 6431da177e4SLinus Torvalds * 6441da177e4SLinus Torvalds * Creates a new stream for the pcm. 6451da177e4SLinus Torvalds * The corresponding stream on the pcm must have been empty before 6461da177e4SLinus Torvalds * calling this, i.e. zero must be given to the argument of 6471da177e4SLinus Torvalds * snd_pcm_new(). 6481da177e4SLinus Torvalds * 649eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 6501da177e4SLinus Torvalds */ 651877211f5STakashi Iwai int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 6521da177e4SLinus Torvalds { 6531da177e4SLinus Torvalds int idx, err; 654877211f5STakashi Iwai struct snd_pcm_str *pstr = &pcm->streams[stream]; 655877211f5STakashi Iwai struct snd_pcm_substream *substream, *prev; 6561da177e4SLinus Torvalds 6578eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 6581a60d4c5SIngo Molnar mutex_init(&pstr->oss.setup_mutex); 6591da177e4SLinus Torvalds #endif 6601da177e4SLinus Torvalds pstr->stream = stream; 6611da177e4SLinus Torvalds pstr->pcm = pcm; 6621da177e4SLinus Torvalds pstr->substream_count = substream_count; 663ef46c7afSTakashi Iwai if (!substream_count) 664ef46c7afSTakashi Iwai return 0; 665ef46c7afSTakashi Iwai 666ef46c7afSTakashi Iwai snd_device_initialize(&pstr->dev, pcm->card); 667ef46c7afSTakashi Iwai pstr->dev.groups = pcm_dev_attr_groups; 6683d21ef0bSTakashi Iwai pstr->dev.type = &pcm_dev_type; 669ef46c7afSTakashi Iwai dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, 670ef46c7afSTakashi Iwai stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 671ef46c7afSTakashi Iwai 672ef46c7afSTakashi Iwai if (!pcm->internal) { 6731da177e4SLinus Torvalds err = snd_pcm_stream_proc_init(pstr); 67473e77ba0STakashi Iwai if (err < 0) { 67509e56df8STakashi Iwai pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); 6761da177e4SLinus Torvalds return err; 6771da177e4SLinus Torvalds } 67873e77ba0STakashi Iwai } 6791da177e4SLinus Torvalds prev = NULL; 6801da177e4SLinus Torvalds for (idx = 0, prev = NULL; idx < substream_count; idx++) { 681ca2c0966STakashi Iwai substream = kzalloc(sizeof(*substream), GFP_KERNEL); 682ec0e9937STakashi Iwai if (!substream) 6831da177e4SLinus Torvalds return -ENOMEM; 6841da177e4SLinus Torvalds substream->pcm = pcm; 6851da177e4SLinus Torvalds substream->pstr = pstr; 6861da177e4SLinus Torvalds substream->number = idx; 6871da177e4SLinus Torvalds substream->stream = stream; 6881da177e4SLinus Torvalds sprintf(substream->name, "subdevice #%i", idx); 6891da177e4SLinus Torvalds substream->buffer_bytes_max = UINT_MAX; 6901da177e4SLinus Torvalds if (prev == NULL) 6911da177e4SLinus Torvalds pstr->substream = substream; 6921da177e4SLinus Torvalds else 6931da177e4SLinus Torvalds prev->next = substream; 694945e5038SLiam Girdwood 695945e5038SLiam Girdwood if (!pcm->internal) { 6961da177e4SLinus Torvalds err = snd_pcm_substream_proc_init(substream); 6971da177e4SLinus Torvalds if (err < 0) { 69809e56df8STakashi Iwai pcm_err(pcm, 69909e56df8STakashi Iwai "Error in snd_pcm_stream_proc_init\n"); 7004d361285SAkinobu Mita if (prev == NULL) 7014d361285SAkinobu Mita pstr->substream = NULL; 7024d361285SAkinobu Mita else 7034d361285SAkinobu Mita prev->next = NULL; 7041da177e4SLinus Torvalds kfree(substream); 7051da177e4SLinus Torvalds return err; 7061da177e4SLinus Torvalds } 707945e5038SLiam Girdwood } 7081da177e4SLinus Torvalds substream->group = &substream->self_group; 70973365cb1STakashi Iwai snd_pcm_group_init(&substream->self_group); 7101da177e4SLinus Torvalds list_add_tail(&substream->link_list, &substream->self_group.substreams); 7119c323fcbSTakashi Iwai atomic_set(&substream->mmap_count, 0); 7121da177e4SLinus Torvalds prev = substream; 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds return 0; 7151da177e4SLinus Torvalds } 716e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new_stream); 717e88e8ae6STakashi Iwai 718945e5038SLiam Girdwood static int _snd_pcm_new(struct snd_card *card, const char *id, int device, 719945e5038SLiam Girdwood int playback_count, int capture_count, bool internal, 720877211f5STakashi Iwai struct snd_pcm **rpcm) 7211da177e4SLinus Torvalds { 722877211f5STakashi Iwai struct snd_pcm *pcm; 7231da177e4SLinus Torvalds int err; 724877211f5STakashi Iwai static struct snd_device_ops ops = { 7251da177e4SLinus Torvalds .dev_free = snd_pcm_dev_free, 7261da177e4SLinus Torvalds .dev_register = snd_pcm_dev_register, 7271da177e4SLinus Torvalds .dev_disconnect = snd_pcm_dev_disconnect, 7281da177e4SLinus Torvalds }; 7298b645e4aSTakashi Iwai static struct snd_device_ops internal_ops = { 7308b645e4aSTakashi Iwai .dev_free = snd_pcm_dev_free, 7318b645e4aSTakashi Iwai }; 7321da177e4SLinus Torvalds 7337eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7347eaa943cSTakashi Iwai return -ENXIO; 7357eaa943cSTakashi Iwai if (rpcm) 7361da177e4SLinus Torvalds *rpcm = NULL; 737ca2c0966STakashi Iwai pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 738ec0e9937STakashi Iwai if (!pcm) 7391da177e4SLinus Torvalds return -ENOMEM; 7401da177e4SLinus Torvalds pcm->card = card; 7411da177e4SLinus Torvalds pcm->device = device; 742945e5038SLiam Girdwood pcm->internal = internal; 743b95bd3a4STakashi Iwai mutex_init(&pcm->open_mutex); 744b95bd3a4STakashi Iwai init_waitqueue_head(&pcm->open_wait); 745b95bd3a4STakashi Iwai INIT_LIST_HEAD(&pcm->list); 74673e77ba0STakashi Iwai if (id) 7471da177e4SLinus Torvalds strlcpy(pcm->id, id, sizeof(pcm->id)); 74897d15a14SMarkus Elfring 74997d15a14SMarkus Elfring err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 75097d15a14SMarkus Elfring playback_count); 75197d15a14SMarkus Elfring if (err < 0) 75297d15a14SMarkus Elfring goto free_pcm; 75397d15a14SMarkus Elfring 75497d15a14SMarkus Elfring err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); 75597d15a14SMarkus Elfring if (err < 0) 75697d15a14SMarkus Elfring goto free_pcm; 75797d15a14SMarkus Elfring 7588b645e4aSTakashi Iwai err = snd_device_new(card, SNDRV_DEV_PCM, pcm, 7598b645e4aSTakashi Iwai internal ? &internal_ops : &ops); 76097d15a14SMarkus Elfring if (err < 0) 76197d15a14SMarkus Elfring goto free_pcm; 76297d15a14SMarkus Elfring 7637eaa943cSTakashi Iwai if (rpcm) 7641da177e4SLinus Torvalds *rpcm = pcm; 7651da177e4SLinus Torvalds return 0; 76697d15a14SMarkus Elfring 76797d15a14SMarkus Elfring free_pcm: 76897d15a14SMarkus Elfring snd_pcm_free(pcm); 76997d15a14SMarkus Elfring return err; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 772945e5038SLiam Girdwood /** 773945e5038SLiam Girdwood * snd_pcm_new - create a new PCM instance 774945e5038SLiam Girdwood * @card: the card instance 775945e5038SLiam Girdwood * @id: the id string 776945e5038SLiam Girdwood * @device: the device index (zero based) 777945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 778945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 779945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 780945e5038SLiam Girdwood * 781945e5038SLiam Girdwood * Creates a new PCM instance. 782945e5038SLiam Girdwood * 783945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 784945e5038SLiam Girdwood * via snd_pcm_set_ops(). 785945e5038SLiam Girdwood * 786eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 787945e5038SLiam Girdwood */ 788945e5038SLiam Girdwood int snd_pcm_new(struct snd_card *card, const char *id, int device, 789945e5038SLiam Girdwood int playback_count, int capture_count, struct snd_pcm **rpcm) 790945e5038SLiam Girdwood { 791945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 792945e5038SLiam Girdwood false, rpcm); 793945e5038SLiam Girdwood } 794e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new); 795e88e8ae6STakashi Iwai 796945e5038SLiam Girdwood /** 797945e5038SLiam Girdwood * snd_pcm_new_internal - create a new internal PCM instance 798945e5038SLiam Girdwood * @card: the card instance 799945e5038SLiam Girdwood * @id: the id string 800945e5038SLiam Girdwood * @device: the device index (zero based - shared with normal PCMs) 801945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 802945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 803945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 804945e5038SLiam Girdwood * 805945e5038SLiam Girdwood * Creates a new internal PCM instance with no userspace device or procfs 806945e5038SLiam Girdwood * entries. This is used by ASoC Back End PCMs in order to create a PCM that 807945e5038SLiam Girdwood * will only be used internally by kernel drivers. i.e. it cannot be opened 808945e5038SLiam Girdwood * by userspace. It provides existing ASoC components drivers with a substream 809945e5038SLiam Girdwood * and access to any private data. 810945e5038SLiam Girdwood * 811945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 812945e5038SLiam Girdwood * via snd_pcm_set_ops(). 813945e5038SLiam Girdwood * 814eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 815945e5038SLiam Girdwood */ 816945e5038SLiam Girdwood int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, 817945e5038SLiam Girdwood int playback_count, int capture_count, 818945e5038SLiam Girdwood struct snd_pcm **rpcm) 819945e5038SLiam Girdwood { 820945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 821945e5038SLiam Girdwood true, rpcm); 822945e5038SLiam Girdwood } 823945e5038SLiam Girdwood EXPORT_SYMBOL(snd_pcm_new_internal); 824945e5038SLiam Girdwood 825a8ff48cbSTakashi Iwai static void free_chmap(struct snd_pcm_str *pstr) 826a8ff48cbSTakashi Iwai { 827a8ff48cbSTakashi Iwai if (pstr->chmap_kctl) { 828a8ff48cbSTakashi Iwai snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); 829a8ff48cbSTakashi Iwai pstr->chmap_kctl = NULL; 830a8ff48cbSTakashi Iwai } 831a8ff48cbSTakashi Iwai } 832a8ff48cbSTakashi Iwai 833877211f5STakashi Iwai static void snd_pcm_free_stream(struct snd_pcm_str * pstr) 8341da177e4SLinus Torvalds { 835877211f5STakashi Iwai struct snd_pcm_substream *substream, *substream_next; 8368eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 837877211f5STakashi Iwai struct snd_pcm_oss_setup *setup, *setupn; 8381da177e4SLinus Torvalds #endif 839480e32ebSTakashi Iwai 840480e32ebSTakashi Iwai /* free all proc files under the stream */ 841480e32ebSTakashi Iwai snd_pcm_stream_proc_done(pstr); 842480e32ebSTakashi Iwai 8431da177e4SLinus Torvalds substream = pstr->substream; 8441da177e4SLinus Torvalds while (substream) { 8451da177e4SLinus Torvalds substream_next = substream->next; 846c461482cSTakashi Iwai snd_pcm_timer_done(substream); 8471da177e4SLinus Torvalds kfree(substream); 8481da177e4SLinus Torvalds substream = substream_next; 8491da177e4SLinus Torvalds } 8508eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 8511da177e4SLinus Torvalds for (setup = pstr->oss.setup_list; setup; setup = setupn) { 8521da177e4SLinus Torvalds setupn = setup->next; 8531da177e4SLinus Torvalds kfree(setup->task_name); 8541da177e4SLinus Torvalds kfree(setup); 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds #endif 857a8ff48cbSTakashi Iwai free_chmap(pstr); 858ef46c7afSTakashi Iwai if (pstr->substream_count) 859ef46c7afSTakashi Iwai put_device(&pstr->dev); 8601da177e4SLinus Torvalds } 8611da177e4SLinus Torvalds 86258f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 86358f30d65STakashi Iwai #define pcm_call_notify(pcm, call) \ 86458f30d65STakashi Iwai do { \ 86558f30d65STakashi Iwai struct snd_pcm_notify *_notify; \ 86658f30d65STakashi Iwai list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ 86758f30d65STakashi Iwai _notify->call(pcm); \ 86858f30d65STakashi Iwai } while (0) 86958f30d65STakashi Iwai #else 8709aee03f3SArnd Bergmann #define pcm_call_notify(pcm, call) do {} while (0) 87158f30d65STakashi Iwai #endif 87258f30d65STakashi Iwai 873877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm) 8741da177e4SLinus Torvalds { 8757eaa943cSTakashi Iwai if (!pcm) 8767eaa943cSTakashi Iwai return 0; 87758f30d65STakashi Iwai if (!pcm->internal) 87858f30d65STakashi Iwai pcm_call_notify(pcm, n_unregister); 8791da177e4SLinus Torvalds if (pcm->private_free) 8801da177e4SLinus Torvalds pcm->private_free(pcm); 8811da177e4SLinus Torvalds snd_pcm_lib_preallocate_free_for_all(pcm); 8821da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 8831da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 8841da177e4SLinus Torvalds kfree(pcm); 8851da177e4SLinus Torvalds return 0; 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds 888877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device) 8891da177e4SLinus Torvalds { 890877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 8911da177e4SLinus Torvalds return snd_pcm_free(pcm); 8921da177e4SLinus Torvalds } 8931da177e4SLinus Torvalds 8943bf75f9bSTakashi Iwai int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 8953bf75f9bSTakashi Iwai struct file *file, 896877211f5STakashi Iwai struct snd_pcm_substream **rsubstream) 8971da177e4SLinus Torvalds { 898877211f5STakashi Iwai struct snd_pcm_str * pstr; 899877211f5STakashi Iwai struct snd_pcm_substream *substream; 900877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 901877211f5STakashi Iwai struct snd_card *card; 90223c18d4bSTakashi Iwai int prefer_subdevice; 9031da177e4SLinus Torvalds size_t size; 9041da177e4SLinus Torvalds 9057eaa943cSTakashi Iwai if (snd_BUG_ON(!pcm || !rsubstream)) 9067eaa943cSTakashi Iwai return -ENXIO; 907ad876c86STakashi Iwai if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && 908ad876c86STakashi Iwai stream != SNDRV_PCM_STREAM_CAPTURE)) 909ad876c86STakashi Iwai return -EINVAL; 9101da177e4SLinus Torvalds *rsubstream = NULL; 9111da177e4SLinus Torvalds pstr = &pcm->streams[stream]; 9123bf75f9bSTakashi Iwai if (pstr->substream == NULL || pstr->substream_count == 0) 9131da177e4SLinus Torvalds return -ENODEV; 9141da177e4SLinus Torvalds 9151da177e4SLinus Torvalds card = pcm->card; 91623c18d4bSTakashi Iwai prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 919ad876c86STakashi Iwai int opposite = !stream; 920ad876c86STakashi Iwai 921ad876c86STakashi Iwai for (substream = pcm->streams[opposite].substream; substream; 922ad876c86STakashi Iwai substream = substream->next) { 9231da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 9241da177e4SLinus Torvalds return -EAGAIN; 9251da177e4SLinus Torvalds } 9261da177e4SLinus Torvalds } 9271da177e4SLinus Torvalds 9280df63e44STakashi Iwai if (file->f_flags & O_APPEND) { 9290df63e44STakashi Iwai if (prefer_subdevice < 0) { 9300df63e44STakashi Iwai if (pstr->substream_count > 1) 9310df63e44STakashi Iwai return -EINVAL; /* must be unique */ 9320df63e44STakashi Iwai substream = pstr->substream; 9330df63e44STakashi Iwai } else { 9340df63e44STakashi Iwai for (substream = pstr->substream; substream; 9350df63e44STakashi Iwai substream = substream->next) 9360df63e44STakashi Iwai if (substream->number == prefer_subdevice) 9370df63e44STakashi Iwai break; 9380df63e44STakashi Iwai } 9390df63e44STakashi Iwai if (! substream) 9400df63e44STakashi Iwai return -ENODEV; 9410df63e44STakashi Iwai if (! SUBSTREAM_BUSY(substream)) 9420df63e44STakashi Iwai return -EBADFD; 9430df63e44STakashi Iwai substream->ref_count++; 9440df63e44STakashi Iwai *rsubstream = substream; 9450df63e44STakashi Iwai return 0; 9460df63e44STakashi Iwai } 9470df63e44STakashi Iwai 948ad876c86STakashi Iwai for (substream = pstr->substream; substream; substream = substream->next) { 949ad876c86STakashi Iwai if (!SUBSTREAM_BUSY(substream) && 950ad876c86STakashi Iwai (prefer_subdevice == -1 || 951ad876c86STakashi Iwai substream->number == prefer_subdevice)) 9521da177e4SLinus Torvalds break; 953ad876c86STakashi Iwai } 9541da177e4SLinus Torvalds if (substream == NULL) 9551da177e4SLinus Torvalds return -EAGAIN; 9561da177e4SLinus Torvalds 957ca2c0966STakashi Iwai runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 9581da177e4SLinus Torvalds if (runtime == NULL) 9591da177e4SLinus Torvalds return -ENOMEM; 9601da177e4SLinus Torvalds 961877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 9621da177e4SLinus Torvalds runtime->status = snd_malloc_pages(size, GFP_KERNEL); 9631da177e4SLinus Torvalds if (runtime->status == NULL) { 9641da177e4SLinus Torvalds kfree(runtime); 9651da177e4SLinus Torvalds return -ENOMEM; 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds memset((void*)runtime->status, 0, size); 9681da177e4SLinus Torvalds 969877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 9701da177e4SLinus Torvalds runtime->control = snd_malloc_pages(size, GFP_KERNEL); 9711da177e4SLinus Torvalds if (runtime->control == NULL) { 972877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 973877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 9741da177e4SLinus Torvalds kfree(runtime); 9751da177e4SLinus Torvalds return -ENOMEM; 9761da177e4SLinus Torvalds } 9771da177e4SLinus Torvalds memset((void*)runtime->control, 0, size); 9781da177e4SLinus Torvalds 9791da177e4SLinus Torvalds init_waitqueue_head(&runtime->sleep); 980c91a988dSJaroslav Kysela init_waitqueue_head(&runtime->tsleep); 9811da177e4SLinus Torvalds 9821da177e4SLinus Torvalds runtime->status->state = SNDRV_PCM_STATE_OPEN; 9831da177e4SLinus Torvalds 9841da177e4SLinus Torvalds substream->runtime = runtime; 9851da177e4SLinus Torvalds substream->private_data = pcm->private_data; 9860df63e44STakashi Iwai substream->ref_count = 1; 9870df63e44STakashi Iwai substream->f_flags = file->f_flags; 988e7373b70SClemens Ladisch substream->pid = get_pid(task_pid(current)); 9891da177e4SLinus Torvalds pstr->substream_opened++; 9901da177e4SLinus Torvalds *rsubstream = substream; 9911da177e4SLinus Torvalds return 0; 9921da177e4SLinus Torvalds } 9931da177e4SLinus Torvalds 9943bf75f9bSTakashi Iwai void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 9951da177e4SLinus Torvalds { 996877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 9970df63e44STakashi Iwai 9987eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 9997eaa943cSTakashi Iwai return; 10001da177e4SLinus Torvalds runtime = substream->runtime; 10011da177e4SLinus Torvalds if (runtime->private_free != NULL) 10021da177e4SLinus Torvalds runtime->private_free(runtime); 1003877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 1004877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 1005877211f5STakashi Iwai snd_free_pages((void*)runtime->control, 1006877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 10071da177e4SLinus Torvalds kfree(runtime->hw_constraints.rules); 1008a820ccbeSTakashi Iwai /* Avoid concurrent access to runtime via PCM timer interface */ 1009a820ccbeSTakashi Iwai if (substream->timer) 1010a820ccbeSTakashi Iwai spin_lock_irq(&substream->timer->lock); 10111da177e4SLinus Torvalds substream->runtime = NULL; 1012a820ccbeSTakashi Iwai if (substream->timer) 1013a820ccbeSTakashi Iwai spin_unlock_irq(&substream->timer->lock); 1014a820ccbeSTakashi Iwai kfree(runtime); 1015e7373b70SClemens Ladisch put_pid(substream->pid); 1016e7373b70SClemens Ladisch substream->pid = NULL; 10171da177e4SLinus Torvalds substream->pstr->substream_opened--; 10181da177e4SLinus Torvalds } 10191da177e4SLinus Torvalds 1020d80f19faSGreg Kroah-Hartman static ssize_t show_pcm_class(struct device *dev, 1021d80f19faSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 10229d19f48cSTakashi Iwai { 102360b93030STakashi Iwai struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 102460b93030STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 10259d19f48cSTakashi Iwai const char *str; 10269d19f48cSTakashi Iwai static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 10279d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_GENERIC] = "generic", 10289d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MULTI] = "multi", 10299d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MODEM] = "modem", 10309d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 10319d19f48cSTakashi Iwai }; 10329d19f48cSTakashi Iwai 103360b93030STakashi Iwai if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) 10349d19f48cSTakashi Iwai str = "none"; 10359d19f48cSTakashi Iwai else 10369d19f48cSTakashi Iwai str = strs[pcm->dev_class]; 10379d19f48cSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", str); 10389d19f48cSTakashi Iwai } 10399d19f48cSTakashi Iwai 10406a73cf46SJoe Perches static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL); 1041caa751baSTakashi Iwai static struct attribute *pcm_dev_attrs[] = { 1042caa751baSTakashi Iwai &dev_attr_pcm_class.attr, 1043caa751baSTakashi Iwai NULL 1044caa751baSTakashi Iwai }; 1045caa751baSTakashi Iwai 1046343fe850SArvind Yadav static const struct attribute_group pcm_dev_attr_group = { 1047caa751baSTakashi Iwai .attrs = pcm_dev_attrs, 1048caa751baSTakashi Iwai }; 1049caa751baSTakashi Iwai 1050caa751baSTakashi Iwai static const struct attribute_group *pcm_dev_attr_groups[] = { 1051caa751baSTakashi Iwai &pcm_dev_attr_group, 1052caa751baSTakashi Iwai NULL 1053caa751baSTakashi Iwai }; 10549d19f48cSTakashi Iwai 1055877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device) 10561da177e4SLinus Torvalds { 1057f87135f5SClemens Ladisch int cidx, err; 1058877211f5STakashi Iwai struct snd_pcm_substream *substream; 10594b3be6afSJulia Lawall struct snd_pcm *pcm; 10601da177e4SLinus Torvalds 10614b3be6afSJulia Lawall if (snd_BUG_ON(!device || !device->device_data)) 10627eaa943cSTakashi Iwai return -ENXIO; 10634b3be6afSJulia Lawall pcm = device->device_data; 1064b95bd3a4STakashi Iwai 10651a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1066f90c06a2SPawel MOLL err = snd_pcm_add(pcm); 1067b95bd3a4STakashi Iwai if (err) 1068b95bd3a4STakashi Iwai goto unlock; 10691da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 10701da177e4SLinus Torvalds int devtype = -1; 1071b95bd3a4STakashi Iwai if (pcm->streams[cidx].substream == NULL) 10721da177e4SLinus Torvalds continue; 10731da177e4SLinus Torvalds switch (cidx) { 10741da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 10751da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 10761da177e4SLinus Torvalds break; 10771da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 10781da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 10791da177e4SLinus Torvalds break; 10801da177e4SLinus Torvalds } 1081c78085fcSJohannes Berg /* register pcm */ 108240a4b263STakashi Iwai err = snd_register_device(devtype, pcm->card, pcm->device, 108340a4b263STakashi Iwai &snd_pcm_f_ops[cidx], pcm, 108440a4b263STakashi Iwai &pcm->streams[cidx].dev); 1085c78085fcSJohannes Berg if (err < 0) { 1086b95bd3a4STakashi Iwai list_del_init(&pcm->list); 1087b95bd3a4STakashi Iwai goto unlock; 10881da177e4SLinus Torvalds } 1089caa751baSTakashi Iwai 10901da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 10911da177e4SLinus Torvalds snd_pcm_timer_init(substream); 10921da177e4SLinus Torvalds } 10939244b2c3SJohannes Berg 109458f30d65STakashi Iwai pcm_call_notify(pcm, n_register); 10959244b2c3SJohannes Berg 1096b95bd3a4STakashi Iwai unlock: 10971a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1098b95bd3a4STakashi Iwai return err; 10991da177e4SLinus Torvalds } 11001da177e4SLinus Torvalds 1101877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device) 11021da177e4SLinus Torvalds { 1103877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 1104877211f5STakashi Iwai struct snd_pcm_substream *substream; 110540a4b263STakashi Iwai int cidx; 11061da177e4SLinus Torvalds 11071a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11089b0573c0STakashi Iwai mutex_lock(&pcm->open_mutex); 11090914f796STakashi Iwai wake_up(&pcm->open_wait); 1110f87135f5SClemens Ladisch list_del_init(&pcm->list); 1111646e1dd8STakashi Iwai for (cidx = 0; cidx < 2; cidx++) { 11129b0573c0STakashi Iwai for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { 11139b0573c0STakashi Iwai snd_pcm_stream_lock_irq(substream); 11140914f796STakashi Iwai if (substream->runtime) { 11156ca73de7STakashi Iwai if (snd_pcm_running(substream)) 11166ca73de7STakashi Iwai snd_pcm_stop(substream, 11176ca73de7STakashi Iwai SNDRV_PCM_STATE_DISCONNECTED); 11186ca73de7STakashi Iwai /* to be sure, set the state unconditionally */ 11191da177e4SLinus Torvalds substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 11200914f796STakashi Iwai wake_up(&substream->runtime->sleep); 11210914f796STakashi Iwai wake_up(&substream->runtime->tsleep); 11220914f796STakashi Iwai } 11239b0573c0STakashi Iwai snd_pcm_stream_unlock_irq(substream); 11249b0573c0STakashi Iwai } 1125646e1dd8STakashi Iwai } 11268b645e4aSTakashi Iwai 112758f30d65STakashi Iwai pcm_call_notify(pcm, n_disconnect); 11281da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 112940a4b263STakashi Iwai snd_unregister_device(&pcm->streams[cidx].dev); 1130a8ff48cbSTakashi Iwai free_chmap(&pcm->streams[cidx]); 11311da177e4SLinus Torvalds } 11329b0573c0STakashi Iwai mutex_unlock(&pcm->open_mutex); 11331a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1134c461482cSTakashi Iwai return 0; 11351da177e4SLinus Torvalds } 11361da177e4SLinus Torvalds 113758f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 113830b771cfSTakashi Iwai /** 113930b771cfSTakashi Iwai * snd_pcm_notify - Add/remove the notify list 114030b771cfSTakashi Iwai * @notify: PCM notify list 114130b771cfSTakashi Iwai * @nfree: 0 = register, 1 = unregister 114230b771cfSTakashi Iwai * 114330b771cfSTakashi Iwai * This adds the given notifier to the global list so that the callback is 114430b771cfSTakashi Iwai * called for each registered PCM devices. This exists only for PCM OSS 114530b771cfSTakashi Iwai * emulation, so far. 114630b771cfSTakashi Iwai */ 1147877211f5STakashi Iwai int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 11481da177e4SLinus Torvalds { 11499244b2c3SJohannes Berg struct snd_pcm *pcm; 11501da177e4SLinus Torvalds 11517eaa943cSTakashi Iwai if (snd_BUG_ON(!notify || 11527eaa943cSTakashi Iwai !notify->n_register || 11537eaa943cSTakashi Iwai !notify->n_unregister || 11547eaa943cSTakashi Iwai !notify->n_disconnect)) 11557eaa943cSTakashi Iwai return -EINVAL; 11561a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11571da177e4SLinus Torvalds if (nfree) { 11581da177e4SLinus Torvalds list_del(¬ify->list); 11599244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 11609244b2c3SJohannes Berg notify->n_unregister(pcm); 11611da177e4SLinus Torvalds } else { 11621da177e4SLinus Torvalds list_add_tail(¬ify->list, &snd_pcm_notify_list); 11639244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 11649244b2c3SJohannes Berg notify->n_register(pcm); 11651da177e4SLinus Torvalds } 11661a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 11671da177e4SLinus Torvalds return 0; 11681da177e4SLinus Torvalds } 1169e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_notify); 117058f30d65STakashi Iwai #endif /* CONFIG_SND_PCM_OSS */ 1171e88e8ae6STakashi Iwai 1172cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS 11731da177e4SLinus Torvalds /* 11741da177e4SLinus Torvalds * Info interface 11751da177e4SLinus Torvalds */ 11761da177e4SLinus Torvalds 1177877211f5STakashi Iwai static void snd_pcm_proc_read(struct snd_info_entry *entry, 1178877211f5STakashi Iwai struct snd_info_buffer *buffer) 11791da177e4SLinus Torvalds { 1180877211f5STakashi Iwai struct snd_pcm *pcm; 11811da177e4SLinus Torvalds 11821a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11839244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 1184f87135f5SClemens Ladisch snd_iprintf(buffer, "%02i-%02i: %s : %s", 1185f87135f5SClemens Ladisch pcm->card->number, pcm->device, pcm->id, pcm->name); 11861da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1187877211f5STakashi Iwai snd_iprintf(buffer, " : playback %i", 1188877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 11891da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 1190877211f5STakashi Iwai snd_iprintf(buffer, " : capture %i", 1191877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 11921da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 11931da177e4SLinus Torvalds } 11941a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds 11976581f4e7STakashi Iwai static struct snd_info_entry *snd_pcm_proc_entry; 11981da177e4SLinus Torvalds 1199e28563ccSTakashi Iwai static void snd_pcm_proc_init(void) 12001da177e4SLinus Torvalds { 1201877211f5STakashi Iwai struct snd_info_entry *entry; 12021da177e4SLinus Torvalds 1203c8da9be4SMarkus Elfring entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL); 1204c8da9be4SMarkus Elfring if (entry) { 1205bf850204STakashi Iwai snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 12061da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 12071da177e4SLinus Torvalds snd_info_free_entry(entry); 12081da177e4SLinus Torvalds entry = NULL; 12091da177e4SLinus Torvalds } 12101da177e4SLinus Torvalds } 12111da177e4SLinus Torvalds snd_pcm_proc_entry = entry; 1212e28563ccSTakashi Iwai } 1213e28563ccSTakashi Iwai 1214e28563ccSTakashi Iwai static void snd_pcm_proc_done(void) 1215e28563ccSTakashi Iwai { 1216746d4a02STakashi Iwai snd_info_free_entry(snd_pcm_proc_entry); 1217e28563ccSTakashi Iwai } 1218e28563ccSTakashi Iwai 1219cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */ 1220e28563ccSTakashi Iwai #define snd_pcm_proc_init() 1221e28563ccSTakashi Iwai #define snd_pcm_proc_done() 1222cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */ 1223e28563ccSTakashi Iwai 1224e28563ccSTakashi Iwai 1225e28563ccSTakashi Iwai /* 1226e28563ccSTakashi Iwai * ENTRY functions 1227e28563ccSTakashi Iwai */ 1228e28563ccSTakashi Iwai 1229e28563ccSTakashi Iwai static int __init alsa_pcm_init(void) 1230e28563ccSTakashi Iwai { 1231e28563ccSTakashi Iwai snd_ctl_register_ioctl(snd_pcm_control_ioctl); 1232e28563ccSTakashi Iwai snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 1233e28563ccSTakashi Iwai snd_pcm_proc_init(); 12341da177e4SLinus Torvalds return 0; 12351da177e4SLinus Torvalds } 12361da177e4SLinus Torvalds 12371da177e4SLinus Torvalds static void __exit alsa_pcm_exit(void) 12381da177e4SLinus Torvalds { 12391da177e4SLinus Torvalds snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 12401da177e4SLinus Torvalds snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 1241e28563ccSTakashi Iwai snd_pcm_proc_done(); 12421da177e4SLinus Torvalds } 12431da177e4SLinus Torvalds 12441da177e4SLinus Torvalds module_init(alsa_pcm_init) 12451da177e4SLinus Torvalds module_exit(alsa_pcm_exit) 1246