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 if (snd_info_register(entry) < 0) { 5321da177e4SLinus Torvalds snd_info_free_entry(entry); 5331da177e4SLinus Torvalds return -ENOMEM; 5341da177e4SLinus Torvalds } 5351da177e4SLinus Torvalds pstr->proc_root = entry; 536c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root); 537c8da9be4SMarkus Elfring if (entry) { 538bf850204STakashi Iwai snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 539480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 5401da177e4SLinus Torvalds snd_info_free_entry(entry); 5411da177e4SLinus Torvalds } 5421da177e4SLinus Torvalds 54361fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 544c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 545c8da9be4SMarkus Elfring pstr->proc_root); 546c8da9be4SMarkus Elfring if (entry) { 5471da177e4SLinus Torvalds entry->c.text.read = snd_pcm_xrun_debug_read; 5481da177e4SLinus Torvalds entry->c.text.write = snd_pcm_xrun_debug_write; 5496a73cf46SJoe Perches entry->mode |= 0200; 5501da177e4SLinus Torvalds entry->private_data = pstr; 551480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 5521da177e4SLinus Torvalds snd_info_free_entry(entry); 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds #endif 5551da177e4SLinus Torvalds return 0; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds 558877211f5STakashi Iwai static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 5591da177e4SLinus Torvalds { 560746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_root); 5611da177e4SLinus Torvalds pstr->proc_root = NULL; 5621da177e4SLinus Torvalds return 0; 5631da177e4SLinus Torvalds } 5641da177e4SLinus Torvalds 565877211f5STakashi Iwai static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5661da177e4SLinus Torvalds { 567877211f5STakashi Iwai struct snd_info_entry *entry; 568877211f5STakashi Iwai struct snd_card *card; 5691da177e4SLinus Torvalds char name[16]; 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds card = substream->pcm->card; 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds sprintf(name, "sub%i", substream->number); 574c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, name, 575c8da9be4SMarkus Elfring substream->pstr->proc_root); 576c8da9be4SMarkus Elfring if (!entry) 5771da177e4SLinus Torvalds return -ENOMEM; 5786a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 5791da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5801da177e4SLinus Torvalds snd_info_free_entry(entry); 5811da177e4SLinus Torvalds return -ENOMEM; 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds substream->proc_root = entry; 584c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, "info", substream->proc_root); 585c8da9be4SMarkus Elfring if (entry) { 586bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 587bf850204STakashi Iwai snd_pcm_substream_proc_info_read); 588480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 5891da177e4SLinus Torvalds snd_info_free_entry(entry); 5901da177e4SLinus Torvalds } 591c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, "hw_params", 592c8da9be4SMarkus Elfring substream->proc_root); 593c8da9be4SMarkus Elfring if (entry) { 594bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 595bf850204STakashi Iwai snd_pcm_substream_proc_hw_params_read); 596480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 5971da177e4SLinus Torvalds snd_info_free_entry(entry); 5981da177e4SLinus Torvalds } 599c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, "sw_params", 600c8da9be4SMarkus Elfring substream->proc_root); 601c8da9be4SMarkus Elfring if (entry) { 602bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 603bf850204STakashi Iwai snd_pcm_substream_proc_sw_params_read); 604480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 6051da177e4SLinus Torvalds snd_info_free_entry(entry); 6061da177e4SLinus Torvalds } 607e6b4c525STakashi Iwai entry = snd_info_create_card_entry(card, "status", 608e6b4c525STakashi Iwai substream->proc_root); 609c8da9be4SMarkus Elfring if (entry) { 610bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 611bf850204STakashi Iwai snd_pcm_substream_proc_status_read); 612480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 6131da177e4SLinus Torvalds snd_info_free_entry(entry); 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds 6162b30d411STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 6172b30d411STakashi Iwai entry = snd_info_create_card_entry(card, "xrun_injection", 6182b30d411STakashi Iwai substream->proc_root); 6192b30d411STakashi Iwai if (entry) { 6202b30d411STakashi Iwai entry->private_data = substream; 6212b30d411STakashi Iwai entry->c.text.read = NULL; 6222b30d411STakashi Iwai entry->c.text.write = snd_pcm_xrun_injection_write; 6236a73cf46SJoe Perches entry->mode = S_IFREG | 0200; 624480e32ebSTakashi Iwai if (snd_info_register(entry) < 0) 6252b30d411STakashi Iwai snd_info_free_entry(entry); 6262b30d411STakashi Iwai } 6272b30d411STakashi Iwai #endif /* CONFIG_SND_PCM_XRUN_DEBUG */ 6282b30d411STakashi Iwai 6291da177e4SLinus Torvalds return 0; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 632b7d90a35STakashi Iwai #else /* !CONFIG_SND_VERBOSE_PROCFS */ 633e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 634e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 635e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 636b7d90a35STakashi Iwai #endif /* CONFIG_SND_VERBOSE_PROCFS */ 6371da177e4SLinus Torvalds 638ef46c7afSTakashi Iwai static const struct attribute_group *pcm_dev_attr_groups[]; 639ef46c7afSTakashi Iwai 6403d21ef0bSTakashi Iwai /* 6413d21ef0bSTakashi Iwai * PM callbacks: we need to deal only with suspend here, as the resume is 6423d21ef0bSTakashi Iwai * triggered either from user-space or the driver's resume callback 6433d21ef0bSTakashi Iwai */ 6443d21ef0bSTakashi Iwai #ifdef CONFIG_PM_SLEEP 6453d21ef0bSTakashi Iwai static int do_pcm_suspend(struct device *dev) 6463d21ef0bSTakashi Iwai { 6473d21ef0bSTakashi Iwai struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 6483d21ef0bSTakashi Iwai 6493d21ef0bSTakashi Iwai if (!pstr->pcm->no_device_suspend) 6503d21ef0bSTakashi Iwai snd_pcm_suspend_all(pstr->pcm); 6513d21ef0bSTakashi Iwai return 0; 6523d21ef0bSTakashi Iwai } 6533d21ef0bSTakashi Iwai #endif 6543d21ef0bSTakashi Iwai 6553d21ef0bSTakashi Iwai static const struct dev_pm_ops pcm_dev_pm_ops = { 6563d21ef0bSTakashi Iwai SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) 6573d21ef0bSTakashi Iwai }; 6583d21ef0bSTakashi Iwai 6593d21ef0bSTakashi Iwai /* device type for PCM -- basically only for passing PM callbacks */ 6603d21ef0bSTakashi Iwai static const struct device_type pcm_dev_type = { 6613d21ef0bSTakashi Iwai .name = "pcm", 6623d21ef0bSTakashi Iwai .pm = &pcm_dev_pm_ops, 6633d21ef0bSTakashi Iwai }; 6643d21ef0bSTakashi Iwai 6651da177e4SLinus Torvalds /** 6661da177e4SLinus Torvalds * snd_pcm_new_stream - create a new PCM stream 6671da177e4SLinus Torvalds * @pcm: the pcm instance 6681da177e4SLinus Torvalds * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 6691da177e4SLinus Torvalds * @substream_count: the number of substreams 6701da177e4SLinus Torvalds * 6711da177e4SLinus Torvalds * Creates a new stream for the pcm. 6721da177e4SLinus Torvalds * The corresponding stream on the pcm must have been empty before 6731da177e4SLinus Torvalds * calling this, i.e. zero must be given to the argument of 6741da177e4SLinus Torvalds * snd_pcm_new(). 6751da177e4SLinus Torvalds * 676eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 6771da177e4SLinus Torvalds */ 678877211f5STakashi Iwai int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 6791da177e4SLinus Torvalds { 6801da177e4SLinus Torvalds int idx, err; 681877211f5STakashi Iwai struct snd_pcm_str *pstr = &pcm->streams[stream]; 682877211f5STakashi Iwai struct snd_pcm_substream *substream, *prev; 6831da177e4SLinus Torvalds 6848eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 6851a60d4c5SIngo Molnar mutex_init(&pstr->oss.setup_mutex); 6861da177e4SLinus Torvalds #endif 6871da177e4SLinus Torvalds pstr->stream = stream; 6881da177e4SLinus Torvalds pstr->pcm = pcm; 6891da177e4SLinus Torvalds pstr->substream_count = substream_count; 690ef46c7afSTakashi Iwai if (!substream_count) 691ef46c7afSTakashi Iwai return 0; 692ef46c7afSTakashi Iwai 693ef46c7afSTakashi Iwai snd_device_initialize(&pstr->dev, pcm->card); 694ef46c7afSTakashi Iwai pstr->dev.groups = pcm_dev_attr_groups; 6953d21ef0bSTakashi Iwai pstr->dev.type = &pcm_dev_type; 696ef46c7afSTakashi Iwai dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, 697ef46c7afSTakashi Iwai stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 698ef46c7afSTakashi Iwai 699ef46c7afSTakashi Iwai if (!pcm->internal) { 7001da177e4SLinus Torvalds err = snd_pcm_stream_proc_init(pstr); 70173e77ba0STakashi Iwai if (err < 0) { 70209e56df8STakashi Iwai pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); 7031da177e4SLinus Torvalds return err; 7041da177e4SLinus Torvalds } 70573e77ba0STakashi Iwai } 7061da177e4SLinus Torvalds prev = NULL; 7071da177e4SLinus Torvalds for (idx = 0, prev = NULL; idx < substream_count; idx++) { 708ca2c0966STakashi Iwai substream = kzalloc(sizeof(*substream), GFP_KERNEL); 709ec0e9937STakashi Iwai if (!substream) 7101da177e4SLinus Torvalds return -ENOMEM; 7111da177e4SLinus Torvalds substream->pcm = pcm; 7121da177e4SLinus Torvalds substream->pstr = pstr; 7131da177e4SLinus Torvalds substream->number = idx; 7141da177e4SLinus Torvalds substream->stream = stream; 7151da177e4SLinus Torvalds sprintf(substream->name, "subdevice #%i", idx); 7161da177e4SLinus Torvalds substream->buffer_bytes_max = UINT_MAX; 7171da177e4SLinus Torvalds if (prev == NULL) 7181da177e4SLinus Torvalds pstr->substream = substream; 7191da177e4SLinus Torvalds else 7201da177e4SLinus Torvalds prev->next = substream; 721945e5038SLiam Girdwood 722945e5038SLiam Girdwood if (!pcm->internal) { 7231da177e4SLinus Torvalds err = snd_pcm_substream_proc_init(substream); 7241da177e4SLinus Torvalds if (err < 0) { 72509e56df8STakashi Iwai pcm_err(pcm, 72609e56df8STakashi Iwai "Error in snd_pcm_stream_proc_init\n"); 7274d361285SAkinobu Mita if (prev == NULL) 7284d361285SAkinobu Mita pstr->substream = NULL; 7294d361285SAkinobu Mita else 7304d361285SAkinobu Mita prev->next = NULL; 7311da177e4SLinus Torvalds kfree(substream); 7321da177e4SLinus Torvalds return err; 7331da177e4SLinus Torvalds } 734945e5038SLiam Girdwood } 7351da177e4SLinus Torvalds substream->group = &substream->self_group; 7361da177e4SLinus Torvalds spin_lock_init(&substream->self_group.lock); 737257f8cceSTakashi Iwai mutex_init(&substream->self_group.mutex); 7381da177e4SLinus Torvalds INIT_LIST_HEAD(&substream->self_group.substreams); 7391da177e4SLinus Torvalds list_add_tail(&substream->link_list, &substream->self_group.substreams); 7409c323fcbSTakashi Iwai atomic_set(&substream->mmap_count, 0); 7411da177e4SLinus Torvalds prev = substream; 7421da177e4SLinus Torvalds } 7431da177e4SLinus Torvalds return 0; 7441da177e4SLinus Torvalds } 745e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new_stream); 746e88e8ae6STakashi Iwai 747945e5038SLiam Girdwood static int _snd_pcm_new(struct snd_card *card, const char *id, int device, 748945e5038SLiam Girdwood int playback_count, int capture_count, bool internal, 749877211f5STakashi Iwai struct snd_pcm **rpcm) 7501da177e4SLinus Torvalds { 751877211f5STakashi Iwai struct snd_pcm *pcm; 7521da177e4SLinus Torvalds int err; 753877211f5STakashi Iwai static struct snd_device_ops ops = { 7541da177e4SLinus Torvalds .dev_free = snd_pcm_dev_free, 7551da177e4SLinus Torvalds .dev_register = snd_pcm_dev_register, 7561da177e4SLinus Torvalds .dev_disconnect = snd_pcm_dev_disconnect, 7571da177e4SLinus Torvalds }; 7588b645e4aSTakashi Iwai static struct snd_device_ops internal_ops = { 7598b645e4aSTakashi Iwai .dev_free = snd_pcm_dev_free, 7608b645e4aSTakashi Iwai }; 7611da177e4SLinus Torvalds 7627eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7637eaa943cSTakashi Iwai return -ENXIO; 7647eaa943cSTakashi Iwai if (rpcm) 7651da177e4SLinus Torvalds *rpcm = NULL; 766ca2c0966STakashi Iwai pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 767ec0e9937STakashi Iwai if (!pcm) 7681da177e4SLinus Torvalds return -ENOMEM; 7691da177e4SLinus Torvalds pcm->card = card; 7701da177e4SLinus Torvalds pcm->device = device; 771945e5038SLiam Girdwood pcm->internal = internal; 772b95bd3a4STakashi Iwai mutex_init(&pcm->open_mutex); 773b95bd3a4STakashi Iwai init_waitqueue_head(&pcm->open_wait); 774b95bd3a4STakashi Iwai INIT_LIST_HEAD(&pcm->list); 77573e77ba0STakashi Iwai if (id) 7761da177e4SLinus Torvalds strlcpy(pcm->id, id, sizeof(pcm->id)); 77797d15a14SMarkus Elfring 77897d15a14SMarkus Elfring err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 77997d15a14SMarkus Elfring playback_count); 78097d15a14SMarkus Elfring if (err < 0) 78197d15a14SMarkus Elfring goto free_pcm; 78297d15a14SMarkus Elfring 78397d15a14SMarkus Elfring err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); 78497d15a14SMarkus Elfring if (err < 0) 78597d15a14SMarkus Elfring goto free_pcm; 78697d15a14SMarkus Elfring 7878b645e4aSTakashi Iwai err = snd_device_new(card, SNDRV_DEV_PCM, pcm, 7888b645e4aSTakashi Iwai internal ? &internal_ops : &ops); 78997d15a14SMarkus Elfring if (err < 0) 79097d15a14SMarkus Elfring goto free_pcm; 79197d15a14SMarkus Elfring 7927eaa943cSTakashi Iwai if (rpcm) 7931da177e4SLinus Torvalds *rpcm = pcm; 7941da177e4SLinus Torvalds return 0; 79597d15a14SMarkus Elfring 79697d15a14SMarkus Elfring free_pcm: 79797d15a14SMarkus Elfring snd_pcm_free(pcm); 79897d15a14SMarkus Elfring return err; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds 801945e5038SLiam Girdwood /** 802945e5038SLiam Girdwood * snd_pcm_new - create a new PCM instance 803945e5038SLiam Girdwood * @card: the card instance 804945e5038SLiam Girdwood * @id: the id string 805945e5038SLiam Girdwood * @device: the device index (zero based) 806945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 807945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 808945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 809945e5038SLiam Girdwood * 810945e5038SLiam Girdwood * Creates a new PCM instance. 811945e5038SLiam Girdwood * 812945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 813945e5038SLiam Girdwood * via snd_pcm_set_ops(). 814945e5038SLiam Girdwood * 815eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 816945e5038SLiam Girdwood */ 817945e5038SLiam Girdwood int snd_pcm_new(struct snd_card *card, const char *id, int device, 818945e5038SLiam Girdwood int playback_count, int capture_count, struct snd_pcm **rpcm) 819945e5038SLiam Girdwood { 820945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 821945e5038SLiam Girdwood false, rpcm); 822945e5038SLiam Girdwood } 823e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new); 824e88e8ae6STakashi Iwai 825945e5038SLiam Girdwood /** 826945e5038SLiam Girdwood * snd_pcm_new_internal - create a new internal PCM instance 827945e5038SLiam Girdwood * @card: the card instance 828945e5038SLiam Girdwood * @id: the id string 829945e5038SLiam Girdwood * @device: the device index (zero based - shared with normal PCMs) 830945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 831945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 832945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 833945e5038SLiam Girdwood * 834945e5038SLiam Girdwood * Creates a new internal PCM instance with no userspace device or procfs 835945e5038SLiam Girdwood * entries. This is used by ASoC Back End PCMs in order to create a PCM that 836945e5038SLiam Girdwood * will only be used internally by kernel drivers. i.e. it cannot be opened 837945e5038SLiam Girdwood * by userspace. It provides existing ASoC components drivers with a substream 838945e5038SLiam Girdwood * and access to any private data. 839945e5038SLiam Girdwood * 840945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 841945e5038SLiam Girdwood * via snd_pcm_set_ops(). 842945e5038SLiam Girdwood * 843eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 844945e5038SLiam Girdwood */ 845945e5038SLiam Girdwood int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, 846945e5038SLiam Girdwood int playback_count, int capture_count, 847945e5038SLiam Girdwood struct snd_pcm **rpcm) 848945e5038SLiam Girdwood { 849945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 850945e5038SLiam Girdwood true, rpcm); 851945e5038SLiam Girdwood } 852945e5038SLiam Girdwood EXPORT_SYMBOL(snd_pcm_new_internal); 853945e5038SLiam Girdwood 854a8ff48cbSTakashi Iwai static void free_chmap(struct snd_pcm_str *pstr) 855a8ff48cbSTakashi Iwai { 856a8ff48cbSTakashi Iwai if (pstr->chmap_kctl) { 857a8ff48cbSTakashi Iwai snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); 858a8ff48cbSTakashi Iwai pstr->chmap_kctl = NULL; 859a8ff48cbSTakashi Iwai } 860a8ff48cbSTakashi Iwai } 861a8ff48cbSTakashi Iwai 862877211f5STakashi Iwai static void snd_pcm_free_stream(struct snd_pcm_str * pstr) 8631da177e4SLinus Torvalds { 864877211f5STakashi Iwai struct snd_pcm_substream *substream, *substream_next; 8658eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 866877211f5STakashi Iwai struct snd_pcm_oss_setup *setup, *setupn; 8671da177e4SLinus Torvalds #endif 868480e32ebSTakashi Iwai 869480e32ebSTakashi Iwai /* free all proc files under the stream */ 870480e32ebSTakashi Iwai snd_pcm_stream_proc_done(pstr); 871480e32ebSTakashi Iwai 8721da177e4SLinus Torvalds substream = pstr->substream; 8731da177e4SLinus Torvalds while (substream) { 8741da177e4SLinus Torvalds substream_next = substream->next; 875c461482cSTakashi Iwai snd_pcm_timer_done(substream); 8761da177e4SLinus Torvalds kfree(substream); 8771da177e4SLinus Torvalds substream = substream_next; 8781da177e4SLinus Torvalds } 8798eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 8801da177e4SLinus Torvalds for (setup = pstr->oss.setup_list; setup; setup = setupn) { 8811da177e4SLinus Torvalds setupn = setup->next; 8821da177e4SLinus Torvalds kfree(setup->task_name); 8831da177e4SLinus Torvalds kfree(setup); 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds #endif 886a8ff48cbSTakashi Iwai free_chmap(pstr); 887ef46c7afSTakashi Iwai if (pstr->substream_count) 888ef46c7afSTakashi Iwai put_device(&pstr->dev); 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds 89158f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 89258f30d65STakashi Iwai #define pcm_call_notify(pcm, call) \ 89358f30d65STakashi Iwai do { \ 89458f30d65STakashi Iwai struct snd_pcm_notify *_notify; \ 89558f30d65STakashi Iwai list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ 89658f30d65STakashi Iwai _notify->call(pcm); \ 89758f30d65STakashi Iwai } while (0) 89858f30d65STakashi Iwai #else 8999aee03f3SArnd Bergmann #define pcm_call_notify(pcm, call) do {} while (0) 90058f30d65STakashi Iwai #endif 90158f30d65STakashi Iwai 902877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm) 9031da177e4SLinus Torvalds { 9047eaa943cSTakashi Iwai if (!pcm) 9057eaa943cSTakashi Iwai return 0; 90658f30d65STakashi Iwai if (!pcm->internal) 90758f30d65STakashi Iwai pcm_call_notify(pcm, n_unregister); 9081da177e4SLinus Torvalds if (pcm->private_free) 9091da177e4SLinus Torvalds pcm->private_free(pcm); 9101da177e4SLinus Torvalds snd_pcm_lib_preallocate_free_for_all(pcm); 9111da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 9121da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 9131da177e4SLinus Torvalds kfree(pcm); 9141da177e4SLinus Torvalds return 0; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds 917877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device) 9181da177e4SLinus Torvalds { 919877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 9201da177e4SLinus Torvalds return snd_pcm_free(pcm); 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 9233bf75f9bSTakashi Iwai int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 9243bf75f9bSTakashi Iwai struct file *file, 925877211f5STakashi Iwai struct snd_pcm_substream **rsubstream) 9261da177e4SLinus Torvalds { 927877211f5STakashi Iwai struct snd_pcm_str * pstr; 928877211f5STakashi Iwai struct snd_pcm_substream *substream; 929877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 930877211f5STakashi Iwai struct snd_card *card; 93123c18d4bSTakashi Iwai int prefer_subdevice; 9321da177e4SLinus Torvalds size_t size; 9331da177e4SLinus Torvalds 9347eaa943cSTakashi Iwai if (snd_BUG_ON(!pcm || !rsubstream)) 9357eaa943cSTakashi Iwai return -ENXIO; 936ad876c86STakashi Iwai if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && 937ad876c86STakashi Iwai stream != SNDRV_PCM_STREAM_CAPTURE)) 938ad876c86STakashi Iwai return -EINVAL; 9391da177e4SLinus Torvalds *rsubstream = NULL; 9401da177e4SLinus Torvalds pstr = &pcm->streams[stream]; 9413bf75f9bSTakashi Iwai if (pstr->substream == NULL || pstr->substream_count == 0) 9421da177e4SLinus Torvalds return -ENODEV; 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds card = pcm->card; 94523c18d4bSTakashi Iwai prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 948ad876c86STakashi Iwai int opposite = !stream; 949ad876c86STakashi Iwai 950ad876c86STakashi Iwai for (substream = pcm->streams[opposite].substream; substream; 951ad876c86STakashi Iwai substream = substream->next) { 9521da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 9531da177e4SLinus Torvalds return -EAGAIN; 9541da177e4SLinus Torvalds } 9551da177e4SLinus Torvalds } 9561da177e4SLinus Torvalds 9570df63e44STakashi Iwai if (file->f_flags & O_APPEND) { 9580df63e44STakashi Iwai if (prefer_subdevice < 0) { 9590df63e44STakashi Iwai if (pstr->substream_count > 1) 9600df63e44STakashi Iwai return -EINVAL; /* must be unique */ 9610df63e44STakashi Iwai substream = pstr->substream; 9620df63e44STakashi Iwai } else { 9630df63e44STakashi Iwai for (substream = pstr->substream; substream; 9640df63e44STakashi Iwai substream = substream->next) 9650df63e44STakashi Iwai if (substream->number == prefer_subdevice) 9660df63e44STakashi Iwai break; 9670df63e44STakashi Iwai } 9680df63e44STakashi Iwai if (! substream) 9690df63e44STakashi Iwai return -ENODEV; 9700df63e44STakashi Iwai if (! SUBSTREAM_BUSY(substream)) 9710df63e44STakashi Iwai return -EBADFD; 9720df63e44STakashi Iwai substream->ref_count++; 9730df63e44STakashi Iwai *rsubstream = substream; 9740df63e44STakashi Iwai return 0; 9750df63e44STakashi Iwai } 9760df63e44STakashi Iwai 977ad876c86STakashi Iwai for (substream = pstr->substream; substream; substream = substream->next) { 978ad876c86STakashi Iwai if (!SUBSTREAM_BUSY(substream) && 979ad876c86STakashi Iwai (prefer_subdevice == -1 || 980ad876c86STakashi Iwai substream->number == prefer_subdevice)) 9811da177e4SLinus Torvalds break; 982ad876c86STakashi Iwai } 9831da177e4SLinus Torvalds if (substream == NULL) 9841da177e4SLinus Torvalds return -EAGAIN; 9851da177e4SLinus Torvalds 986ca2c0966STakashi Iwai runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 9871da177e4SLinus Torvalds if (runtime == NULL) 9881da177e4SLinus Torvalds return -ENOMEM; 9891da177e4SLinus Torvalds 990877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 9911da177e4SLinus Torvalds runtime->status = snd_malloc_pages(size, GFP_KERNEL); 9921da177e4SLinus Torvalds if (runtime->status == NULL) { 9931da177e4SLinus Torvalds kfree(runtime); 9941da177e4SLinus Torvalds return -ENOMEM; 9951da177e4SLinus Torvalds } 9961da177e4SLinus Torvalds memset((void*)runtime->status, 0, size); 9971da177e4SLinus Torvalds 998877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 9991da177e4SLinus Torvalds runtime->control = snd_malloc_pages(size, GFP_KERNEL); 10001da177e4SLinus Torvalds if (runtime->control == NULL) { 1001877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 1002877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 10031da177e4SLinus Torvalds kfree(runtime); 10041da177e4SLinus Torvalds return -ENOMEM; 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds memset((void*)runtime->control, 0, size); 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds init_waitqueue_head(&runtime->sleep); 1009c91a988dSJaroslav Kysela init_waitqueue_head(&runtime->tsleep); 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds runtime->status->state = SNDRV_PCM_STATE_OPEN; 10121da177e4SLinus Torvalds 10131da177e4SLinus Torvalds substream->runtime = runtime; 10141da177e4SLinus Torvalds substream->private_data = pcm->private_data; 10150df63e44STakashi Iwai substream->ref_count = 1; 10160df63e44STakashi Iwai substream->f_flags = file->f_flags; 1017e7373b70SClemens Ladisch substream->pid = get_pid(task_pid(current)); 10181da177e4SLinus Torvalds pstr->substream_opened++; 10191da177e4SLinus Torvalds *rsubstream = substream; 10201da177e4SLinus Torvalds return 0; 10211da177e4SLinus Torvalds } 10221da177e4SLinus Torvalds 10233bf75f9bSTakashi Iwai void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 10241da177e4SLinus Torvalds { 1025877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 10260df63e44STakashi Iwai 10277eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 10287eaa943cSTakashi Iwai return; 10291da177e4SLinus Torvalds runtime = substream->runtime; 10301da177e4SLinus Torvalds if (runtime->private_free != NULL) 10311da177e4SLinus Torvalds runtime->private_free(runtime); 1032877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 1033877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 1034877211f5STakashi Iwai snd_free_pages((void*)runtime->control, 1035877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 10361da177e4SLinus Torvalds kfree(runtime->hw_constraints.rules); 1037a820ccbeSTakashi Iwai /* Avoid concurrent access to runtime via PCM timer interface */ 1038a820ccbeSTakashi Iwai if (substream->timer) 1039a820ccbeSTakashi Iwai spin_lock_irq(&substream->timer->lock); 10401da177e4SLinus Torvalds substream->runtime = NULL; 1041a820ccbeSTakashi Iwai if (substream->timer) 1042a820ccbeSTakashi Iwai spin_unlock_irq(&substream->timer->lock); 1043a820ccbeSTakashi Iwai kfree(runtime); 1044e7373b70SClemens Ladisch put_pid(substream->pid); 1045e7373b70SClemens Ladisch substream->pid = NULL; 10461da177e4SLinus Torvalds substream->pstr->substream_opened--; 10471da177e4SLinus Torvalds } 10481da177e4SLinus Torvalds 1049d80f19faSGreg Kroah-Hartman static ssize_t show_pcm_class(struct device *dev, 1050d80f19faSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 10519d19f48cSTakashi Iwai { 105260b93030STakashi Iwai struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 105360b93030STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 10549d19f48cSTakashi Iwai const char *str; 10559d19f48cSTakashi Iwai static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 10569d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_GENERIC] = "generic", 10579d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MULTI] = "multi", 10589d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MODEM] = "modem", 10599d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 10609d19f48cSTakashi Iwai }; 10619d19f48cSTakashi Iwai 106260b93030STakashi Iwai if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) 10639d19f48cSTakashi Iwai str = "none"; 10649d19f48cSTakashi Iwai else 10659d19f48cSTakashi Iwai str = strs[pcm->dev_class]; 10669d19f48cSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", str); 10679d19f48cSTakashi Iwai } 10689d19f48cSTakashi Iwai 10696a73cf46SJoe Perches static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL); 1070caa751baSTakashi Iwai static struct attribute *pcm_dev_attrs[] = { 1071caa751baSTakashi Iwai &dev_attr_pcm_class.attr, 1072caa751baSTakashi Iwai NULL 1073caa751baSTakashi Iwai }; 1074caa751baSTakashi Iwai 1075343fe850SArvind Yadav static const struct attribute_group pcm_dev_attr_group = { 1076caa751baSTakashi Iwai .attrs = pcm_dev_attrs, 1077caa751baSTakashi Iwai }; 1078caa751baSTakashi Iwai 1079caa751baSTakashi Iwai static const struct attribute_group *pcm_dev_attr_groups[] = { 1080caa751baSTakashi Iwai &pcm_dev_attr_group, 1081caa751baSTakashi Iwai NULL 1082caa751baSTakashi Iwai }; 10839d19f48cSTakashi Iwai 1084877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device) 10851da177e4SLinus Torvalds { 1086f87135f5SClemens Ladisch int cidx, err; 1087877211f5STakashi Iwai struct snd_pcm_substream *substream; 10884b3be6afSJulia Lawall struct snd_pcm *pcm; 10891da177e4SLinus Torvalds 10904b3be6afSJulia Lawall if (snd_BUG_ON(!device || !device->device_data)) 10917eaa943cSTakashi Iwai return -ENXIO; 10924b3be6afSJulia Lawall pcm = device->device_data; 1093b95bd3a4STakashi Iwai 10941a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1095f90c06a2SPawel MOLL err = snd_pcm_add(pcm); 1096b95bd3a4STakashi Iwai if (err) 1097b95bd3a4STakashi Iwai goto unlock; 10981da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 10991da177e4SLinus Torvalds int devtype = -1; 1100b95bd3a4STakashi Iwai if (pcm->streams[cidx].substream == NULL) 11011da177e4SLinus Torvalds continue; 11021da177e4SLinus Torvalds switch (cidx) { 11031da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 11041da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 11051da177e4SLinus Torvalds break; 11061da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 11071da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 11081da177e4SLinus Torvalds break; 11091da177e4SLinus Torvalds } 1110c78085fcSJohannes Berg /* register pcm */ 111140a4b263STakashi Iwai err = snd_register_device(devtype, pcm->card, pcm->device, 111240a4b263STakashi Iwai &snd_pcm_f_ops[cidx], pcm, 111340a4b263STakashi Iwai &pcm->streams[cidx].dev); 1114c78085fcSJohannes Berg if (err < 0) { 1115b95bd3a4STakashi Iwai list_del_init(&pcm->list); 1116b95bd3a4STakashi Iwai goto unlock; 11171da177e4SLinus Torvalds } 1118caa751baSTakashi Iwai 11191da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 11201da177e4SLinus Torvalds snd_pcm_timer_init(substream); 11211da177e4SLinus Torvalds } 11229244b2c3SJohannes Berg 112358f30d65STakashi Iwai pcm_call_notify(pcm, n_register); 11249244b2c3SJohannes Berg 1125b95bd3a4STakashi Iwai unlock: 11261a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1127b95bd3a4STakashi Iwai return err; 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds 1130877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device) 11311da177e4SLinus Torvalds { 1132877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 1133877211f5STakashi Iwai struct snd_pcm_substream *substream; 113440a4b263STakashi Iwai int cidx; 11351da177e4SLinus Torvalds 11361a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11379b0573c0STakashi Iwai mutex_lock(&pcm->open_mutex); 11380914f796STakashi Iwai wake_up(&pcm->open_wait); 1139f87135f5SClemens Ladisch list_del_init(&pcm->list); 1140646e1dd8STakashi Iwai for (cidx = 0; cidx < 2; cidx++) { 11419b0573c0STakashi Iwai for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { 11429b0573c0STakashi Iwai snd_pcm_stream_lock_irq(substream); 11430914f796STakashi Iwai if (substream->runtime) { 11446ca73de7STakashi Iwai if (snd_pcm_running(substream)) 11456ca73de7STakashi Iwai snd_pcm_stop(substream, 11466ca73de7STakashi Iwai SNDRV_PCM_STATE_DISCONNECTED); 11476ca73de7STakashi Iwai /* to be sure, set the state unconditionally */ 11481da177e4SLinus Torvalds substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 11490914f796STakashi Iwai wake_up(&substream->runtime->sleep); 11500914f796STakashi Iwai wake_up(&substream->runtime->tsleep); 11510914f796STakashi Iwai } 11529b0573c0STakashi Iwai snd_pcm_stream_unlock_irq(substream); 11539b0573c0STakashi Iwai } 1154646e1dd8STakashi Iwai } 11558b645e4aSTakashi Iwai 115658f30d65STakashi Iwai pcm_call_notify(pcm, n_disconnect); 11571da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 115840a4b263STakashi Iwai snd_unregister_device(&pcm->streams[cidx].dev); 1159a8ff48cbSTakashi Iwai free_chmap(&pcm->streams[cidx]); 11601da177e4SLinus Torvalds } 11619b0573c0STakashi Iwai mutex_unlock(&pcm->open_mutex); 11621a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1163c461482cSTakashi Iwai return 0; 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds 116658f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 116730b771cfSTakashi Iwai /** 116830b771cfSTakashi Iwai * snd_pcm_notify - Add/remove the notify list 116930b771cfSTakashi Iwai * @notify: PCM notify list 117030b771cfSTakashi Iwai * @nfree: 0 = register, 1 = unregister 117130b771cfSTakashi Iwai * 117230b771cfSTakashi Iwai * This adds the given notifier to the global list so that the callback is 117330b771cfSTakashi Iwai * called for each registered PCM devices. This exists only for PCM OSS 117430b771cfSTakashi Iwai * emulation, so far. 117530b771cfSTakashi Iwai */ 1176877211f5STakashi Iwai int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 11771da177e4SLinus Torvalds { 11789244b2c3SJohannes Berg struct snd_pcm *pcm; 11791da177e4SLinus Torvalds 11807eaa943cSTakashi Iwai if (snd_BUG_ON(!notify || 11817eaa943cSTakashi Iwai !notify->n_register || 11827eaa943cSTakashi Iwai !notify->n_unregister || 11837eaa943cSTakashi Iwai !notify->n_disconnect)) 11847eaa943cSTakashi Iwai return -EINVAL; 11851a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11861da177e4SLinus Torvalds if (nfree) { 11871da177e4SLinus Torvalds list_del(¬ify->list); 11889244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 11899244b2c3SJohannes Berg notify->n_unregister(pcm); 11901da177e4SLinus Torvalds } else { 11911da177e4SLinus Torvalds list_add_tail(¬ify->list, &snd_pcm_notify_list); 11929244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 11939244b2c3SJohannes Berg notify->n_register(pcm); 11941da177e4SLinus Torvalds } 11951a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 11961da177e4SLinus Torvalds return 0; 11971da177e4SLinus Torvalds } 1198e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_notify); 119958f30d65STakashi Iwai #endif /* CONFIG_SND_PCM_OSS */ 1200e88e8ae6STakashi Iwai 1201cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS 12021da177e4SLinus Torvalds /* 12031da177e4SLinus Torvalds * Info interface 12041da177e4SLinus Torvalds */ 12051da177e4SLinus Torvalds 1206877211f5STakashi Iwai static void snd_pcm_proc_read(struct snd_info_entry *entry, 1207877211f5STakashi Iwai struct snd_info_buffer *buffer) 12081da177e4SLinus Torvalds { 1209877211f5STakashi Iwai struct snd_pcm *pcm; 12101da177e4SLinus Torvalds 12111a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 12129244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 1213f87135f5SClemens Ladisch snd_iprintf(buffer, "%02i-%02i: %s : %s", 1214f87135f5SClemens Ladisch pcm->card->number, pcm->device, pcm->id, pcm->name); 12151da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1216877211f5STakashi Iwai snd_iprintf(buffer, " : playback %i", 1217877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 12181da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 1219877211f5STakashi Iwai snd_iprintf(buffer, " : capture %i", 1220877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 12211da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 12221da177e4SLinus Torvalds } 12231a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 12241da177e4SLinus Torvalds } 12251da177e4SLinus Torvalds 12266581f4e7STakashi Iwai static struct snd_info_entry *snd_pcm_proc_entry; 12271da177e4SLinus Torvalds 1228e28563ccSTakashi Iwai static void snd_pcm_proc_init(void) 12291da177e4SLinus Torvalds { 1230877211f5STakashi Iwai struct snd_info_entry *entry; 12311da177e4SLinus Torvalds 1232c8da9be4SMarkus Elfring entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL); 1233c8da9be4SMarkus Elfring if (entry) { 1234bf850204STakashi Iwai snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 12351da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 12361da177e4SLinus Torvalds snd_info_free_entry(entry); 12371da177e4SLinus Torvalds entry = NULL; 12381da177e4SLinus Torvalds } 12391da177e4SLinus Torvalds } 12401da177e4SLinus Torvalds snd_pcm_proc_entry = entry; 1241e28563ccSTakashi Iwai } 1242e28563ccSTakashi Iwai 1243e28563ccSTakashi Iwai static void snd_pcm_proc_done(void) 1244e28563ccSTakashi Iwai { 1245746d4a02STakashi Iwai snd_info_free_entry(snd_pcm_proc_entry); 1246e28563ccSTakashi Iwai } 1247e28563ccSTakashi Iwai 1248cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */ 1249e28563ccSTakashi Iwai #define snd_pcm_proc_init() 1250e28563ccSTakashi Iwai #define snd_pcm_proc_done() 1251cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */ 1252e28563ccSTakashi Iwai 1253e28563ccSTakashi Iwai 1254e28563ccSTakashi Iwai /* 1255e28563ccSTakashi Iwai * ENTRY functions 1256e28563ccSTakashi Iwai */ 1257e28563ccSTakashi Iwai 1258e28563ccSTakashi Iwai static int __init alsa_pcm_init(void) 1259e28563ccSTakashi Iwai { 1260e28563ccSTakashi Iwai snd_ctl_register_ioctl(snd_pcm_control_ioctl); 1261e28563ccSTakashi Iwai snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 1262e28563ccSTakashi Iwai snd_pcm_proc_init(); 12631da177e4SLinus Torvalds return 0; 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds static void __exit alsa_pcm_exit(void) 12671da177e4SLinus Torvalds { 12681da177e4SLinus Torvalds snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 12691da177e4SLinus Torvalds snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 1270e28563ccSTakashi Iwai snd_pcm_proc_done(); 12711da177e4SLinus Torvalds } 12721da177e4SLinus Torvalds 12731da177e4SLinus Torvalds module_init(alsa_pcm_init) 12741da177e4SLinus Torvalds module_exit(alsa_pcm_exit) 1275