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); 5391da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5401da177e4SLinus Torvalds snd_info_free_entry(entry); 5411da177e4SLinus Torvalds entry = NULL; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds } 5441da177e4SLinus Torvalds pstr->proc_info_entry = entry; 5451da177e4SLinus Torvalds 54661fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 547c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 548c8da9be4SMarkus Elfring pstr->proc_root); 549c8da9be4SMarkus Elfring if (entry) { 5501da177e4SLinus Torvalds entry->c.text.read = snd_pcm_xrun_debug_read; 5511da177e4SLinus Torvalds entry->c.text.write = snd_pcm_xrun_debug_write; 5526a73cf46SJoe Perches entry->mode |= 0200; 5531da177e4SLinus Torvalds entry->private_data = pstr; 5541da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5551da177e4SLinus Torvalds snd_info_free_entry(entry); 5561da177e4SLinus Torvalds entry = NULL; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = entry; 5601da177e4SLinus Torvalds #endif 5611da177e4SLinus Torvalds return 0; 5621da177e4SLinus Torvalds } 5631da177e4SLinus Torvalds 564877211f5STakashi Iwai static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 5651da177e4SLinus Torvalds { 56661fb63c0SJaroslav Kysela #ifdef CONFIG_SND_PCM_XRUN_DEBUG 567746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_xrun_debug_entry); 5681da177e4SLinus Torvalds pstr->proc_xrun_debug_entry = NULL; 5691da177e4SLinus Torvalds #endif 570746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_info_entry); 5711da177e4SLinus Torvalds pstr->proc_info_entry = NULL; 572746d4a02STakashi Iwai snd_info_free_entry(pstr->proc_root); 5731da177e4SLinus Torvalds pstr->proc_root = NULL; 5741da177e4SLinus Torvalds return 0; 5751da177e4SLinus Torvalds } 5761da177e4SLinus Torvalds 577877211f5STakashi Iwai static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5781da177e4SLinus Torvalds { 579877211f5STakashi Iwai struct snd_info_entry *entry; 580877211f5STakashi Iwai struct snd_card *card; 5811da177e4SLinus Torvalds char name[16]; 5821da177e4SLinus Torvalds 5831da177e4SLinus Torvalds card = substream->pcm->card; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds sprintf(name, "sub%i", substream->number); 586c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, name, 587c8da9be4SMarkus Elfring substream->pstr->proc_root); 588c8da9be4SMarkus Elfring if (!entry) 5891da177e4SLinus Torvalds return -ENOMEM; 5906a73cf46SJoe Perches entry->mode = S_IFDIR | 0555; 5911da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 5921da177e4SLinus Torvalds snd_info_free_entry(entry); 5931da177e4SLinus Torvalds return -ENOMEM; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds substream->proc_root = entry; 596c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, "info", substream->proc_root); 597c8da9be4SMarkus Elfring if (entry) { 598bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 599bf850204STakashi Iwai snd_pcm_substream_proc_info_read); 6001da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6011da177e4SLinus Torvalds snd_info_free_entry(entry); 6021da177e4SLinus Torvalds entry = NULL; 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds substream->proc_info_entry = entry; 606c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, "hw_params", 607c8da9be4SMarkus Elfring substream->proc_root); 608c8da9be4SMarkus Elfring if (entry) { 609bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 610bf850204STakashi Iwai snd_pcm_substream_proc_hw_params_read); 6111da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6121da177e4SLinus Torvalds snd_info_free_entry(entry); 6131da177e4SLinus Torvalds entry = NULL; 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds } 6161da177e4SLinus Torvalds substream->proc_hw_params_entry = entry; 617c8da9be4SMarkus Elfring entry = snd_info_create_card_entry(card, "sw_params", 618c8da9be4SMarkus Elfring substream->proc_root); 619c8da9be4SMarkus Elfring if (entry) { 620bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 621bf850204STakashi Iwai snd_pcm_substream_proc_sw_params_read); 6221da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6231da177e4SLinus Torvalds snd_info_free_entry(entry); 6241da177e4SLinus Torvalds entry = NULL; 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds } 6271da177e4SLinus Torvalds substream->proc_sw_params_entry = entry; 628e6b4c525STakashi Iwai entry = snd_info_create_card_entry(card, "status", 629e6b4c525STakashi Iwai substream->proc_root); 630c8da9be4SMarkus Elfring if (entry) { 631bf850204STakashi Iwai snd_info_set_text_ops(entry, substream, 632bf850204STakashi Iwai snd_pcm_substream_proc_status_read); 6331da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 6341da177e4SLinus Torvalds snd_info_free_entry(entry); 6351da177e4SLinus Torvalds entry = NULL; 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds substream->proc_status_entry = entry; 6391da177e4SLinus Torvalds 6402b30d411STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 6412b30d411STakashi Iwai entry = snd_info_create_card_entry(card, "xrun_injection", 6422b30d411STakashi Iwai substream->proc_root); 6432b30d411STakashi Iwai if (entry) { 6442b30d411STakashi Iwai entry->private_data = substream; 6452b30d411STakashi Iwai entry->c.text.read = NULL; 6462b30d411STakashi Iwai entry->c.text.write = snd_pcm_xrun_injection_write; 6476a73cf46SJoe Perches entry->mode = S_IFREG | 0200; 6482b30d411STakashi Iwai if (snd_info_register(entry) < 0) { 6492b30d411STakashi Iwai snd_info_free_entry(entry); 6502b30d411STakashi Iwai entry = NULL; 6512b30d411STakashi Iwai } 6522b30d411STakashi Iwai } 6532b30d411STakashi Iwai substream->proc_xrun_injection_entry = entry; 6542b30d411STakashi Iwai #endif /* CONFIG_SND_PCM_XRUN_DEBUG */ 6552b30d411STakashi Iwai 6561da177e4SLinus Torvalds return 0; 6571da177e4SLinus Torvalds } 6581da177e4SLinus Torvalds 659877211f5STakashi Iwai static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) 6601da177e4SLinus Torvalds { 661746d4a02STakashi Iwai snd_info_free_entry(substream->proc_info_entry); 6621da177e4SLinus Torvalds substream->proc_info_entry = NULL; 663746d4a02STakashi Iwai snd_info_free_entry(substream->proc_hw_params_entry); 6641da177e4SLinus Torvalds substream->proc_hw_params_entry = NULL; 665746d4a02STakashi Iwai snd_info_free_entry(substream->proc_sw_params_entry); 6661da177e4SLinus Torvalds substream->proc_sw_params_entry = NULL; 667746d4a02STakashi Iwai snd_info_free_entry(substream->proc_status_entry); 6681da177e4SLinus Torvalds substream->proc_status_entry = NULL; 6692b30d411STakashi Iwai #ifdef CONFIG_SND_PCM_XRUN_DEBUG 6702b30d411STakashi Iwai snd_info_free_entry(substream->proc_xrun_injection_entry); 6712b30d411STakashi Iwai substream->proc_xrun_injection_entry = NULL; 6722b30d411STakashi Iwai #endif 673746d4a02STakashi Iwai snd_info_free_entry(substream->proc_root); 6741da177e4SLinus Torvalds substream->proc_root = NULL; 6751da177e4SLinus Torvalds return 0; 6761da177e4SLinus Torvalds } 677b7d90a35STakashi Iwai #else /* !CONFIG_SND_VERBOSE_PROCFS */ 678e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 679e28563ccSTakashi Iwai static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 680e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 681e28563ccSTakashi Iwai static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; } 682b7d90a35STakashi Iwai #endif /* CONFIG_SND_VERBOSE_PROCFS */ 6831da177e4SLinus Torvalds 684ef46c7afSTakashi Iwai static const struct attribute_group *pcm_dev_attr_groups[]; 685ef46c7afSTakashi Iwai 6861da177e4SLinus Torvalds /** 6871da177e4SLinus Torvalds * snd_pcm_new_stream - create a new PCM stream 6881da177e4SLinus Torvalds * @pcm: the pcm instance 6891da177e4SLinus Torvalds * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 6901da177e4SLinus Torvalds * @substream_count: the number of substreams 6911da177e4SLinus Torvalds * 6921da177e4SLinus Torvalds * Creates a new stream for the pcm. 6931da177e4SLinus Torvalds * The corresponding stream on the pcm must have been empty before 6941da177e4SLinus Torvalds * calling this, i.e. zero must be given to the argument of 6951da177e4SLinus Torvalds * snd_pcm_new(). 6961da177e4SLinus Torvalds * 697eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 6981da177e4SLinus Torvalds */ 699877211f5STakashi Iwai int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 7001da177e4SLinus Torvalds { 7011da177e4SLinus Torvalds int idx, err; 702877211f5STakashi Iwai struct snd_pcm_str *pstr = &pcm->streams[stream]; 703877211f5STakashi Iwai struct snd_pcm_substream *substream, *prev; 7041da177e4SLinus Torvalds 7058eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 7061a60d4c5SIngo Molnar mutex_init(&pstr->oss.setup_mutex); 7071da177e4SLinus Torvalds #endif 7081da177e4SLinus Torvalds pstr->stream = stream; 7091da177e4SLinus Torvalds pstr->pcm = pcm; 7101da177e4SLinus Torvalds pstr->substream_count = substream_count; 711ef46c7afSTakashi Iwai if (!substream_count) 712ef46c7afSTakashi Iwai return 0; 713ef46c7afSTakashi Iwai 714ef46c7afSTakashi Iwai snd_device_initialize(&pstr->dev, pcm->card); 715ef46c7afSTakashi Iwai pstr->dev.groups = pcm_dev_attr_groups; 716ef46c7afSTakashi Iwai dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, 717ef46c7afSTakashi Iwai stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 718ef46c7afSTakashi Iwai 719ef46c7afSTakashi Iwai if (!pcm->internal) { 7201da177e4SLinus Torvalds err = snd_pcm_stream_proc_init(pstr); 72173e77ba0STakashi Iwai if (err < 0) { 72209e56df8STakashi Iwai pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); 7231da177e4SLinus Torvalds return err; 7241da177e4SLinus Torvalds } 72573e77ba0STakashi Iwai } 7261da177e4SLinus Torvalds prev = NULL; 7271da177e4SLinus Torvalds for (idx = 0, prev = NULL; idx < substream_count; idx++) { 728ca2c0966STakashi Iwai substream = kzalloc(sizeof(*substream), GFP_KERNEL); 729ec0e9937STakashi Iwai if (!substream) 7301da177e4SLinus Torvalds return -ENOMEM; 7311da177e4SLinus Torvalds substream->pcm = pcm; 7321da177e4SLinus Torvalds substream->pstr = pstr; 7331da177e4SLinus Torvalds substream->number = idx; 7341da177e4SLinus Torvalds substream->stream = stream; 7351da177e4SLinus Torvalds sprintf(substream->name, "subdevice #%i", idx); 7361da177e4SLinus Torvalds substream->buffer_bytes_max = UINT_MAX; 7371da177e4SLinus Torvalds if (prev == NULL) 7381da177e4SLinus Torvalds pstr->substream = substream; 7391da177e4SLinus Torvalds else 7401da177e4SLinus Torvalds prev->next = substream; 741945e5038SLiam Girdwood 742945e5038SLiam Girdwood if (!pcm->internal) { 7431da177e4SLinus Torvalds err = snd_pcm_substream_proc_init(substream); 7441da177e4SLinus Torvalds if (err < 0) { 74509e56df8STakashi Iwai pcm_err(pcm, 74609e56df8STakashi Iwai "Error in snd_pcm_stream_proc_init\n"); 7474d361285SAkinobu Mita if (prev == NULL) 7484d361285SAkinobu Mita pstr->substream = NULL; 7494d361285SAkinobu Mita else 7504d361285SAkinobu Mita prev->next = NULL; 7511da177e4SLinus Torvalds kfree(substream); 7521da177e4SLinus Torvalds return err; 7531da177e4SLinus Torvalds } 754945e5038SLiam Girdwood } 7551da177e4SLinus Torvalds substream->group = &substream->self_group; 75673365cb1STakashi Iwai snd_pcm_group_init(&substream->self_group); 7571da177e4SLinus Torvalds list_add_tail(&substream->link_list, &substream->self_group.substreams); 7589c323fcbSTakashi Iwai atomic_set(&substream->mmap_count, 0); 7591da177e4SLinus Torvalds prev = substream; 7601da177e4SLinus Torvalds } 7611da177e4SLinus Torvalds return 0; 7621da177e4SLinus Torvalds } 763e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new_stream); 764e88e8ae6STakashi Iwai 765945e5038SLiam Girdwood static int _snd_pcm_new(struct snd_card *card, const char *id, int device, 766945e5038SLiam Girdwood int playback_count, int capture_count, bool internal, 767877211f5STakashi Iwai struct snd_pcm **rpcm) 7681da177e4SLinus Torvalds { 769877211f5STakashi Iwai struct snd_pcm *pcm; 7701da177e4SLinus Torvalds int err; 771877211f5STakashi Iwai static struct snd_device_ops ops = { 7721da177e4SLinus Torvalds .dev_free = snd_pcm_dev_free, 7731da177e4SLinus Torvalds .dev_register = snd_pcm_dev_register, 7741da177e4SLinus Torvalds .dev_disconnect = snd_pcm_dev_disconnect, 7751da177e4SLinus Torvalds }; 7768b645e4aSTakashi Iwai static struct snd_device_ops internal_ops = { 7778b645e4aSTakashi Iwai .dev_free = snd_pcm_dev_free, 7788b645e4aSTakashi Iwai }; 7791da177e4SLinus Torvalds 7807eaa943cSTakashi Iwai if (snd_BUG_ON(!card)) 7817eaa943cSTakashi Iwai return -ENXIO; 7827eaa943cSTakashi Iwai if (rpcm) 7831da177e4SLinus Torvalds *rpcm = NULL; 784ca2c0966STakashi Iwai pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 785ec0e9937STakashi Iwai if (!pcm) 7861da177e4SLinus Torvalds return -ENOMEM; 7871da177e4SLinus Torvalds pcm->card = card; 7881da177e4SLinus Torvalds pcm->device = device; 789945e5038SLiam Girdwood pcm->internal = internal; 790b95bd3a4STakashi Iwai mutex_init(&pcm->open_mutex); 791b95bd3a4STakashi Iwai init_waitqueue_head(&pcm->open_wait); 792b95bd3a4STakashi Iwai INIT_LIST_HEAD(&pcm->list); 79373e77ba0STakashi Iwai if (id) 7941da177e4SLinus Torvalds strlcpy(pcm->id, id, sizeof(pcm->id)); 79597d15a14SMarkus Elfring 79697d15a14SMarkus Elfring err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 79797d15a14SMarkus Elfring playback_count); 79897d15a14SMarkus Elfring if (err < 0) 79997d15a14SMarkus Elfring goto free_pcm; 80097d15a14SMarkus Elfring 80197d15a14SMarkus Elfring err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); 80297d15a14SMarkus Elfring if (err < 0) 80397d15a14SMarkus Elfring goto free_pcm; 80497d15a14SMarkus Elfring 8058b645e4aSTakashi Iwai err = snd_device_new(card, SNDRV_DEV_PCM, pcm, 8068b645e4aSTakashi Iwai internal ? &internal_ops : &ops); 80797d15a14SMarkus Elfring if (err < 0) 80897d15a14SMarkus Elfring goto free_pcm; 80997d15a14SMarkus Elfring 8107eaa943cSTakashi Iwai if (rpcm) 8111da177e4SLinus Torvalds *rpcm = pcm; 8121da177e4SLinus Torvalds return 0; 81397d15a14SMarkus Elfring 81497d15a14SMarkus Elfring free_pcm: 81597d15a14SMarkus Elfring snd_pcm_free(pcm); 81697d15a14SMarkus Elfring return err; 8171da177e4SLinus Torvalds } 8181da177e4SLinus Torvalds 819945e5038SLiam Girdwood /** 820945e5038SLiam Girdwood * snd_pcm_new - create a new PCM instance 821945e5038SLiam Girdwood * @card: the card instance 822945e5038SLiam Girdwood * @id: the id string 823945e5038SLiam Girdwood * @device: the device index (zero based) 824945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 825945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 826945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 827945e5038SLiam Girdwood * 828945e5038SLiam Girdwood * Creates a new PCM instance. 829945e5038SLiam Girdwood * 830945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 831945e5038SLiam Girdwood * via snd_pcm_set_ops(). 832945e5038SLiam Girdwood * 833eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 834945e5038SLiam Girdwood */ 835945e5038SLiam Girdwood int snd_pcm_new(struct snd_card *card, const char *id, int device, 836945e5038SLiam Girdwood int playback_count, int capture_count, struct snd_pcm **rpcm) 837945e5038SLiam Girdwood { 838945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 839945e5038SLiam Girdwood false, rpcm); 840945e5038SLiam Girdwood } 841e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_new); 842e88e8ae6STakashi Iwai 843945e5038SLiam Girdwood /** 844945e5038SLiam Girdwood * snd_pcm_new_internal - create a new internal PCM instance 845945e5038SLiam Girdwood * @card: the card instance 846945e5038SLiam Girdwood * @id: the id string 847945e5038SLiam Girdwood * @device: the device index (zero based - shared with normal PCMs) 848945e5038SLiam Girdwood * @playback_count: the number of substreams for playback 849945e5038SLiam Girdwood * @capture_count: the number of substreams for capture 850945e5038SLiam Girdwood * @rpcm: the pointer to store the new pcm instance 851945e5038SLiam Girdwood * 852945e5038SLiam Girdwood * Creates a new internal PCM instance with no userspace device or procfs 853945e5038SLiam Girdwood * entries. This is used by ASoC Back End PCMs in order to create a PCM that 854945e5038SLiam Girdwood * will only be used internally by kernel drivers. i.e. it cannot be opened 855945e5038SLiam Girdwood * by userspace. It provides existing ASoC components drivers with a substream 856945e5038SLiam Girdwood * and access to any private data. 857945e5038SLiam Girdwood * 858945e5038SLiam Girdwood * The pcm operators have to be set afterwards to the new instance 859945e5038SLiam Girdwood * via snd_pcm_set_ops(). 860945e5038SLiam Girdwood * 861eb7c06e8SYacine Belkadi * Return: Zero if successful, or a negative error code on failure. 862945e5038SLiam Girdwood */ 863945e5038SLiam Girdwood int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, 864945e5038SLiam Girdwood int playback_count, int capture_count, 865945e5038SLiam Girdwood struct snd_pcm **rpcm) 866945e5038SLiam Girdwood { 867945e5038SLiam Girdwood return _snd_pcm_new(card, id, device, playback_count, capture_count, 868945e5038SLiam Girdwood true, rpcm); 869945e5038SLiam Girdwood } 870945e5038SLiam Girdwood EXPORT_SYMBOL(snd_pcm_new_internal); 871945e5038SLiam Girdwood 872a8ff48cbSTakashi Iwai static void free_chmap(struct snd_pcm_str *pstr) 873a8ff48cbSTakashi Iwai { 874a8ff48cbSTakashi Iwai if (pstr->chmap_kctl) { 875a8ff48cbSTakashi Iwai snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl); 876a8ff48cbSTakashi Iwai pstr->chmap_kctl = NULL; 877a8ff48cbSTakashi Iwai } 878a8ff48cbSTakashi Iwai } 879a8ff48cbSTakashi Iwai 880877211f5STakashi Iwai static void snd_pcm_free_stream(struct snd_pcm_str * pstr) 8811da177e4SLinus Torvalds { 882877211f5STakashi Iwai struct snd_pcm_substream *substream, *substream_next; 8838eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 884877211f5STakashi Iwai struct snd_pcm_oss_setup *setup, *setupn; 8851da177e4SLinus Torvalds #endif 8861da177e4SLinus Torvalds substream = pstr->substream; 8871da177e4SLinus Torvalds while (substream) { 8881da177e4SLinus Torvalds substream_next = substream->next; 889c461482cSTakashi Iwai snd_pcm_timer_done(substream); 8901da177e4SLinus Torvalds snd_pcm_substream_proc_done(substream); 8911da177e4SLinus Torvalds kfree(substream); 8921da177e4SLinus Torvalds substream = substream_next; 8931da177e4SLinus Torvalds } 8941da177e4SLinus Torvalds snd_pcm_stream_proc_done(pstr); 8958eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 8961da177e4SLinus Torvalds for (setup = pstr->oss.setup_list; setup; setup = setupn) { 8971da177e4SLinus Torvalds setupn = setup->next; 8981da177e4SLinus Torvalds kfree(setup->task_name); 8991da177e4SLinus Torvalds kfree(setup); 9001da177e4SLinus Torvalds } 9011da177e4SLinus Torvalds #endif 902a8ff48cbSTakashi Iwai free_chmap(pstr); 903ef46c7afSTakashi Iwai if (pstr->substream_count) 904ef46c7afSTakashi Iwai put_device(&pstr->dev); 9051da177e4SLinus Torvalds } 9061da177e4SLinus Torvalds 90758f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 90858f30d65STakashi Iwai #define pcm_call_notify(pcm, call) \ 90958f30d65STakashi Iwai do { \ 91058f30d65STakashi Iwai struct snd_pcm_notify *_notify; \ 91158f30d65STakashi Iwai list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ 91258f30d65STakashi Iwai _notify->call(pcm); \ 91358f30d65STakashi Iwai } while (0) 91458f30d65STakashi Iwai #else 9159aee03f3SArnd Bergmann #define pcm_call_notify(pcm, call) do {} while (0) 91658f30d65STakashi Iwai #endif 91758f30d65STakashi Iwai 918877211f5STakashi Iwai static int snd_pcm_free(struct snd_pcm *pcm) 9191da177e4SLinus Torvalds { 9207eaa943cSTakashi Iwai if (!pcm) 9217eaa943cSTakashi Iwai return 0; 92258f30d65STakashi Iwai if (!pcm->internal) 92358f30d65STakashi Iwai pcm_call_notify(pcm, n_unregister); 9241da177e4SLinus Torvalds if (pcm->private_free) 9251da177e4SLinus Torvalds pcm->private_free(pcm); 9261da177e4SLinus Torvalds snd_pcm_lib_preallocate_free_for_all(pcm); 9271da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 9281da177e4SLinus Torvalds snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 9291da177e4SLinus Torvalds kfree(pcm); 9301da177e4SLinus Torvalds return 0; 9311da177e4SLinus Torvalds } 9321da177e4SLinus Torvalds 933877211f5STakashi Iwai static int snd_pcm_dev_free(struct snd_device *device) 9341da177e4SLinus Torvalds { 935877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 9361da177e4SLinus Torvalds return snd_pcm_free(pcm); 9371da177e4SLinus Torvalds } 9381da177e4SLinus Torvalds 9393bf75f9bSTakashi Iwai int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 9403bf75f9bSTakashi Iwai struct file *file, 941877211f5STakashi Iwai struct snd_pcm_substream **rsubstream) 9421da177e4SLinus Torvalds { 943877211f5STakashi Iwai struct snd_pcm_str * pstr; 944877211f5STakashi Iwai struct snd_pcm_substream *substream; 945877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 946877211f5STakashi Iwai struct snd_card *card; 94723c18d4bSTakashi Iwai int prefer_subdevice; 9481da177e4SLinus Torvalds size_t size; 9491da177e4SLinus Torvalds 9507eaa943cSTakashi Iwai if (snd_BUG_ON(!pcm || !rsubstream)) 9517eaa943cSTakashi Iwai return -ENXIO; 952ad876c86STakashi Iwai if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && 953ad876c86STakashi Iwai stream != SNDRV_PCM_STREAM_CAPTURE)) 954ad876c86STakashi Iwai return -EINVAL; 9551da177e4SLinus Torvalds *rsubstream = NULL; 9561da177e4SLinus Torvalds pstr = &pcm->streams[stream]; 9573bf75f9bSTakashi Iwai if (pstr->substream == NULL || pstr->substream_count == 0) 9581da177e4SLinus Torvalds return -ENODEV; 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds card = pcm->card; 96123c18d4bSTakashi Iwai prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); 9621da177e4SLinus Torvalds 9631da177e4SLinus Torvalds if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 964ad876c86STakashi Iwai int opposite = !stream; 965ad876c86STakashi Iwai 966ad876c86STakashi Iwai for (substream = pcm->streams[opposite].substream; substream; 967ad876c86STakashi Iwai substream = substream->next) { 9681da177e4SLinus Torvalds if (SUBSTREAM_BUSY(substream)) 9691da177e4SLinus Torvalds return -EAGAIN; 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds } 9721da177e4SLinus Torvalds 9730df63e44STakashi Iwai if (file->f_flags & O_APPEND) { 9740df63e44STakashi Iwai if (prefer_subdevice < 0) { 9750df63e44STakashi Iwai if (pstr->substream_count > 1) 9760df63e44STakashi Iwai return -EINVAL; /* must be unique */ 9770df63e44STakashi Iwai substream = pstr->substream; 9780df63e44STakashi Iwai } else { 9790df63e44STakashi Iwai for (substream = pstr->substream; substream; 9800df63e44STakashi Iwai substream = substream->next) 9810df63e44STakashi Iwai if (substream->number == prefer_subdevice) 9820df63e44STakashi Iwai break; 9830df63e44STakashi Iwai } 9840df63e44STakashi Iwai if (! substream) 9850df63e44STakashi Iwai return -ENODEV; 9860df63e44STakashi Iwai if (! SUBSTREAM_BUSY(substream)) 9870df63e44STakashi Iwai return -EBADFD; 9880df63e44STakashi Iwai substream->ref_count++; 9890df63e44STakashi Iwai *rsubstream = substream; 9900df63e44STakashi Iwai return 0; 9910df63e44STakashi Iwai } 9920df63e44STakashi Iwai 993ad876c86STakashi Iwai for (substream = pstr->substream; substream; substream = substream->next) { 994ad876c86STakashi Iwai if (!SUBSTREAM_BUSY(substream) && 995ad876c86STakashi Iwai (prefer_subdevice == -1 || 996ad876c86STakashi Iwai substream->number == prefer_subdevice)) 9971da177e4SLinus Torvalds break; 998ad876c86STakashi Iwai } 9991da177e4SLinus Torvalds if (substream == NULL) 10001da177e4SLinus Torvalds return -EAGAIN; 10011da177e4SLinus Torvalds 1002ca2c0966STakashi Iwai runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 10031da177e4SLinus Torvalds if (runtime == NULL) 10041da177e4SLinus Torvalds return -ENOMEM; 10051da177e4SLinus Torvalds 1006877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 10071da177e4SLinus Torvalds runtime->status = snd_malloc_pages(size, GFP_KERNEL); 10081da177e4SLinus Torvalds if (runtime->status == NULL) { 10091da177e4SLinus Torvalds kfree(runtime); 10101da177e4SLinus Torvalds return -ENOMEM; 10111da177e4SLinus Torvalds } 10121da177e4SLinus Torvalds memset((void*)runtime->status, 0, size); 10131da177e4SLinus Torvalds 1014877211f5STakashi Iwai size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 10151da177e4SLinus Torvalds runtime->control = snd_malloc_pages(size, GFP_KERNEL); 10161da177e4SLinus Torvalds if (runtime->control == NULL) { 1017877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 1018877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 10191da177e4SLinus Torvalds kfree(runtime); 10201da177e4SLinus Torvalds return -ENOMEM; 10211da177e4SLinus Torvalds } 10221da177e4SLinus Torvalds memset((void*)runtime->control, 0, size); 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds init_waitqueue_head(&runtime->sleep); 1025c91a988dSJaroslav Kysela init_waitqueue_head(&runtime->tsleep); 10261da177e4SLinus Torvalds 10271da177e4SLinus Torvalds runtime->status->state = SNDRV_PCM_STATE_OPEN; 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds substream->runtime = runtime; 10301da177e4SLinus Torvalds substream->private_data = pcm->private_data; 10310df63e44STakashi Iwai substream->ref_count = 1; 10320df63e44STakashi Iwai substream->f_flags = file->f_flags; 1033e7373b70SClemens Ladisch substream->pid = get_pid(task_pid(current)); 10341da177e4SLinus Torvalds pstr->substream_opened++; 10351da177e4SLinus Torvalds *rsubstream = substream; 10361da177e4SLinus Torvalds return 0; 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds 10393bf75f9bSTakashi Iwai void snd_pcm_detach_substream(struct snd_pcm_substream *substream) 10401da177e4SLinus Torvalds { 1041877211f5STakashi Iwai struct snd_pcm_runtime *runtime; 10420df63e44STakashi Iwai 10437eaa943cSTakashi Iwai if (PCM_RUNTIME_CHECK(substream)) 10447eaa943cSTakashi Iwai return; 10451da177e4SLinus Torvalds runtime = substream->runtime; 10461da177e4SLinus Torvalds if (runtime->private_free != NULL) 10471da177e4SLinus Torvalds runtime->private_free(runtime); 1048877211f5STakashi Iwai snd_free_pages((void*)runtime->status, 1049877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 1050877211f5STakashi Iwai snd_free_pages((void*)runtime->control, 1051877211f5STakashi Iwai PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 10521da177e4SLinus Torvalds kfree(runtime->hw_constraints.rules); 1053a820ccbeSTakashi Iwai /* Avoid concurrent access to runtime via PCM timer interface */ 1054a820ccbeSTakashi Iwai if (substream->timer) 1055a820ccbeSTakashi Iwai spin_lock_irq(&substream->timer->lock); 10561da177e4SLinus Torvalds substream->runtime = NULL; 1057a820ccbeSTakashi Iwai if (substream->timer) 1058a820ccbeSTakashi Iwai spin_unlock_irq(&substream->timer->lock); 1059a820ccbeSTakashi Iwai kfree(runtime); 1060e7373b70SClemens Ladisch put_pid(substream->pid); 1061e7373b70SClemens Ladisch substream->pid = NULL; 10621da177e4SLinus Torvalds substream->pstr->substream_opened--; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds 1065d80f19faSGreg Kroah-Hartman static ssize_t show_pcm_class(struct device *dev, 1066d80f19faSGreg Kroah-Hartman struct device_attribute *attr, char *buf) 10679d19f48cSTakashi Iwai { 106860b93030STakashi Iwai struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 106960b93030STakashi Iwai struct snd_pcm *pcm = pstr->pcm; 10709d19f48cSTakashi Iwai const char *str; 10719d19f48cSTakashi Iwai static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 10729d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_GENERIC] = "generic", 10739d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MULTI] = "multi", 10749d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_MODEM] = "modem", 10759d19f48cSTakashi Iwai [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 10769d19f48cSTakashi Iwai }; 10779d19f48cSTakashi Iwai 107860b93030STakashi Iwai if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) 10799d19f48cSTakashi Iwai str = "none"; 10809d19f48cSTakashi Iwai else 10819d19f48cSTakashi Iwai str = strs[pcm->dev_class]; 10829d19f48cSTakashi Iwai return snprintf(buf, PAGE_SIZE, "%s\n", str); 10839d19f48cSTakashi Iwai } 10849d19f48cSTakashi Iwai 10856a73cf46SJoe Perches static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL); 1086caa751baSTakashi Iwai static struct attribute *pcm_dev_attrs[] = { 1087caa751baSTakashi Iwai &dev_attr_pcm_class.attr, 1088caa751baSTakashi Iwai NULL 1089caa751baSTakashi Iwai }; 1090caa751baSTakashi Iwai 1091343fe850SArvind Yadav static const struct attribute_group pcm_dev_attr_group = { 1092caa751baSTakashi Iwai .attrs = pcm_dev_attrs, 1093caa751baSTakashi Iwai }; 1094caa751baSTakashi Iwai 1095caa751baSTakashi Iwai static const struct attribute_group *pcm_dev_attr_groups[] = { 1096caa751baSTakashi Iwai &pcm_dev_attr_group, 1097caa751baSTakashi Iwai NULL 1098caa751baSTakashi Iwai }; 10999d19f48cSTakashi Iwai 1100877211f5STakashi Iwai static int snd_pcm_dev_register(struct snd_device *device) 11011da177e4SLinus Torvalds { 1102f87135f5SClemens Ladisch int cidx, err; 1103877211f5STakashi Iwai struct snd_pcm_substream *substream; 11044b3be6afSJulia Lawall struct snd_pcm *pcm; 11051da177e4SLinus Torvalds 11064b3be6afSJulia Lawall if (snd_BUG_ON(!device || !device->device_data)) 11077eaa943cSTakashi Iwai return -ENXIO; 11084b3be6afSJulia Lawall pcm = device->device_data; 1109b95bd3a4STakashi Iwai 11101a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 1111f90c06a2SPawel MOLL err = snd_pcm_add(pcm); 1112b95bd3a4STakashi Iwai if (err) 1113b95bd3a4STakashi Iwai goto unlock; 11141da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 11151da177e4SLinus Torvalds int devtype = -1; 1116b95bd3a4STakashi Iwai if (pcm->streams[cidx].substream == NULL) 11171da177e4SLinus Torvalds continue; 11181da177e4SLinus Torvalds switch (cidx) { 11191da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 11201da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 11211da177e4SLinus Torvalds break; 11221da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 11231da177e4SLinus Torvalds devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 11241da177e4SLinus Torvalds break; 11251da177e4SLinus Torvalds } 1126c78085fcSJohannes Berg /* register pcm */ 112740a4b263STakashi Iwai err = snd_register_device(devtype, pcm->card, pcm->device, 112840a4b263STakashi Iwai &snd_pcm_f_ops[cidx], pcm, 112940a4b263STakashi Iwai &pcm->streams[cidx].dev); 1130c78085fcSJohannes Berg if (err < 0) { 1131b95bd3a4STakashi Iwai list_del_init(&pcm->list); 1132b95bd3a4STakashi Iwai goto unlock; 11331da177e4SLinus Torvalds } 1134caa751baSTakashi Iwai 11351da177e4SLinus Torvalds for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 11361da177e4SLinus Torvalds snd_pcm_timer_init(substream); 11371da177e4SLinus Torvalds } 11389244b2c3SJohannes Berg 113958f30d65STakashi Iwai pcm_call_notify(pcm, n_register); 11409244b2c3SJohannes Berg 1141b95bd3a4STakashi Iwai unlock: 11421a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1143b95bd3a4STakashi Iwai return err; 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds 1146877211f5STakashi Iwai static int snd_pcm_dev_disconnect(struct snd_device *device) 11471da177e4SLinus Torvalds { 1148877211f5STakashi Iwai struct snd_pcm *pcm = device->device_data; 1149877211f5STakashi Iwai struct snd_pcm_substream *substream; 115040a4b263STakashi Iwai int cidx; 11511da177e4SLinus Torvalds 11521a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 11539b0573c0STakashi Iwai mutex_lock(&pcm->open_mutex); 11540914f796STakashi Iwai wake_up(&pcm->open_wait); 1155f87135f5SClemens Ladisch list_del_init(&pcm->list); 1156646e1dd8STakashi Iwai for (cidx = 0; cidx < 2; cidx++) { 11579b0573c0STakashi Iwai for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { 11589b0573c0STakashi Iwai snd_pcm_stream_lock_irq(substream); 11590914f796STakashi Iwai if (substream->runtime) { 11606ca73de7STakashi Iwai if (snd_pcm_running(substream)) 11616ca73de7STakashi Iwai snd_pcm_stop(substream, 11626ca73de7STakashi Iwai SNDRV_PCM_STATE_DISCONNECTED); 11636ca73de7STakashi Iwai /* to be sure, set the state unconditionally */ 11641da177e4SLinus Torvalds substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 11650914f796STakashi Iwai wake_up(&substream->runtime->sleep); 11660914f796STakashi Iwai wake_up(&substream->runtime->tsleep); 11670914f796STakashi Iwai } 11689b0573c0STakashi Iwai snd_pcm_stream_unlock_irq(substream); 11699b0573c0STakashi Iwai } 1170646e1dd8STakashi Iwai } 11718b645e4aSTakashi Iwai 117258f30d65STakashi Iwai pcm_call_notify(pcm, n_disconnect); 11731da177e4SLinus Torvalds for (cidx = 0; cidx < 2; cidx++) { 117440a4b263STakashi Iwai snd_unregister_device(&pcm->streams[cidx].dev); 1175a8ff48cbSTakashi Iwai free_chmap(&pcm->streams[cidx]); 11761da177e4SLinus Torvalds } 11779b0573c0STakashi Iwai mutex_unlock(&pcm->open_mutex); 11781a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 1179c461482cSTakashi Iwai return 0; 11801da177e4SLinus Torvalds } 11811da177e4SLinus Torvalds 118258f30d65STakashi Iwai #if IS_ENABLED(CONFIG_SND_PCM_OSS) 118330b771cfSTakashi Iwai /** 118430b771cfSTakashi Iwai * snd_pcm_notify - Add/remove the notify list 118530b771cfSTakashi Iwai * @notify: PCM notify list 118630b771cfSTakashi Iwai * @nfree: 0 = register, 1 = unregister 118730b771cfSTakashi Iwai * 118830b771cfSTakashi Iwai * This adds the given notifier to the global list so that the callback is 118930b771cfSTakashi Iwai * called for each registered PCM devices. This exists only for PCM OSS 119030b771cfSTakashi Iwai * emulation, so far. 119130b771cfSTakashi Iwai */ 1192877211f5STakashi Iwai int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 11931da177e4SLinus Torvalds { 11949244b2c3SJohannes Berg struct snd_pcm *pcm; 11951da177e4SLinus Torvalds 11967eaa943cSTakashi Iwai if (snd_BUG_ON(!notify || 11977eaa943cSTakashi Iwai !notify->n_register || 11987eaa943cSTakashi Iwai !notify->n_unregister || 11997eaa943cSTakashi Iwai !notify->n_disconnect)) 12007eaa943cSTakashi Iwai return -EINVAL; 12011a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 12021da177e4SLinus Torvalds if (nfree) { 12031da177e4SLinus Torvalds list_del(¬ify->list); 12049244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 12059244b2c3SJohannes Berg notify->n_unregister(pcm); 12061da177e4SLinus Torvalds } else { 12071da177e4SLinus Torvalds list_add_tail(¬ify->list, &snd_pcm_notify_list); 12089244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) 12099244b2c3SJohannes Berg notify->n_register(pcm); 12101da177e4SLinus Torvalds } 12111a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 12121da177e4SLinus Torvalds return 0; 12131da177e4SLinus Torvalds } 1214e88e8ae6STakashi Iwai EXPORT_SYMBOL(snd_pcm_notify); 121558f30d65STakashi Iwai #endif /* CONFIG_SND_PCM_OSS */ 1216e88e8ae6STakashi Iwai 1217cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS 12181da177e4SLinus Torvalds /* 12191da177e4SLinus Torvalds * Info interface 12201da177e4SLinus Torvalds */ 12211da177e4SLinus Torvalds 1222877211f5STakashi Iwai static void snd_pcm_proc_read(struct snd_info_entry *entry, 1223877211f5STakashi Iwai struct snd_info_buffer *buffer) 12241da177e4SLinus Torvalds { 1225877211f5STakashi Iwai struct snd_pcm *pcm; 12261da177e4SLinus Torvalds 12271a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 12289244b2c3SJohannes Berg list_for_each_entry(pcm, &snd_pcm_devices, list) { 1229f87135f5SClemens Ladisch snd_iprintf(buffer, "%02i-%02i: %s : %s", 1230f87135f5SClemens Ladisch pcm->card->number, pcm->device, pcm->id, pcm->name); 12311da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 1232877211f5STakashi Iwai snd_iprintf(buffer, " : playback %i", 1233877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 12341da177e4SLinus Torvalds if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 1235877211f5STakashi Iwai snd_iprintf(buffer, " : capture %i", 1236877211f5STakashi Iwai pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 12371da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 12381da177e4SLinus Torvalds } 12391a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds 12426581f4e7STakashi Iwai static struct snd_info_entry *snd_pcm_proc_entry; 12431da177e4SLinus Torvalds 1244e28563ccSTakashi Iwai static void snd_pcm_proc_init(void) 12451da177e4SLinus Torvalds { 1246877211f5STakashi Iwai struct snd_info_entry *entry; 12471da177e4SLinus Torvalds 1248c8da9be4SMarkus Elfring entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL); 1249c8da9be4SMarkus Elfring if (entry) { 1250bf850204STakashi Iwai snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 12511da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 12521da177e4SLinus Torvalds snd_info_free_entry(entry); 12531da177e4SLinus Torvalds entry = NULL; 12541da177e4SLinus Torvalds } 12551da177e4SLinus Torvalds } 12561da177e4SLinus Torvalds snd_pcm_proc_entry = entry; 1257e28563ccSTakashi Iwai } 1258e28563ccSTakashi Iwai 1259e28563ccSTakashi Iwai static void snd_pcm_proc_done(void) 1260e28563ccSTakashi Iwai { 1261746d4a02STakashi Iwai snd_info_free_entry(snd_pcm_proc_entry); 1262e28563ccSTakashi Iwai } 1263e28563ccSTakashi Iwai 1264cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */ 1265e28563ccSTakashi Iwai #define snd_pcm_proc_init() 1266e28563ccSTakashi Iwai #define snd_pcm_proc_done() 1267cd6a6503SJie Yang #endif /* CONFIG_SND_PROC_FS */ 1268e28563ccSTakashi Iwai 1269e28563ccSTakashi Iwai 1270e28563ccSTakashi Iwai /* 1271e28563ccSTakashi Iwai * ENTRY functions 1272e28563ccSTakashi Iwai */ 1273e28563ccSTakashi Iwai 1274e28563ccSTakashi Iwai static int __init alsa_pcm_init(void) 1275e28563ccSTakashi Iwai { 1276e28563ccSTakashi Iwai snd_ctl_register_ioctl(snd_pcm_control_ioctl); 1277e28563ccSTakashi Iwai snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 1278e28563ccSTakashi Iwai snd_pcm_proc_init(); 12791da177e4SLinus Torvalds return 0; 12801da177e4SLinus Torvalds } 12811da177e4SLinus Torvalds 12821da177e4SLinus Torvalds static void __exit alsa_pcm_exit(void) 12831da177e4SLinus Torvalds { 12841da177e4SLinus Torvalds snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 12851da177e4SLinus Torvalds snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 1286e28563ccSTakashi Iwai snd_pcm_proc_done(); 12871da177e4SLinus Torvalds } 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds module_init(alsa_pcm_init) 12901da177e4SLinus Torvalds module_exit(alsa_pcm_exit) 1291