18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Digital Audio (PCM) abstract layer 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/init.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/time.h> 118c2ecf20Sopenharmony_ci#include <linux/mutex.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/nospec.h> 148c2ecf20Sopenharmony_ci#include <sound/core.h> 158c2ecf20Sopenharmony_ci#include <sound/minors.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm.h> 178c2ecf20Sopenharmony_ci#include <sound/timer.h> 188c2ecf20Sopenharmony_ci#include <sound/control.h> 198c2ecf20Sopenharmony_ci#include <sound/info.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "pcm_local.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); 248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Midlevel PCM code for ALSA."); 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_pcm_devices); 288c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(register_mutex); 298c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 308c2ecf20Sopenharmony_cistatic LIST_HEAD(snd_pcm_notify_list); 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int snd_pcm_free(struct snd_pcm *pcm); 348c2ecf20Sopenharmony_cistatic int snd_pcm_dev_free(struct snd_device *device); 358c2ecf20Sopenharmony_cistatic int snd_pcm_dev_register(struct snd_device *device); 368c2ecf20Sopenharmony_cistatic int snd_pcm_dev_disconnect(struct snd_device *device); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 438c2ecf20Sopenharmony_ci if (pcm->card == card && pcm->device == device) 448c2ecf20Sopenharmony_ci return pcm; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int snd_pcm_next(struct snd_card *card, int device) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 548c2ecf20Sopenharmony_ci if (pcm->card == card && pcm->device > device) 558c2ecf20Sopenharmony_ci return pcm->device; 568c2ecf20Sopenharmony_ci else if (pcm->card->number > card->number) 578c2ecf20Sopenharmony_ci return -1; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci return -1; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int snd_pcm_add(struct snd_pcm *newpcm) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (newpcm->internal) 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 708c2ecf20Sopenharmony_ci if (pcm->card == newpcm->card && pcm->device == newpcm->device) 718c2ecf20Sopenharmony_ci return -EBUSY; 728c2ecf20Sopenharmony_ci if (pcm->card->number > newpcm->card->number || 738c2ecf20Sopenharmony_ci (pcm->card == newpcm->card && 748c2ecf20Sopenharmony_ci pcm->device > newpcm->device)) { 758c2ecf20Sopenharmony_ci list_add(&newpcm->list, pcm->list.prev); 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci list_add_tail(&newpcm->list, &snd_pcm_devices); 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int snd_pcm_control_ioctl(struct snd_card *card, 848c2ecf20Sopenharmony_ci struct snd_ctl_file *control, 858c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci switch (cmd) { 888c2ecf20Sopenharmony_ci case SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE: 898c2ecf20Sopenharmony_ci { 908c2ecf20Sopenharmony_ci int device; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (get_user(device, (int __user *)arg)) 938c2ecf20Sopenharmony_ci return -EFAULT; 948c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 958c2ecf20Sopenharmony_ci device = snd_pcm_next(card, device); 968c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 978c2ecf20Sopenharmony_ci if (put_user(device, (int __user *)arg)) 988c2ecf20Sopenharmony_ci return -EFAULT; 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci case SNDRV_CTL_IOCTL_PCM_INFO: 1028c2ecf20Sopenharmony_ci { 1038c2ecf20Sopenharmony_ci struct snd_pcm_info __user *info; 1048c2ecf20Sopenharmony_ci unsigned int device, subdevice; 1058c2ecf20Sopenharmony_ci int stream; 1068c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 1078c2ecf20Sopenharmony_ci struct snd_pcm_str *pstr; 1088c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1098c2ecf20Sopenharmony_ci int err; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci info = (struct snd_pcm_info __user *)arg; 1128c2ecf20Sopenharmony_ci if (get_user(device, &info->device)) 1138c2ecf20Sopenharmony_ci return -EFAULT; 1148c2ecf20Sopenharmony_ci if (get_user(stream, &info->stream)) 1158c2ecf20Sopenharmony_ci return -EFAULT; 1168c2ecf20Sopenharmony_ci if (stream < 0 || stream > 1) 1178c2ecf20Sopenharmony_ci return -EINVAL; 1188c2ecf20Sopenharmony_ci stream = array_index_nospec(stream, 2); 1198c2ecf20Sopenharmony_ci if (get_user(subdevice, &info->subdevice)) 1208c2ecf20Sopenharmony_ci return -EFAULT; 1218c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 1228c2ecf20Sopenharmony_ci pcm = snd_pcm_get(card, device); 1238c2ecf20Sopenharmony_ci if (pcm == NULL) { 1248c2ecf20Sopenharmony_ci err = -ENXIO; 1258c2ecf20Sopenharmony_ci goto _error; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci pstr = &pcm->streams[stream]; 1288c2ecf20Sopenharmony_ci if (pstr->substream_count == 0) { 1298c2ecf20Sopenharmony_ci err = -ENOENT; 1308c2ecf20Sopenharmony_ci goto _error; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci if (subdevice >= pstr->substream_count) { 1338c2ecf20Sopenharmony_ci err = -ENXIO; 1348c2ecf20Sopenharmony_ci goto _error; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci for (substream = pstr->substream; substream; 1378c2ecf20Sopenharmony_ci substream = substream->next) 1388c2ecf20Sopenharmony_ci if (substream->number == (int)subdevice) 1398c2ecf20Sopenharmony_ci break; 1408c2ecf20Sopenharmony_ci if (substream == NULL) { 1418c2ecf20Sopenharmony_ci err = -ENXIO; 1428c2ecf20Sopenharmony_ci goto _error; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci mutex_lock(&pcm->open_mutex); 1458c2ecf20Sopenharmony_ci err = snd_pcm_info_user(substream, info); 1468c2ecf20Sopenharmony_ci mutex_unlock(&pcm->open_mutex); 1478c2ecf20Sopenharmony_ci _error: 1488c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 1498c2ecf20Sopenharmony_ci return err; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci case SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE: 1528c2ecf20Sopenharmony_ci { 1538c2ecf20Sopenharmony_ci int val; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (get_user(val, (int __user *)arg)) 1568c2ecf20Sopenharmony_ci return -EFAULT; 1578c2ecf20Sopenharmony_ci control->preferred_subdevice[SND_CTL_SUBDEV_PCM] = val; 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const char * const snd_pcm_format_names[] = { 1678c2ecf20Sopenharmony_ci FORMAT(S8), 1688c2ecf20Sopenharmony_ci FORMAT(U8), 1698c2ecf20Sopenharmony_ci FORMAT(S16_LE), 1708c2ecf20Sopenharmony_ci FORMAT(S16_BE), 1718c2ecf20Sopenharmony_ci FORMAT(U16_LE), 1728c2ecf20Sopenharmony_ci FORMAT(U16_BE), 1738c2ecf20Sopenharmony_ci FORMAT(S24_LE), 1748c2ecf20Sopenharmony_ci FORMAT(S24_BE), 1758c2ecf20Sopenharmony_ci FORMAT(U24_LE), 1768c2ecf20Sopenharmony_ci FORMAT(U24_BE), 1778c2ecf20Sopenharmony_ci FORMAT(S32_LE), 1788c2ecf20Sopenharmony_ci FORMAT(S32_BE), 1798c2ecf20Sopenharmony_ci FORMAT(U32_LE), 1808c2ecf20Sopenharmony_ci FORMAT(U32_BE), 1818c2ecf20Sopenharmony_ci FORMAT(FLOAT_LE), 1828c2ecf20Sopenharmony_ci FORMAT(FLOAT_BE), 1838c2ecf20Sopenharmony_ci FORMAT(FLOAT64_LE), 1848c2ecf20Sopenharmony_ci FORMAT(FLOAT64_BE), 1858c2ecf20Sopenharmony_ci FORMAT(IEC958_SUBFRAME_LE), 1868c2ecf20Sopenharmony_ci FORMAT(IEC958_SUBFRAME_BE), 1878c2ecf20Sopenharmony_ci FORMAT(MU_LAW), 1888c2ecf20Sopenharmony_ci FORMAT(A_LAW), 1898c2ecf20Sopenharmony_ci FORMAT(IMA_ADPCM), 1908c2ecf20Sopenharmony_ci FORMAT(MPEG), 1918c2ecf20Sopenharmony_ci FORMAT(GSM), 1928c2ecf20Sopenharmony_ci FORMAT(SPECIAL), 1938c2ecf20Sopenharmony_ci FORMAT(S24_3LE), 1948c2ecf20Sopenharmony_ci FORMAT(S24_3BE), 1958c2ecf20Sopenharmony_ci FORMAT(U24_3LE), 1968c2ecf20Sopenharmony_ci FORMAT(U24_3BE), 1978c2ecf20Sopenharmony_ci FORMAT(S20_3LE), 1988c2ecf20Sopenharmony_ci FORMAT(S20_3BE), 1998c2ecf20Sopenharmony_ci FORMAT(U20_3LE), 2008c2ecf20Sopenharmony_ci FORMAT(U20_3BE), 2018c2ecf20Sopenharmony_ci FORMAT(S18_3LE), 2028c2ecf20Sopenharmony_ci FORMAT(S18_3BE), 2038c2ecf20Sopenharmony_ci FORMAT(U18_3LE), 2048c2ecf20Sopenharmony_ci FORMAT(U18_3BE), 2058c2ecf20Sopenharmony_ci FORMAT(G723_24), 2068c2ecf20Sopenharmony_ci FORMAT(G723_24_1B), 2078c2ecf20Sopenharmony_ci FORMAT(G723_40), 2088c2ecf20Sopenharmony_ci FORMAT(G723_40_1B), 2098c2ecf20Sopenharmony_ci FORMAT(DSD_U8), 2108c2ecf20Sopenharmony_ci FORMAT(DSD_U16_LE), 2118c2ecf20Sopenharmony_ci FORMAT(DSD_U32_LE), 2128c2ecf20Sopenharmony_ci FORMAT(DSD_U16_BE), 2138c2ecf20Sopenharmony_ci FORMAT(DSD_U32_BE), 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/** 2178c2ecf20Sopenharmony_ci * snd_pcm_format_name - Return a name string for the given PCM format 2188c2ecf20Sopenharmony_ci * @format: PCM format 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ciconst char *snd_pcm_format_name(snd_pcm_format_t format) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) 2238c2ecf20Sopenharmony_ci return "Unknown"; 2248c2ecf20Sopenharmony_ci return snd_pcm_format_names[(__force unsigned int)format]; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_pcm_format_name); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_VERBOSE_PROCFS 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci#define STATE(v) [SNDRV_PCM_STATE_##v] = #v 2318c2ecf20Sopenharmony_ci#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v 2328c2ecf20Sopenharmony_ci#define READY(v) [SNDRV_PCM_READY_##v] = #v 2338c2ecf20Sopenharmony_ci#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v 2348c2ecf20Sopenharmony_ci#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v 2358c2ecf20Sopenharmony_ci#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v 2368c2ecf20Sopenharmony_ci#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v 2378c2ecf20Sopenharmony_ci#define START(v) [SNDRV_PCM_START_##v] = #v 2388c2ecf20Sopenharmony_ci#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const char * const snd_pcm_stream_names[] = { 2418c2ecf20Sopenharmony_ci STREAM(PLAYBACK), 2428c2ecf20Sopenharmony_ci STREAM(CAPTURE), 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const char * const snd_pcm_state_names[] = { 2468c2ecf20Sopenharmony_ci STATE(OPEN), 2478c2ecf20Sopenharmony_ci STATE(SETUP), 2488c2ecf20Sopenharmony_ci STATE(PREPARED), 2498c2ecf20Sopenharmony_ci STATE(RUNNING), 2508c2ecf20Sopenharmony_ci STATE(XRUN), 2518c2ecf20Sopenharmony_ci STATE(DRAINING), 2528c2ecf20Sopenharmony_ci STATE(PAUSED), 2538c2ecf20Sopenharmony_ci STATE(SUSPENDED), 2548c2ecf20Sopenharmony_ci STATE(DISCONNECTED), 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic const char * const snd_pcm_access_names[] = { 2588c2ecf20Sopenharmony_ci ACCESS(MMAP_INTERLEAVED), 2598c2ecf20Sopenharmony_ci ACCESS(MMAP_NONINTERLEAVED), 2608c2ecf20Sopenharmony_ci ACCESS(MMAP_COMPLEX), 2618c2ecf20Sopenharmony_ci ACCESS(RW_INTERLEAVED), 2628c2ecf20Sopenharmony_ci ACCESS(RW_NONINTERLEAVED), 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const char * const snd_pcm_subformat_names[] = { 2668c2ecf20Sopenharmony_ci SUBFORMAT(STD), 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const char * const snd_pcm_tstamp_mode_names[] = { 2708c2ecf20Sopenharmony_ci TSTAMP(NONE), 2718c2ecf20Sopenharmony_ci TSTAMP(ENABLE), 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic const char *snd_pcm_stream_name(int stream) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return snd_pcm_stream_names[stream]; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const char *snd_pcm_access_name(snd_pcm_access_t access) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci return snd_pcm_access_names[(__force int)access]; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci return snd_pcm_subformat_names[(__force int)subformat]; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const char *snd_pcm_tstamp_mode_name(int mode) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci return snd_pcm_tstamp_mode_names[mode]; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic const char *snd_pcm_state_name(snd_pcm_state_t state) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci return snd_pcm_state_names[(__force int)state]; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 3008c2ecf20Sopenharmony_ci#include <linux/soundcard.h> 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic const char *snd_pcm_oss_format_name(int format) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci switch (format) { 3058c2ecf20Sopenharmony_ci case AFMT_MU_LAW: 3068c2ecf20Sopenharmony_ci return "MU_LAW"; 3078c2ecf20Sopenharmony_ci case AFMT_A_LAW: 3088c2ecf20Sopenharmony_ci return "A_LAW"; 3098c2ecf20Sopenharmony_ci case AFMT_IMA_ADPCM: 3108c2ecf20Sopenharmony_ci return "IMA_ADPCM"; 3118c2ecf20Sopenharmony_ci case AFMT_U8: 3128c2ecf20Sopenharmony_ci return "U8"; 3138c2ecf20Sopenharmony_ci case AFMT_S16_LE: 3148c2ecf20Sopenharmony_ci return "S16_LE"; 3158c2ecf20Sopenharmony_ci case AFMT_S16_BE: 3168c2ecf20Sopenharmony_ci return "S16_BE"; 3178c2ecf20Sopenharmony_ci case AFMT_S8: 3188c2ecf20Sopenharmony_ci return "S8"; 3198c2ecf20Sopenharmony_ci case AFMT_U16_LE: 3208c2ecf20Sopenharmony_ci return "U16_LE"; 3218c2ecf20Sopenharmony_ci case AFMT_U16_BE: 3228c2ecf20Sopenharmony_ci return "U16_BE"; 3238c2ecf20Sopenharmony_ci case AFMT_MPEG: 3248c2ecf20Sopenharmony_ci return "MPEG"; 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci return "unknown"; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci#endif 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, 3328c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct snd_pcm_info *info; 3358c2ecf20Sopenharmony_ci int err; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (! substream) 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci info = kmalloc(sizeof(*info), GFP_KERNEL); 3418c2ecf20Sopenharmony_ci if (!info) 3428c2ecf20Sopenharmony_ci return; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci err = snd_pcm_info(substream, info); 3458c2ecf20Sopenharmony_ci if (err < 0) { 3468c2ecf20Sopenharmony_ci snd_iprintf(buffer, "error %d\n", err); 3478c2ecf20Sopenharmony_ci kfree(info); 3488c2ecf20Sopenharmony_ci return; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci snd_iprintf(buffer, "card: %d\n", info->card); 3518c2ecf20Sopenharmony_ci snd_iprintf(buffer, "device: %d\n", info->device); 3528c2ecf20Sopenharmony_ci snd_iprintf(buffer, "subdevice: %d\n", info->subdevice); 3538c2ecf20Sopenharmony_ci snd_iprintf(buffer, "stream: %s\n", snd_pcm_stream_name(info->stream)); 3548c2ecf20Sopenharmony_ci snd_iprintf(buffer, "id: %s\n", info->id); 3558c2ecf20Sopenharmony_ci snd_iprintf(buffer, "name: %s\n", info->name); 3568c2ecf20Sopenharmony_ci snd_iprintf(buffer, "subname: %s\n", info->subname); 3578c2ecf20Sopenharmony_ci snd_iprintf(buffer, "class: %d\n", info->dev_class); 3588c2ecf20Sopenharmony_ci snd_iprintf(buffer, "subclass: %d\n", info->dev_subclass); 3598c2ecf20Sopenharmony_ci snd_iprintf(buffer, "subdevices_count: %d\n", info->subdevices_count); 3608c2ecf20Sopenharmony_ci snd_iprintf(buffer, "subdevices_avail: %d\n", info->subdevices_avail); 3618c2ecf20Sopenharmony_ci kfree(info); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void snd_pcm_stream_proc_info_read(struct snd_info_entry *entry, 3658c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci snd_pcm_proc_info_read(((struct snd_pcm_str *)entry->private_data)->substream, 3688c2ecf20Sopenharmony_ci buffer); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void snd_pcm_substream_proc_info_read(struct snd_info_entry *entry, 3728c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci snd_pcm_proc_info_read(entry->private_data, buffer); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic void snd_pcm_substream_proc_hw_params_read(struct snd_info_entry *entry, 3788c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 3818c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci mutex_lock(&substream->pcm->open_mutex); 3848c2ecf20Sopenharmony_ci runtime = substream->runtime; 3858c2ecf20Sopenharmony_ci if (!runtime) { 3868c2ecf20Sopenharmony_ci snd_iprintf(buffer, "closed\n"); 3878c2ecf20Sopenharmony_ci goto unlock; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 3908c2ecf20Sopenharmony_ci snd_iprintf(buffer, "no setup\n"); 3918c2ecf20Sopenharmony_ci goto unlock; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); 3948c2ecf20Sopenharmony_ci snd_iprintf(buffer, "format: %s\n", snd_pcm_format_name(runtime->format)); 3958c2ecf20Sopenharmony_ci snd_iprintf(buffer, "subformat: %s\n", snd_pcm_subformat_name(runtime->subformat)); 3968c2ecf20Sopenharmony_ci snd_iprintf(buffer, "channels: %u\n", runtime->channels); 3978c2ecf20Sopenharmony_ci snd_iprintf(buffer, "rate: %u (%u/%u)\n", runtime->rate, runtime->rate_num, runtime->rate_den); 3988c2ecf20Sopenharmony_ci snd_iprintf(buffer, "period_size: %lu\n", runtime->period_size); 3998c2ecf20Sopenharmony_ci snd_iprintf(buffer, "buffer_size: %lu\n", runtime->buffer_size); 4008c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 4018c2ecf20Sopenharmony_ci if (substream->oss.oss) { 4028c2ecf20Sopenharmony_ci snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format)); 4038c2ecf20Sopenharmony_ci snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels); 4048c2ecf20Sopenharmony_ci snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate); 4058c2ecf20Sopenharmony_ci snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes); 4068c2ecf20Sopenharmony_ci snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods); 4078c2ecf20Sopenharmony_ci snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci#endif 4108c2ecf20Sopenharmony_ci unlock: 4118c2ecf20Sopenharmony_ci mutex_unlock(&substream->pcm->open_mutex); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, 4158c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 4188c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci mutex_lock(&substream->pcm->open_mutex); 4218c2ecf20Sopenharmony_ci runtime = substream->runtime; 4228c2ecf20Sopenharmony_ci if (!runtime) { 4238c2ecf20Sopenharmony_ci snd_iprintf(buffer, "closed\n"); 4248c2ecf20Sopenharmony_ci goto unlock; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { 4278c2ecf20Sopenharmony_ci snd_iprintf(buffer, "no setup\n"); 4288c2ecf20Sopenharmony_ci goto unlock; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); 4318c2ecf20Sopenharmony_ci snd_iprintf(buffer, "period_step: %u\n", runtime->period_step); 4328c2ecf20Sopenharmony_ci snd_iprintf(buffer, "avail_min: %lu\n", runtime->control->avail_min); 4338c2ecf20Sopenharmony_ci snd_iprintf(buffer, "start_threshold: %lu\n", runtime->start_threshold); 4348c2ecf20Sopenharmony_ci snd_iprintf(buffer, "stop_threshold: %lu\n", runtime->stop_threshold); 4358c2ecf20Sopenharmony_ci snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); 4368c2ecf20Sopenharmony_ci snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); 4378c2ecf20Sopenharmony_ci snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); 4388c2ecf20Sopenharmony_ci unlock: 4398c2ecf20Sopenharmony_ci mutex_unlock(&substream->pcm->open_mutex); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, 4438c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 4468c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 4478c2ecf20Sopenharmony_ci struct snd_pcm_status64 status; 4488c2ecf20Sopenharmony_ci int err; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci mutex_lock(&substream->pcm->open_mutex); 4518c2ecf20Sopenharmony_ci runtime = substream->runtime; 4528c2ecf20Sopenharmony_ci if (!runtime) { 4538c2ecf20Sopenharmony_ci snd_iprintf(buffer, "closed\n"); 4548c2ecf20Sopenharmony_ci goto unlock; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci memset(&status, 0, sizeof(status)); 4578c2ecf20Sopenharmony_ci err = snd_pcm_status64(substream, &status); 4588c2ecf20Sopenharmony_ci if (err < 0) { 4598c2ecf20Sopenharmony_ci snd_iprintf(buffer, "error %d\n", err); 4608c2ecf20Sopenharmony_ci goto unlock; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); 4638c2ecf20Sopenharmony_ci snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); 4648c2ecf20Sopenharmony_ci snd_iprintf(buffer, "trigger_time: %lld.%09lld\n", 4658c2ecf20Sopenharmony_ci status.trigger_tstamp_sec, status.trigger_tstamp_nsec); 4668c2ecf20Sopenharmony_ci snd_iprintf(buffer, "tstamp : %lld.%09lld\n", 4678c2ecf20Sopenharmony_ci status.tstamp_sec, status.tstamp_nsec); 4688c2ecf20Sopenharmony_ci snd_iprintf(buffer, "delay : %ld\n", status.delay); 4698c2ecf20Sopenharmony_ci snd_iprintf(buffer, "avail : %ld\n", status.avail); 4708c2ecf20Sopenharmony_ci snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); 4718c2ecf20Sopenharmony_ci snd_iprintf(buffer, "-----\n"); 4728c2ecf20Sopenharmony_ci snd_iprintf(buffer, "hw_ptr : %ld\n", runtime->status->hw_ptr); 4738c2ecf20Sopenharmony_ci snd_iprintf(buffer, "appl_ptr : %ld\n", runtime->control->appl_ptr); 4748c2ecf20Sopenharmony_ci unlock: 4758c2ecf20Sopenharmony_ci mutex_unlock(&substream->pcm->open_mutex); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PCM_XRUN_DEBUG 4798c2ecf20Sopenharmony_cistatic void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, 4808c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = entry->private_data; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci snd_pcm_stop_xrun(substream); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, 4888c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct snd_pcm_str *pstr = entry->private_data; 4918c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%d\n", pstr->xrun_debug); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void snd_pcm_xrun_debug_write(struct snd_info_entry *entry, 4958c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct snd_pcm_str *pstr = entry->private_data; 4988c2ecf20Sopenharmony_ci char line[64]; 4998c2ecf20Sopenharmony_ci if (!snd_info_get_line(buffer, line, sizeof(line))) 5008c2ecf20Sopenharmony_ci pstr->xrun_debug = simple_strtoul(line, NULL, 10); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci#endif 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct snd_pcm *pcm = pstr->pcm; 5078c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 5088c2ecf20Sopenharmony_ci char name[16]; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci sprintf(name, "pcm%i%c", pcm->device, 5118c2ecf20Sopenharmony_ci pstr->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 5128c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(pcm->card, name, 5138c2ecf20Sopenharmony_ci pcm->card->proc_root); 5148c2ecf20Sopenharmony_ci if (!entry) 5158c2ecf20Sopenharmony_ci return -ENOMEM; 5168c2ecf20Sopenharmony_ci entry->mode = S_IFDIR | 0555; 5178c2ecf20Sopenharmony_ci pstr->proc_root = entry; 5188c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root); 5198c2ecf20Sopenharmony_ci if (entry) 5208c2ecf20Sopenharmony_ci snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); 5218c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PCM_XRUN_DEBUG 5228c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(pcm->card, "xrun_debug", 5238c2ecf20Sopenharmony_ci pstr->proc_root); 5248c2ecf20Sopenharmony_ci if (entry) { 5258c2ecf20Sopenharmony_ci snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read); 5268c2ecf20Sopenharmony_ci entry->c.text.write = snd_pcm_xrun_debug_write; 5278c2ecf20Sopenharmony_ci entry->mode |= 0200; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci#endif 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci snd_info_free_entry(pstr->proc_root); 5368c2ecf20Sopenharmony_ci pstr->proc_root = NULL; 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic struct snd_info_entry * 5418c2ecf20Sopenharmony_cicreate_substream_info_entry(struct snd_pcm_substream *substream, 5428c2ecf20Sopenharmony_ci const char *name, 5438c2ecf20Sopenharmony_ci void (*read)(struct snd_info_entry *, 5448c2ecf20Sopenharmony_ci struct snd_info_buffer *)) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(substream->pcm->card, name, 5498c2ecf20Sopenharmony_ci substream->proc_root); 5508c2ecf20Sopenharmony_ci if (entry) 5518c2ecf20Sopenharmony_ci snd_info_set_text_ops(entry, substream, read); 5528c2ecf20Sopenharmony_ci return entry; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 5588c2ecf20Sopenharmony_ci struct snd_card *card; 5598c2ecf20Sopenharmony_ci char name[16]; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci card = substream->pcm->card; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci sprintf(name, "sub%i", substream->number); 5648c2ecf20Sopenharmony_ci entry = snd_info_create_card_entry(card, name, 5658c2ecf20Sopenharmony_ci substream->pstr->proc_root); 5668c2ecf20Sopenharmony_ci if (!entry) 5678c2ecf20Sopenharmony_ci return -ENOMEM; 5688c2ecf20Sopenharmony_ci entry->mode = S_IFDIR | 0555; 5698c2ecf20Sopenharmony_ci substream->proc_root = entry; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci create_substream_info_entry(substream, "info", 5728c2ecf20Sopenharmony_ci snd_pcm_substream_proc_info_read); 5738c2ecf20Sopenharmony_ci create_substream_info_entry(substream, "hw_params", 5748c2ecf20Sopenharmony_ci snd_pcm_substream_proc_hw_params_read); 5758c2ecf20Sopenharmony_ci create_substream_info_entry(substream, "sw_params", 5768c2ecf20Sopenharmony_ci snd_pcm_substream_proc_sw_params_read); 5778c2ecf20Sopenharmony_ci create_substream_info_entry(substream, "status", 5788c2ecf20Sopenharmony_ci snd_pcm_substream_proc_status_read); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PCM_XRUN_DEBUG 5818c2ecf20Sopenharmony_ci entry = create_substream_info_entry(substream, "xrun_injection", NULL); 5828c2ecf20Sopenharmony_ci if (entry) { 5838c2ecf20Sopenharmony_ci entry->c.text.write = snd_pcm_xrun_injection_write; 5848c2ecf20Sopenharmony_ci entry->mode = S_IFREG | 0200; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_PCM_XRUN_DEBUG */ 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci#else /* !CONFIG_SND_VERBOSE_PROCFS */ 5928c2ecf20Sopenharmony_cistatic inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; } 5938c2ecf20Sopenharmony_cistatic inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; } 5948c2ecf20Sopenharmony_cistatic inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; } 5958c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_VERBOSE_PROCFS */ 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic const struct attribute_group *pcm_dev_attr_groups[]; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci/* 6008c2ecf20Sopenharmony_ci * PM callbacks: we need to deal only with suspend here, as the resume is 6018c2ecf20Sopenharmony_ci * triggered either from user-space or the driver's resume callback 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 6048c2ecf20Sopenharmony_cistatic int do_pcm_suspend(struct device *dev) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (!pstr->pcm->no_device_suspend) 6098c2ecf20Sopenharmony_ci snd_pcm_suspend_all(pstr->pcm); 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci#endif 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic const struct dev_pm_ops pcm_dev_pm_ops = { 6158c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL) 6168c2ecf20Sopenharmony_ci}; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* device type for PCM -- basically only for passing PM callbacks */ 6198c2ecf20Sopenharmony_cistatic const struct device_type pcm_dev_type = { 6208c2ecf20Sopenharmony_ci .name = "pcm", 6218c2ecf20Sopenharmony_ci .pm = &pcm_dev_pm_ops, 6228c2ecf20Sopenharmony_ci}; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci/** 6258c2ecf20Sopenharmony_ci * snd_pcm_new_stream - create a new PCM stream 6268c2ecf20Sopenharmony_ci * @pcm: the pcm instance 6278c2ecf20Sopenharmony_ci * @stream: the stream direction, SNDRV_PCM_STREAM_XXX 6288c2ecf20Sopenharmony_ci * @substream_count: the number of substreams 6298c2ecf20Sopenharmony_ci * 6308c2ecf20Sopenharmony_ci * Creates a new stream for the pcm. 6318c2ecf20Sopenharmony_ci * The corresponding stream on the pcm must have been empty before 6328c2ecf20Sopenharmony_ci * calling this, i.e. zero must be given to the argument of 6338c2ecf20Sopenharmony_ci * snd_pcm_new(). 6348c2ecf20Sopenharmony_ci * 6358c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ciint snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci int idx, err; 6408c2ecf20Sopenharmony_ci struct snd_pcm_str *pstr = &pcm->streams[stream]; 6418c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, *prev; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 6448c2ecf20Sopenharmony_ci mutex_init(&pstr->oss.setup_mutex); 6458c2ecf20Sopenharmony_ci#endif 6468c2ecf20Sopenharmony_ci pstr->stream = stream; 6478c2ecf20Sopenharmony_ci pstr->pcm = pcm; 6488c2ecf20Sopenharmony_ci pstr->substream_count = substream_count; 6498c2ecf20Sopenharmony_ci if (!substream_count) 6508c2ecf20Sopenharmony_ci return 0; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci snd_device_initialize(&pstr->dev, pcm->card); 6538c2ecf20Sopenharmony_ci pstr->dev.groups = pcm_dev_attr_groups; 6548c2ecf20Sopenharmony_ci pstr->dev.type = &pcm_dev_type; 6558c2ecf20Sopenharmony_ci dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, 6568c2ecf20Sopenharmony_ci stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c'); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!pcm->internal) { 6598c2ecf20Sopenharmony_ci err = snd_pcm_stream_proc_init(pstr); 6608c2ecf20Sopenharmony_ci if (err < 0) { 6618c2ecf20Sopenharmony_ci pcm_err(pcm, "Error in snd_pcm_stream_proc_init\n"); 6628c2ecf20Sopenharmony_ci return err; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci prev = NULL; 6668c2ecf20Sopenharmony_ci for (idx = 0, prev = NULL; idx < substream_count; idx++) { 6678c2ecf20Sopenharmony_ci substream = kzalloc(sizeof(*substream), GFP_KERNEL); 6688c2ecf20Sopenharmony_ci if (!substream) 6698c2ecf20Sopenharmony_ci return -ENOMEM; 6708c2ecf20Sopenharmony_ci substream->pcm = pcm; 6718c2ecf20Sopenharmony_ci substream->pstr = pstr; 6728c2ecf20Sopenharmony_ci substream->number = idx; 6738c2ecf20Sopenharmony_ci substream->stream = stream; 6748c2ecf20Sopenharmony_ci sprintf(substream->name, "subdevice #%i", idx); 6758c2ecf20Sopenharmony_ci substream->buffer_bytes_max = UINT_MAX; 6768c2ecf20Sopenharmony_ci if (prev == NULL) 6778c2ecf20Sopenharmony_ci pstr->substream = substream; 6788c2ecf20Sopenharmony_ci else 6798c2ecf20Sopenharmony_ci prev->next = substream; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (!pcm->internal) { 6828c2ecf20Sopenharmony_ci err = snd_pcm_substream_proc_init(substream); 6838c2ecf20Sopenharmony_ci if (err < 0) { 6848c2ecf20Sopenharmony_ci pcm_err(pcm, 6858c2ecf20Sopenharmony_ci "Error in snd_pcm_stream_proc_init\n"); 6868c2ecf20Sopenharmony_ci if (prev == NULL) 6878c2ecf20Sopenharmony_ci pstr->substream = NULL; 6888c2ecf20Sopenharmony_ci else 6898c2ecf20Sopenharmony_ci prev->next = NULL; 6908c2ecf20Sopenharmony_ci kfree(substream); 6918c2ecf20Sopenharmony_ci return err; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci substream->group = &substream->self_group; 6958c2ecf20Sopenharmony_ci snd_pcm_group_init(&substream->self_group); 6968c2ecf20Sopenharmony_ci list_add_tail(&substream->link_list, &substream->self_group.substreams); 6978c2ecf20Sopenharmony_ci atomic_set(&substream->mmap_count, 0); 6988c2ecf20Sopenharmony_ci prev = substream; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_new_stream); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic int _snd_pcm_new(struct snd_card *card, const char *id, int device, 7058c2ecf20Sopenharmony_ci int playback_count, int capture_count, bool internal, 7068c2ecf20Sopenharmony_ci struct snd_pcm **rpcm) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 7098c2ecf20Sopenharmony_ci int err; 7108c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 7118c2ecf20Sopenharmony_ci .dev_free = snd_pcm_dev_free, 7128c2ecf20Sopenharmony_ci .dev_register = snd_pcm_dev_register, 7138c2ecf20Sopenharmony_ci .dev_disconnect = snd_pcm_dev_disconnect, 7148c2ecf20Sopenharmony_ci }; 7158c2ecf20Sopenharmony_ci static const struct snd_device_ops internal_ops = { 7168c2ecf20Sopenharmony_ci .dev_free = snd_pcm_dev_free, 7178c2ecf20Sopenharmony_ci }; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (snd_BUG_ON(!card)) 7208c2ecf20Sopenharmony_ci return -ENXIO; 7218c2ecf20Sopenharmony_ci if (rpcm) 7228c2ecf20Sopenharmony_ci *rpcm = NULL; 7238c2ecf20Sopenharmony_ci pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); 7248c2ecf20Sopenharmony_ci if (!pcm) 7258c2ecf20Sopenharmony_ci return -ENOMEM; 7268c2ecf20Sopenharmony_ci pcm->card = card; 7278c2ecf20Sopenharmony_ci pcm->device = device; 7288c2ecf20Sopenharmony_ci pcm->internal = internal; 7298c2ecf20Sopenharmony_ci mutex_init(&pcm->open_mutex); 7308c2ecf20Sopenharmony_ci init_waitqueue_head(&pcm->open_wait); 7318c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pcm->list); 7328c2ecf20Sopenharmony_ci if (id) 7338c2ecf20Sopenharmony_ci strlcpy(pcm->id, id, sizeof(pcm->id)); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, 7368c2ecf20Sopenharmony_ci playback_count); 7378c2ecf20Sopenharmony_ci if (err < 0) 7388c2ecf20Sopenharmony_ci goto free_pcm; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count); 7418c2ecf20Sopenharmony_ci if (err < 0) 7428c2ecf20Sopenharmony_ci goto free_pcm; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci err = snd_device_new(card, SNDRV_DEV_PCM, pcm, 7458c2ecf20Sopenharmony_ci internal ? &internal_ops : &ops); 7468c2ecf20Sopenharmony_ci if (err < 0) 7478c2ecf20Sopenharmony_ci goto free_pcm; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (rpcm) 7508c2ecf20Sopenharmony_ci *rpcm = pcm; 7518c2ecf20Sopenharmony_ci return 0; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cifree_pcm: 7548c2ecf20Sopenharmony_ci snd_pcm_free(pcm); 7558c2ecf20Sopenharmony_ci return err; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/** 7598c2ecf20Sopenharmony_ci * snd_pcm_new - create a new PCM instance 7608c2ecf20Sopenharmony_ci * @card: the card instance 7618c2ecf20Sopenharmony_ci * @id: the id string 7628c2ecf20Sopenharmony_ci * @device: the device index (zero based) 7638c2ecf20Sopenharmony_ci * @playback_count: the number of substreams for playback 7648c2ecf20Sopenharmony_ci * @capture_count: the number of substreams for capture 7658c2ecf20Sopenharmony_ci * @rpcm: the pointer to store the new pcm instance 7668c2ecf20Sopenharmony_ci * 7678c2ecf20Sopenharmony_ci * Creates a new PCM instance. 7688c2ecf20Sopenharmony_ci * 7698c2ecf20Sopenharmony_ci * The pcm operators have to be set afterwards to the new instance 7708c2ecf20Sopenharmony_ci * via snd_pcm_set_ops(). 7718c2ecf20Sopenharmony_ci * 7728c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_ciint snd_pcm_new(struct snd_card *card, const char *id, int device, 7758c2ecf20Sopenharmony_ci int playback_count, int capture_count, struct snd_pcm **rpcm) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci return _snd_pcm_new(card, id, device, playback_count, capture_count, 7788c2ecf20Sopenharmony_ci false, rpcm); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_new); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/** 7838c2ecf20Sopenharmony_ci * snd_pcm_new_internal - create a new internal PCM instance 7848c2ecf20Sopenharmony_ci * @card: the card instance 7858c2ecf20Sopenharmony_ci * @id: the id string 7868c2ecf20Sopenharmony_ci * @device: the device index (zero based - shared with normal PCMs) 7878c2ecf20Sopenharmony_ci * @playback_count: the number of substreams for playback 7888c2ecf20Sopenharmony_ci * @capture_count: the number of substreams for capture 7898c2ecf20Sopenharmony_ci * @rpcm: the pointer to store the new pcm instance 7908c2ecf20Sopenharmony_ci * 7918c2ecf20Sopenharmony_ci * Creates a new internal PCM instance with no userspace device or procfs 7928c2ecf20Sopenharmony_ci * entries. This is used by ASoC Back End PCMs in order to create a PCM that 7938c2ecf20Sopenharmony_ci * will only be used internally by kernel drivers. i.e. it cannot be opened 7948c2ecf20Sopenharmony_ci * by userspace. It provides existing ASoC components drivers with a substream 7958c2ecf20Sopenharmony_ci * and access to any private data. 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * The pcm operators have to be set afterwards to the new instance 7988c2ecf20Sopenharmony_ci * via snd_pcm_set_ops(). 7998c2ecf20Sopenharmony_ci * 8008c2ecf20Sopenharmony_ci * Return: Zero if successful, or a negative error code on failure. 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ciint snd_pcm_new_internal(struct snd_card *card, const char *id, int device, 8038c2ecf20Sopenharmony_ci int playback_count, int capture_count, 8048c2ecf20Sopenharmony_ci struct snd_pcm **rpcm) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci return _snd_pcm_new(card, id, device, playback_count, capture_count, 8078c2ecf20Sopenharmony_ci true, rpcm); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_new_internal); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic void free_chmap(struct snd_pcm_str *pstr) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci if (pstr->chmap_kctl) { 8148c2ecf20Sopenharmony_ci struct snd_card *card = pstr->pcm->card; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci down_write(&card->controls_rwsem); 8178c2ecf20Sopenharmony_ci snd_ctl_remove(card, pstr->chmap_kctl); 8188c2ecf20Sopenharmony_ci up_write(&card->controls_rwsem); 8198c2ecf20Sopenharmony_ci pstr->chmap_kctl = NULL; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic void snd_pcm_free_stream(struct snd_pcm_str * pstr) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, *substream_next; 8268c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 8278c2ecf20Sopenharmony_ci struct snd_pcm_oss_setup *setup, *setupn; 8288c2ecf20Sopenharmony_ci#endif 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* free all proc files under the stream */ 8318c2ecf20Sopenharmony_ci snd_pcm_stream_proc_done(pstr); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci substream = pstr->substream; 8348c2ecf20Sopenharmony_ci while (substream) { 8358c2ecf20Sopenharmony_ci substream_next = substream->next; 8368c2ecf20Sopenharmony_ci snd_pcm_timer_done(substream); 8378c2ecf20Sopenharmony_ci kfree(substream); 8388c2ecf20Sopenharmony_ci substream = substream_next; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 8418c2ecf20Sopenharmony_ci for (setup = pstr->oss.setup_list; setup; setup = setupn) { 8428c2ecf20Sopenharmony_ci setupn = setup->next; 8438c2ecf20Sopenharmony_ci kfree(setup->task_name); 8448c2ecf20Sopenharmony_ci kfree(setup); 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci#endif 8478c2ecf20Sopenharmony_ci free_chmap(pstr); 8488c2ecf20Sopenharmony_ci if (pstr->substream_count) 8498c2ecf20Sopenharmony_ci put_device(&pstr->dev); 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 8538c2ecf20Sopenharmony_ci#define pcm_call_notify(pcm, call) \ 8548c2ecf20Sopenharmony_ci do { \ 8558c2ecf20Sopenharmony_ci struct snd_pcm_notify *_notify; \ 8568c2ecf20Sopenharmony_ci list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ 8578c2ecf20Sopenharmony_ci _notify->call(pcm); \ 8588c2ecf20Sopenharmony_ci } while (0) 8598c2ecf20Sopenharmony_ci#else 8608c2ecf20Sopenharmony_ci#define pcm_call_notify(pcm, call) do {} while (0) 8618c2ecf20Sopenharmony_ci#endif 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic int snd_pcm_free(struct snd_pcm *pcm) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci if (!pcm) 8668c2ecf20Sopenharmony_ci return 0; 8678c2ecf20Sopenharmony_ci if (!pcm->internal) 8688c2ecf20Sopenharmony_ci pcm_call_notify(pcm, n_unregister); 8698c2ecf20Sopenharmony_ci if (pcm->private_free) 8708c2ecf20Sopenharmony_ci pcm->private_free(pcm); 8718c2ecf20Sopenharmony_ci snd_pcm_lib_preallocate_free_for_all(pcm); 8728c2ecf20Sopenharmony_ci snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); 8738c2ecf20Sopenharmony_ci snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); 8748c2ecf20Sopenharmony_ci kfree(pcm); 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_cistatic int snd_pcm_dev_free(struct snd_device *device) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci struct snd_pcm *pcm = device->device_data; 8818c2ecf20Sopenharmony_ci return snd_pcm_free(pcm); 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ciint snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, 8858c2ecf20Sopenharmony_ci struct file *file, 8868c2ecf20Sopenharmony_ci struct snd_pcm_substream **rsubstream) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci struct snd_pcm_str * pstr; 8898c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 8908c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 8918c2ecf20Sopenharmony_ci struct snd_card *card; 8928c2ecf20Sopenharmony_ci int prefer_subdevice; 8938c2ecf20Sopenharmony_ci size_t size; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (snd_BUG_ON(!pcm || !rsubstream)) 8968c2ecf20Sopenharmony_ci return -ENXIO; 8978c2ecf20Sopenharmony_ci if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && 8988c2ecf20Sopenharmony_ci stream != SNDRV_PCM_STREAM_CAPTURE)) 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci *rsubstream = NULL; 9018c2ecf20Sopenharmony_ci pstr = &pcm->streams[stream]; 9028c2ecf20Sopenharmony_ci if (pstr->substream == NULL || pstr->substream_count == 0) 9038c2ecf20Sopenharmony_ci return -ENODEV; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci card = pcm->card; 9068c2ecf20Sopenharmony_ci prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { 9098c2ecf20Sopenharmony_ci int opposite = !stream; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci for (substream = pcm->streams[opposite].substream; substream; 9128c2ecf20Sopenharmony_ci substream = substream->next) { 9138c2ecf20Sopenharmony_ci if (SUBSTREAM_BUSY(substream)) 9148c2ecf20Sopenharmony_ci return -EAGAIN; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (file->f_flags & O_APPEND) { 9198c2ecf20Sopenharmony_ci if (prefer_subdevice < 0) { 9208c2ecf20Sopenharmony_ci if (pstr->substream_count > 1) 9218c2ecf20Sopenharmony_ci return -EINVAL; /* must be unique */ 9228c2ecf20Sopenharmony_ci substream = pstr->substream; 9238c2ecf20Sopenharmony_ci } else { 9248c2ecf20Sopenharmony_ci for (substream = pstr->substream; substream; 9258c2ecf20Sopenharmony_ci substream = substream->next) 9268c2ecf20Sopenharmony_ci if (substream->number == prefer_subdevice) 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci if (! substream) 9308c2ecf20Sopenharmony_ci return -ENODEV; 9318c2ecf20Sopenharmony_ci if (! SUBSTREAM_BUSY(substream)) 9328c2ecf20Sopenharmony_ci return -EBADFD; 9338c2ecf20Sopenharmony_ci substream->ref_count++; 9348c2ecf20Sopenharmony_ci *rsubstream = substream; 9358c2ecf20Sopenharmony_ci return 0; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci for (substream = pstr->substream; substream; substream = substream->next) { 9398c2ecf20Sopenharmony_ci if (!SUBSTREAM_BUSY(substream) && 9408c2ecf20Sopenharmony_ci (prefer_subdevice == -1 || 9418c2ecf20Sopenharmony_ci substream->number == prefer_subdevice)) 9428c2ecf20Sopenharmony_ci break; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci if (substream == NULL) 9458c2ecf20Sopenharmony_ci return -EAGAIN; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); 9488c2ecf20Sopenharmony_ci if (runtime == NULL) 9498c2ecf20Sopenharmony_ci return -ENOMEM; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); 9528c2ecf20Sopenharmony_ci runtime->status = alloc_pages_exact(size, GFP_KERNEL); 9538c2ecf20Sopenharmony_ci if (runtime->status == NULL) { 9548c2ecf20Sopenharmony_ci kfree(runtime); 9558c2ecf20Sopenharmony_ci return -ENOMEM; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci memset(runtime->status, 0, size); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); 9608c2ecf20Sopenharmony_ci runtime->control = alloc_pages_exact(size, GFP_KERNEL); 9618c2ecf20Sopenharmony_ci if (runtime->control == NULL) { 9628c2ecf20Sopenharmony_ci free_pages_exact(runtime->status, 9638c2ecf20Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 9648c2ecf20Sopenharmony_ci kfree(runtime); 9658c2ecf20Sopenharmony_ci return -ENOMEM; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci memset(runtime->control, 0, size); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci init_waitqueue_head(&runtime->sleep); 9708c2ecf20Sopenharmony_ci init_waitqueue_head(&runtime->tsleep); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci runtime->status->state = SNDRV_PCM_STATE_OPEN; 9738c2ecf20Sopenharmony_ci mutex_init(&runtime->buffer_mutex); 9748c2ecf20Sopenharmony_ci atomic_set(&runtime->buffer_accessing, 0); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci substream->runtime = runtime; 9778c2ecf20Sopenharmony_ci substream->private_data = pcm->private_data; 9788c2ecf20Sopenharmony_ci substream->ref_count = 1; 9798c2ecf20Sopenharmony_ci substream->f_flags = file->f_flags; 9808c2ecf20Sopenharmony_ci substream->pid = get_pid(task_pid(current)); 9818c2ecf20Sopenharmony_ci pstr->substream_opened++; 9828c2ecf20Sopenharmony_ci *rsubstream = substream; 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_civoid snd_pcm_detach_substream(struct snd_pcm_substream *substream) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (PCM_RUNTIME_CHECK(substream)) 9918c2ecf20Sopenharmony_ci return; 9928c2ecf20Sopenharmony_ci runtime = substream->runtime; 9938c2ecf20Sopenharmony_ci if (runtime->private_free != NULL) 9948c2ecf20Sopenharmony_ci runtime->private_free(runtime); 9958c2ecf20Sopenharmony_ci free_pages_exact(runtime->status, 9968c2ecf20Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); 9978c2ecf20Sopenharmony_ci free_pages_exact(runtime->control, 9988c2ecf20Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); 9998c2ecf20Sopenharmony_ci kfree(runtime->hw_constraints.rules); 10008c2ecf20Sopenharmony_ci /* Avoid concurrent access to runtime via PCM timer interface */ 10018c2ecf20Sopenharmony_ci if (substream->timer) { 10028c2ecf20Sopenharmony_ci spin_lock_irq(&substream->timer->lock); 10038c2ecf20Sopenharmony_ci substream->runtime = NULL; 10048c2ecf20Sopenharmony_ci spin_unlock_irq(&substream->timer->lock); 10058c2ecf20Sopenharmony_ci } else { 10068c2ecf20Sopenharmony_ci substream->runtime = NULL; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci mutex_destroy(&runtime->buffer_mutex); 10098c2ecf20Sopenharmony_ci kfree(runtime); 10108c2ecf20Sopenharmony_ci put_pid(substream->pid); 10118c2ecf20Sopenharmony_ci substream->pid = NULL; 10128c2ecf20Sopenharmony_ci substream->pstr->substream_opened--; 10138c2ecf20Sopenharmony_ci} 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_cistatic ssize_t show_pcm_class(struct device *dev, 10168c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev); 10198c2ecf20Sopenharmony_ci struct snd_pcm *pcm = pstr->pcm; 10208c2ecf20Sopenharmony_ci const char *str; 10218c2ecf20Sopenharmony_ci static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { 10228c2ecf20Sopenharmony_ci [SNDRV_PCM_CLASS_GENERIC] = "generic", 10238c2ecf20Sopenharmony_ci [SNDRV_PCM_CLASS_MULTI] = "multi", 10248c2ecf20Sopenharmony_ci [SNDRV_PCM_CLASS_MODEM] = "modem", 10258c2ecf20Sopenharmony_ci [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", 10268c2ecf20Sopenharmony_ci }; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (pcm->dev_class > SNDRV_PCM_CLASS_LAST) 10298c2ecf20Sopenharmony_ci str = "none"; 10308c2ecf20Sopenharmony_ci else 10318c2ecf20Sopenharmony_ci str = strs[pcm->dev_class]; 10328c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", str); 10338c2ecf20Sopenharmony_ci} 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_cistatic DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL); 10368c2ecf20Sopenharmony_cistatic struct attribute *pcm_dev_attrs[] = { 10378c2ecf20Sopenharmony_ci &dev_attr_pcm_class.attr, 10388c2ecf20Sopenharmony_ci NULL 10398c2ecf20Sopenharmony_ci}; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic const struct attribute_group pcm_dev_attr_group = { 10428c2ecf20Sopenharmony_ci .attrs = pcm_dev_attrs, 10438c2ecf20Sopenharmony_ci}; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_cistatic const struct attribute_group *pcm_dev_attr_groups[] = { 10468c2ecf20Sopenharmony_ci &pcm_dev_attr_group, 10478c2ecf20Sopenharmony_ci NULL 10488c2ecf20Sopenharmony_ci}; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic int snd_pcm_dev_register(struct snd_device *device) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci int cidx, err; 10538c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 10548c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (snd_BUG_ON(!device || !device->device_data)) 10578c2ecf20Sopenharmony_ci return -ENXIO; 10588c2ecf20Sopenharmony_ci pcm = device->device_data; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 10618c2ecf20Sopenharmony_ci err = snd_pcm_add(pcm); 10628c2ecf20Sopenharmony_ci if (err) 10638c2ecf20Sopenharmony_ci goto unlock; 10648c2ecf20Sopenharmony_ci for (cidx = 0; cidx < 2; cidx++) { 10658c2ecf20Sopenharmony_ci int devtype = -1; 10668c2ecf20Sopenharmony_ci if (pcm->streams[cidx].substream == NULL) 10678c2ecf20Sopenharmony_ci continue; 10688c2ecf20Sopenharmony_ci switch (cidx) { 10698c2ecf20Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 10708c2ecf20Sopenharmony_ci devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; 10718c2ecf20Sopenharmony_ci break; 10728c2ecf20Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 10738c2ecf20Sopenharmony_ci devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; 10748c2ecf20Sopenharmony_ci break; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci /* register pcm */ 10778c2ecf20Sopenharmony_ci err = snd_register_device(devtype, pcm->card, pcm->device, 10788c2ecf20Sopenharmony_ci &snd_pcm_f_ops[cidx], pcm, 10798c2ecf20Sopenharmony_ci &pcm->streams[cidx].dev); 10808c2ecf20Sopenharmony_ci if (err < 0) { 10818c2ecf20Sopenharmony_ci list_del_init(&pcm->list); 10828c2ecf20Sopenharmony_ci goto unlock; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 10868c2ecf20Sopenharmony_ci snd_pcm_timer_init(substream); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci pcm_call_notify(pcm, n_register); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci unlock: 10928c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 10938c2ecf20Sopenharmony_ci return err; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic int snd_pcm_dev_disconnect(struct snd_device *device) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct snd_pcm *pcm = device->device_data; 10998c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 11008c2ecf20Sopenharmony_ci int cidx; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 11038c2ecf20Sopenharmony_ci mutex_lock(&pcm->open_mutex); 11048c2ecf20Sopenharmony_ci wake_up(&pcm->open_wait); 11058c2ecf20Sopenharmony_ci list_del_init(&pcm->list); 11068c2ecf20Sopenharmony_ci for (cidx = 0; cidx < 2; cidx++) { 11078c2ecf20Sopenharmony_ci for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { 11088c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irq(substream); 11098c2ecf20Sopenharmony_ci if (substream->runtime) { 11108c2ecf20Sopenharmony_ci if (snd_pcm_running(substream)) 11118c2ecf20Sopenharmony_ci snd_pcm_stop(substream, 11128c2ecf20Sopenharmony_ci SNDRV_PCM_STATE_DISCONNECTED); 11138c2ecf20Sopenharmony_ci /* to be sure, set the state unconditionally */ 11148c2ecf20Sopenharmony_ci substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; 11158c2ecf20Sopenharmony_ci wake_up(&substream->runtime->sleep); 11168c2ecf20Sopenharmony_ci wake_up(&substream->runtime->tsleep); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irq(substream); 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci for (cidx = 0; cidx < 2; cidx++) 11238c2ecf20Sopenharmony_ci for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) 11248c2ecf20Sopenharmony_ci snd_pcm_sync_stop(substream, false); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci pcm_call_notify(pcm, n_disconnect); 11278c2ecf20Sopenharmony_ci for (cidx = 0; cidx < 2; cidx++) { 11288c2ecf20Sopenharmony_ci snd_unregister_device(&pcm->streams[cidx].dev); 11298c2ecf20Sopenharmony_ci free_chmap(&pcm->streams[cidx]); 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci mutex_unlock(&pcm->open_mutex); 11328c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 11338c2ecf20Sopenharmony_ci return 0; 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_PCM_OSS) 11378c2ecf20Sopenharmony_ci/** 11388c2ecf20Sopenharmony_ci * snd_pcm_notify - Add/remove the notify list 11398c2ecf20Sopenharmony_ci * @notify: PCM notify list 11408c2ecf20Sopenharmony_ci * @nfree: 0 = register, 1 = unregister 11418c2ecf20Sopenharmony_ci * 11428c2ecf20Sopenharmony_ci * This adds the given notifier to the global list so that the callback is 11438c2ecf20Sopenharmony_ci * called for each registered PCM devices. This exists only for PCM OSS 11448c2ecf20Sopenharmony_ci * emulation, so far. 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_ciint snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (snd_BUG_ON(!notify || 11518c2ecf20Sopenharmony_ci !notify->n_register || 11528c2ecf20Sopenharmony_ci !notify->n_unregister || 11538c2ecf20Sopenharmony_ci !notify->n_disconnect)) 11548c2ecf20Sopenharmony_ci return -EINVAL; 11558c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 11568c2ecf20Sopenharmony_ci if (nfree) { 11578c2ecf20Sopenharmony_ci list_del(¬ify->list); 11588c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) 11598c2ecf20Sopenharmony_ci notify->n_unregister(pcm); 11608c2ecf20Sopenharmony_ci } else { 11618c2ecf20Sopenharmony_ci list_add_tail(¬ify->list, &snd_pcm_notify_list); 11628c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) 11638c2ecf20Sopenharmony_ci notify->n_register(pcm); 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 11668c2ecf20Sopenharmony_ci return 0; 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_pcm_notify); 11698c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_PCM_OSS */ 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS 11728c2ecf20Sopenharmony_ci/* 11738c2ecf20Sopenharmony_ci * Info interface 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_cistatic void snd_pcm_proc_read(struct snd_info_entry *entry, 11778c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci mutex_lock(®ister_mutex); 11828c2ecf20Sopenharmony_ci list_for_each_entry(pcm, &snd_pcm_devices, list) { 11838c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02i-%02i: %s : %s", 11848c2ecf20Sopenharmony_ci pcm->card->number, pcm->device, pcm->id, pcm->name); 11858c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) 11868c2ecf20Sopenharmony_ci snd_iprintf(buffer, " : playback %i", 11878c2ecf20Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); 11888c2ecf20Sopenharmony_ci if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) 11898c2ecf20Sopenharmony_ci snd_iprintf(buffer, " : capture %i", 11908c2ecf20Sopenharmony_ci pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); 11918c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci mutex_unlock(®ister_mutex); 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic struct snd_info_entry *snd_pcm_proc_entry; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void snd_pcm_proc_init(void) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL); 12038c2ecf20Sopenharmony_ci if (entry) { 12048c2ecf20Sopenharmony_ci snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); 12058c2ecf20Sopenharmony_ci if (snd_info_register(entry) < 0) { 12068c2ecf20Sopenharmony_ci snd_info_free_entry(entry); 12078c2ecf20Sopenharmony_ci entry = NULL; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci snd_pcm_proc_entry = entry; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_cistatic void snd_pcm_proc_done(void) 12148c2ecf20Sopenharmony_ci{ 12158c2ecf20Sopenharmony_ci snd_info_free_entry(snd_pcm_proc_entry); 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */ 12198c2ecf20Sopenharmony_ci#define snd_pcm_proc_init() 12208c2ecf20Sopenharmony_ci#define snd_pcm_proc_done() 12218c2ecf20Sopenharmony_ci#endif /* CONFIG_SND_PROC_FS */ 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci/* 12258c2ecf20Sopenharmony_ci * ENTRY functions 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic int __init alsa_pcm_init(void) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci snd_ctl_register_ioctl(snd_pcm_control_ioctl); 12318c2ecf20Sopenharmony_ci snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); 12328c2ecf20Sopenharmony_ci snd_pcm_proc_init(); 12338c2ecf20Sopenharmony_ci return 0; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic void __exit alsa_pcm_exit(void) 12378c2ecf20Sopenharmony_ci{ 12388c2ecf20Sopenharmony_ci snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); 12398c2ecf20Sopenharmony_ci snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); 12408c2ecf20Sopenharmony_ci snd_pcm_proc_done(); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cimodule_init(alsa_pcm_init) 12448c2ecf20Sopenharmony_cimodule_exit(alsa_pcm_exit) 1245